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


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
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/ekylibre/plugin.rb', line 152

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,guides,helpers,inputs,integrations,jobs,mailers,models}')) 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|
        unless Rails.application.config.assets.paths.include?(assets_directory.join(type).to_s)
          Rails.application.config.assets.paths << assets_directory.join(type).to_s
        end
        unless %w[javascript stylesheets].include? type
          files_to_compile = Dir[type + '/**/*'].select { |f| File.file? f }.map do |f|
            Pathname.new(f).relative_path_from(Pathname.new(type)).to_s unless f == type
          end
          Rails.application.config.assets.precompile += files_to_compile
        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


148
149
150
# File 'lib/ekylibre/plugin.rb', line 148

def initializers
  @initializers
end

#javascriptsObject (readonly)

Returns the value of attribute javascripts


148
149
150
# File 'lib/ekylibre/plugin.rb', line 148

def javascripts
  @javascripts
end

#rootObject (readonly)

Returns the value of attribute root


148
149
150
# File 'lib/ekylibre/plugin.rb', line 148

def root
  @root
end

#routesObject (readonly)

Returns the value of attribute routes


148
149
150
# File 'lib/ekylibre/plugin.rb', line 148

def routes
  @routes
end

#themes_assetsObject (readonly)

Returns the value of attribute themes_assets


148
149
150
# File 'lib/ekylibre/plugin.rb', line 148

def themes_assets
  @themes_assets
end

Class Method Details

.after_login_path(resource) ⇒ Object

Call after_login_path on 'after login' plugin


143
144
145
# File 'lib/ekylibre/plugin.rb', line 143

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

.after_login_pluginObject Also known as: redirect_after_login?


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

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

.eachObject


75
76
77
78
79
# File 'lib/ekylibre/plugin.rb', line 75

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


83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ekylibre/plugin.rb', line 83

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 #{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


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/ekylibre/plugin.rb', line 100

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"
        next unless addons[:stylesheets]
        addons[:stylesheets].each do |file|
          stylesheet << "@import \"#{file}\";\n"
        end
      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_integrationsObject


53
54
55
56
57
58
59
60
# File 'lib/ekylibre/plugin.rb', line 53

def load_integrations
  Dir.glob(File.join(directory, '*')).sort.each do |directory|
    next unless File.directory?(directory)
    Dir.glob(File.join(directory, 'app', 'integrations', '**', '*.rb')).sort.each do |integration|
      require integration
    end
  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


64
65
66
67
68
69
70
71
72
73
# File 'lib/ekylibre/plugin.rb', line 64

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


123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/ekylibre/plugin.rb', line 123

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_cobble_addon(partial_path, options = {}) ⇒ Object


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

def add_cobble_addon(partial_path, options = {})
  Ekylibre::View::Addon.add(:cobbler, partial_path, options)
end

#add_routes(&block) ⇒ Object

Adds routes to access controllers


318
319
320
# File 'lib/ekylibre/plugin.rb', line 318

def add_routes(&block)
  @routes = block
end

#add_stylesheet(name) ⇒ Object

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


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

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


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

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

#add_toolbar_addon(partial_path, options = {}) ⇒ Object


322
323
324
325
326
# File 'lib/ekylibre/plugin.rb', line 322

def add_toolbar_addon(partial_path, options = {})
  # Config main toolbar by default because with current tools, no way to specify
  # which toolbar use when many in the same page.
  Ekylibre::View::Addon.add(:main_toolbar, partial_path, options)
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'


280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/ekylibre/plugin.rb', line 280

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

  version = Ekylibre.version
  version = version.split(' - ').first if version.include?('-')

  unless Gem::Requirement.new(*requirements) =~ Gem::Version.create(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


265
266
267
# File 'lib/ekylibre/plugin.rb', line 265

def app_version
  Ekylibre.version
end

#extend_navigation(&block) ⇒ Object

Adds menus with DSL in Ekylibre backend nav


333
334
335
# File 'lib/ekylibre/plugin.rb', line 333

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

#initializer(name, &block) ⇒ Object


337
338
339
# File 'lib/ekylibre/plugin.rb', line 337

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.


352
353
354
# File 'lib/ekylibre/plugin.rb', line 352

def 
  @redirect_after_login = true
end

#redirect_after_login?Boolean

Accessors

Returns:

  • (Boolean)

259
260
261
# File 'lib/ekylibre/plugin.rb', line 259

def redirect_after_login?
  @redirect_after_login
end

#register_manure_management_method(name, class_name) ⇒ Object


341
342
343
# File 'lib/ekylibre/plugin.rb', line 341

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


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

def require_javascript(path)
  @javascripts << path
end

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

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


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

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

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


345
346
347
# File 'lib/ekylibre/plugin.rb', line 345

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