Class: Ekylibre::Plugin

Inherits:
Object
  • Object
show all
Defined in:
lib/ekylibre/plugin.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(plugfile_path) ⇒ Plugin

Links plugin into app


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/ekylibre/plugin.rb', line 142

def initialize(plugfile_path)
  @root = Pathname.new(plugfile_path).dirname
  @view_path = @root.join('app', 'views')
  @themes_assets = {}.with_indifferent_access
  @javascripts = []
  @initializers = {}

  lib = @root.join('lib')
  if File.directory?(lib)
    $LOAD_PATH.unshift lib
    ActiveSupport::Dependencies.autoload_paths += [lib]
  end

  instance_eval(File.read(plugfile_path), plugfile_path, 1)

  if @name
    @name = @name.to_sym
  else
    raise "Need a name for plugin #{plugfile_path}"
  end
  raise "Plugin name cannot be #{@name}." if [:ekylibre].include?(@name)

  # Adds lib
  @lib_dir = @root.join('lib')
  if @lib_dir.exist?
    $LOAD_PATH.unshift(@lib_dir.to_s)
    require @name.to_s unless @required.is_a?(FalseClass)
  end

  # Adds rights
  @right_file = root.join('config', 'rights.yml')
  Ekylibre::Access.load_file(@right_file) if @right_file.exist?

  # Adds aggregators
  @aggregators_path = @root.join('config', 'aggregators')
  if @aggregators_path.exist?
    Aggeratio.load_path += Dir.glob(@aggregators_path.join('**', '*.xml'))
  end

  # Adds initializers
  @initializers_path = @root.join('config', 'initializers')
  if @initializers_path.exist?
    Dir.glob(@initializers_path.join('**', '*.rb')).each do |file|
      path = Pathname.new(file)
      @initializers[path.relative_path_from(@initializers_path).to_s] = path
    end
  end

  # Adds locales (translation and reporting)
  @locales_path = @root.join('config', 'locales')
  if @locales_path.exist?
    Rails.application.config.i18n.load_path += Dir.glob(@locales_path.join('**', '*.{rb,yml}'))
    DocumentTemplate.load_path << @locales_path
  end

  # Adds view path
  if @view_path.directory?
    ActionController::Base.prepend_view_path(@view_path)
    ActionMailer::Base.prepend_view_path(@view_path)
  end

  # Adds the app/{controllers,helpers,models} directories of the plugin to the autoload path
  Dir.glob File.expand_path(@root.join('app', '{controllers,exchangers,helpers,models,jobs,mailers,inputs,guides}')) do |dir|
    ActiveSupport::Dependencies.autoload_paths += [dir]
    $LOAD_PATH.unshift(dir) if Dir.exist?(dir)
  end

  # Load all exchanger
  Dir.glob(@root.join('app', 'exchangers', '**', '*.rb')).each do |path|
    require path
  end

  # Adds the app/{controllers,helpers,models} concerns directories of the plugin to the autoload path
  Dir.glob File.expand_path(@root.join('app', '{controllers,models}/concerns')) do |dir|
    ActiveSupport::Dependencies.autoload_paths += [dir]
  end

  # Load helpers
  helpers_path = @root.join('app', 'helpers')
  Dir.glob File.expand_path(helpers_path.join('**', '*.rb')) do |dir|
    helper_module = Pathname.new(dir).relative_path_from(helpers_path).to_s.gsub(/\.rb$/, '').camelcase
    initializer "include helper #{helper_module}" do
      ::ActionView::Base.send(:include, helper_module.constantize)
    end
  end

  # Adds assets
  if assets_directory.exist?
    # Emulate "subdir by plugin" config
    # plugins/<plugin>/app/assets/*/ => tmp/plugins/assets/*/plugins/<plugin>/
    Dir.chdir(assets_directory) do
      Dir.glob('*') do |type|
        type_dir = self.class.type_assets_directory(type)
        plugin_type_dir = type_dir.join('plugins', @name.to_s) # mirrored_assets_directory(type)
        FileUtils.rm_rf plugin_type_dir
        FileUtils.mkdir_p(plugin_type_dir.dirname) unless plugin_type_dir.dirname.exist?
        FileUtils.ln_sf(assets_directory.join(type), plugin_type_dir)
        unless Rails.application.config.assets.paths.include?(type_dir.to_s)
          Rails.application.config.assets.paths << type_dir.to_s
        end
      end
    end
  end
end

Class Attribute Details

.registered_pluginsObject

Returns the value of attribute registered_plugins


21
22
23
# File 'lib/ekylibre/plugin.rb', line 21

def registered_plugins
  @registered_plugins
end

Instance Attribute Details

#initializersObject (readonly)

Returns the value of attribute initializers


138
139
140
# File 'lib/ekylibre/plugin.rb', line 138

def initializers
  @initializers
end

#javascriptsObject (readonly)

Returns the value of attribute javascripts


138
139
140
# File 'lib/ekylibre/plugin.rb', line 138

def javascripts
  @javascripts
end

#rootObject (readonly)

Returns the value of attribute root


138
139
140
# File 'lib/ekylibre/plugin.rb', line 138

def root
  @root
end

#routesObject (readonly)

Returns the value of attribute routes


138
139
140
# File 'lib/ekylibre/plugin.rb', line 138

def routes
  @routes
end

#themes_assetsObject (readonly)

Returns the value of attribute themes_assets


138
139
140
# File 'lib/ekylibre/plugin.rb', line 138

def themes_assets
  @themes_assets
end

Class Method Details

.after_login_path(resource) ⇒ Object

Call after_login_path on 'after login' plugin


133
134
135
# File 'lib/ekylibre/plugin.rb', line 133

def (resource)
  .name.to_s.camelize.constantize.(resource)
end

.after_login_pluginObject Also known as: redirect_after_login?


127
128
129
# File 'lib/ekylibre/plugin.rb', line 127

def 
  registered_plugins.values.detect(&:redirect_after_login?)
end

.eachObject


66
67
68
69
70
# File 'lib/ekylibre/plugin.rb', line 66

def each
  registered_plugins.each do |_key, plugin|
    yield plugin
  end
end

.field_accessor(*names) ⇒ Object


23
24
25
26
27
28
29
30
31
# File 'lib/ekylibre/plugin.rb', line 23

def field_accessor(*names)
  class_eval do
    names.each do |name|
      define_method(name) do |*args|
        args.empty? ? instance_variable_get("@#{name}") : instance_variable_set("@#{name}", *args)
      end
    end
  end
end

.generate_javascript_indexObject

Generate a javascript for all plugins which refes by theme to add addons import. This way permit to use natural sprocket cache approach without ERB filtering


74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/ekylibre/plugin.rb', line 74

def generate_javascript_index
  base_dir = Rails.root.join('tmp', 'plugins', 'javascript-addons')
  Rails.application.config.assets.paths << base_dir.to_s
  script = "# This files contains JS addons from plugins\n"
  each do |plugin|
    plugin.javascripts.each do |path|
      script << "#= require plugins/#{plugin.name}/#{path}\n"
    end
  end
  # <base_dir>/plugins.js.coffee
  file = base_dir.join('plugins.js.coffee')
  FileUtils.mkdir_p file.dirname
  File.write(file, script)
end

.generate_themes_stylesheetsObject

Generate a stylesheet by theme to add addons import. This way permit to use natural sprocket cache approach without ERB filtering


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/ekylibre/plugin.rb', line 91

def generate_themes_stylesheets
  base_dir = Rails.root.join('tmp', 'plugins', 'theme-addons')
  Rails.application.config.assets.paths << base_dir.to_s
  Ekylibre.themes.each do |theme|
    stylesheet = "// This files contains #{theme} theme addons from plugins\n\n"
    each do |plugin|
      plugin.themes_assets.each do |name, addons|
        next unless name == theme || name == '*' || (name.respond_to?(:match) && theme.match(name))
        stylesheet << "// #{plugin.name}\n"
        addons[:stylesheets].each do |file|
          stylesheet << "@import \"plugins/#{plugin.name}/#{file}\";"
        end if addons[:stylesheets]
      end
    end
    # <base_dir>/themes/<theme>/plugins.scss
    file = base_dir.join('themes', theme.to_s, 'plugins.scss')
    FileUtils.mkdir_p file.dirname
    File.write(file, stylesheet)
  end
end

.loadObject

Load all plugins


34
35
36
37
38
39
# File 'lib/ekylibre/plugin.rb', line 34

def load
  Dir.glob(File.join(directory, '*')).sort.each do |directory|
    next unless File.directory?(directory)
    load_plugin(directory)
  end
end

.load_plugin(path) ⇒ Object

Load a given plugin


42
43
44
45
46
47
48
49
50
51
# File 'lib/ekylibre/plugin.rb', line 42

def load_plugin(path)
  plugfile = File.join(path, 'Plugfile')
  if File.file?(plugfile)
    plugin = new(plugfile)
    registered_plugins[plugin.name] = plugin
    Rails.logger.info "Load #{plugin.name} plugin"
  else
    Rails.logger.warn "No Plugfile found in #{path}"
  end
end

.plugObject

Adds hooks for plugins Must be done after load


55
56
57
58
59
60
61
62
63
64
# File 'lib/ekylibre/plugin.rb', line 55

def plug
  # Adds helper 'plugins' for routes
  require 'ekylibre/plugin/routing'
  # Generate themes files
  generate_themes_stylesheets
  # Generate JS file
  generate_javascript_index
  # Load initializers
  run_initializers
end

.run_initializersObject

Run all initializers of plugins


113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/ekylibre/plugin.rb', line 113

def run_initializers
  each do |plugin|
    plugin.initializers.each do |name, block|
      if block.is_a?(Pathname)
        Rails.logger.info "Require initializer #{name}"
        require block
      else
        Rails.logger.info "Run initialize #{name}"
        block.call(Rails.application)
      end
    end
  end
end

.type_assets_directory(type) ⇒ Object

Returns a type (stylesheets, fonts…) directory for all plugins


16
17
18
# File 'lib/ekylibre/plugin.rb', line 16

def self.type_assets_directory(type)
  mirrored_assets_directory.join(type)
end

Instance Method Details

#add_routes(&block) ⇒ Object

Adds routes to access controllers


303
304
305
# File 'lib/ekylibre/plugin.rb', line 303

def add_routes(&block)
  @routes = block
end

#add_stylesheet(name) ⇒ Object

Adds a snippet in app (for side or help places)


293
294
295
# File 'lib/ekylibre/plugin.rb', line 293

def add_stylesheet(name)
  Rails.application.config.assets.precompile << "plugins/#{@name}/#{name}"
end

#add_theme_stylesheet(theme, file) ⇒ Object

Adds a stylesheet inside a given theme


298
299
300
# File 'lib/ekylibre/plugin.rb', line 298

def add_theme_stylesheet(theme, file)
  add_theme_asset(theme, file, :stylesheets)
end

#app(*requirements) ⇒ Object

Require app version, used for compatibility app '1.0.0' app '~> 1.0.0' app '> 1.0.0' app '>= 1.0.0', '< 2.0'


269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/ekylibre/plugin.rb', line 269

def app(*requirements)
  options = requirements.extract_options!
  requirements.each do |requirement|
    unless requirement =~ /\A((~>|>=|>|<|<=)\s+)?\d.\d(\.[a-z0-9]+)*\z/
      raise PluginRequirementError, "Invalid version requirement expression: #{requirement}"
    end
  end
  unless Gem::Requirement.new(*requirements) =~ Gem::Version.create(Ekylibre.version)
    raise PluginRequirementError, "Plugin (#{@name}) is incompatible with current version of app (#{Ekylibre.version} not #{requirements.inspect})"
  end
  true
end

#app_versionObject

TODO: externalize all following methods in a DSL module


254
255
256
# File 'lib/ekylibre/plugin.rb', line 254

def app_version
  Ekylibre.version
end

#extend_navigation(&block) ⇒ Object

Adds menus with DSL in Ekylibre backend nav


308
309
310
# File 'lib/ekylibre/plugin.rb', line 308

def extend_navigation(&block)
  Ekylibre::Navigation.exec_dsl(&block)
end

#initializer(name, &block) ⇒ Object


312
313
314
# File 'lib/ekylibre/plugin.rb', line 312

def initializer(name, &block)
  @initializers[name] = block
end

#redirect_after_loginObject

Will call method MyPlugin.after_login_path to get url to redirect to CAUTION: Only one plugin can use it. Only first plugin will be called if many are using this directive.


327
328
329
# File 'lib/ekylibre/plugin.rb', line 327

def 
  @redirect_after_login = true
end

#redirect_after_login?Boolean

Accessors


248
249
250
# File 'lib/ekylibre/plugin.rb', line 248

def redirect_after_login?
  @redirect_after_login
end

#register_manure_management_method(name, class_name) ⇒ Object


316
317
318
# File 'lib/ekylibre/plugin.rb', line 316

def register_manure_management_method(name, class_name)
  Calculus::ManureManagementPlan.register_method(name, class_name)
end

#require_javascript(path) ⇒ Object

Require a JS file from application.js


288
289
290
# File 'lib/ekylibre/plugin.rb', line 288

def require_javascript(path)
  @javascripts << path
end

#snippet(name, options = {}) ⇒ Object

Adds a snippet in app (for side or help places)


283
284
285
# File 'lib/ekylibre/plugin.rb', line 283

def snippet(name, options = {})
  Ekylibre::Snippet.add("#{@name}-#{name}", snippets_directory.join(name.to_s), options)
end

#subscribe(message, proc = nil, &block) ⇒ Object


320
321
322
# File 'lib/ekylibre/plugin.rb', line 320

def subscribe(message, proc = nil, &block)
  Ekylibre::Hook.subscribe(message, proc, &block)
end