Module Merb::Slices
In: merb-slices/lib/merb-slices/module.rb
merb-slices/lib/merb-slices/controller_mixin.rb
merb-slices/lib/merb-slices/module_mixin.rb

Methods

Classes and Modules

Module Merb::Slices::ControllerMixin
Module Merb::Slices::ModuleMixin
Module Merb::Slices::Support
Class Merb::Slices::Activate
Class Merb::Slices::DynamicLoader
Class Merb::Slices::Initialize
Class Merb::Slices::Loader

Constants

VERSION = "0.9.8"

External Aliases

activate_by_file -> register_and_load

Public Class methods

Retrieve a slice module by name

@param <to_s> The slice module to check for. @return <Module> The slice module itself.

[Source]

    # File merb-slices/lib/merb-slices/module.rb, line 12
12:       def [](module_name)
13:         Object.full_const_get(module_name.to_s) if exists?(module_name)
14:       end

Activate a Slice module at runtime

Looks for previously registered slices; then searches :search_path for matches.

@param slice_module<to_s> Usually a string of version of the slice module name.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 91
 91:       def activate(slice_module)  
 92:         unless slice_file = self.files[slice_module.to_s]
 93:           module_name_underscored = slice_module.to_s.snake_case.escape_regexp
 94:           module_name_dasherized  = module_name_underscored.tr('_', '-').escape_regexp
 95:           regexp = Regexp.new(/\/(#{module_name_underscored}|#{module_name_dasherized})\/lib\/(#{module_name_underscored}|#{module_name_dasherized})\.rb$/)
 96:           slice_file = slice_files_from_search_path.find { |path| path.match(regexp) } # from search path(s)
 97:         end
 98:         activate_by_file(slice_file) if slice_file
 99:       rescue => e
100:         Merb.logger.error!("Failed to activate slice #{slice_module} (#{e.message})")
101:       end

Register a Slice by its gem/lib init file path and activate it at runtime

Normally slices are loaded using BootLoaders on application startup. This method gives you the possibility to add slices at runtime, all without restarting your app. Together with deactivate it allows you to enable/disable slices at any time. The router is reloaded to incorporate any changes. Disabled slices will be skipped when routes are regenerated.

@param slice_file<String> The path of the gem ‘init file‘

@example Merb::Slices.activate_by_file(’/path/to/gems/slice-name/lib/slice-name.rb’)

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 115
115:       def activate_by_file(slice_file)
116:         Merb::Slices::Loader.load_classes(slice_file)
117:         slice = register(slice_file, false) # just to get module by slice_file
118:         slice.load_slice # load the slice
119:         Merb::Slices::Loader.reload_router!
120:         slice.init     if slice.respond_to?(:init)
121:         slice.activate if slice.respond_to?(:activate) && slice.routed?
122:         slice
123:       rescue
124:         Merb::Slices::Loader.reload_router!
125:       end

@return <Hash>

  The configuration loaded from Merb.root / "config/slices.yml" or, if
  the load fails, an empty hash.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 194
194:       def config
195:         @config ||= begin
196:           empty_hash = Hash.new { |h,k| h[k] = {} }
197:           if File.exists?(Merb.root / "config" / "slices.yml")
198:             require "yaml"
199:             YAML.load(File.read(Merb.root / "config" / "slices.yml")) || empty_hash
200:           else
201:             empty_hash
202:           end
203:         end
204:       end

Deactivate a Slice module at runtime

@param slice_module<to_s> The Slice module to unregister.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 131
131:       def deactivate(slice_module)
132:         if slice = self[slice_module]
133:           slice.deactivate if slice.respond_to?(:deactivate) && slice.routed?
134:           unregister(slice)
135:         end
136:       end

Deactivate a Slice module at runtime by specifying its slice file

@param slice_file<String> The Slice location of the slice init file to unregister.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 141
141:       def deactivate_by_file(slice_file)
142:         if slice = self.slices.find { |s| s.file == slice_file }
143:           deactivate(slice.name)
144:         end
145:       end

Iterate over all registered slices

By default iterates alphabetically over all registered modules. If Merb::Plugins.config[:merb_slices][:queue] is set, only the defined modules are loaded in the given order. This can be used to selectively load slices, and also maintain load-order for slices that depend on eachother.

@yield Iterate over known slices and pass in the slice module. @yieldparam module<Module> The Slice module.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 256
256:       def each_slice(&block)
257:         loadable_slices = Merb::Plugins.config[:merb_slices].key?(:queue) ? Merb::Plugins.config[:merb_slices][:queue] : slice_names
258:         loadable_slices.each do |module_name|
259:           if mod = self[module_name]
260:             block.call(mod)
261:           end
262:         end
263:       end

Check whether a Slice exists

@param <to_s> The slice module to check for.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 225
225:       def exists?(module_name)
226:         const_name = module_name.to_s.camel_case
227:         slice_names.include?(const_name) && Object.const_defined?(const_name)
228:       end

Helper method to transform a slice filename to a module Symbol

[Source]

    # File merb-slices/lib/merb-slices/module.rb, line 17
17:       def filename2module(slice_file)
18:         File.basename(slice_file, '.rb').gsub('-', '_').camel_case.to_sym
19:       end

A lookup for finding a Slice module‘s slice file path

@return <Hash> A Hash mapping module names to slice files. @note This is unaffected by deactivating a slice; used to reload slices by name.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 242
242:       def files
243:         @files ||= {}
244:       end

@return <Hash[Hash]>

  A Hash mapping between slice identifiers and non-prefixed named routes.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 187
187:       def named_routes
188:         @named_routes ||= {}
189:       end

A lookup for finding a Slice module‘s path

@return <Hash> A Hash mapping module names to root paths. @note Whenever a slice is deactivated, its path is removed from the lookup.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 234
234:       def paths
235:         @paths ||= {}
236:       end

Register a Slice by its gem/lib path for loading at startup

This is referenced from gems/<slice-gem-x.x.x>/lib/<slice-gem>.rb Which gets loaded for any gem. The name of the file is used to extract the Slice module name.

@param slice_file<String> The path of the gem ‘init file’ @param force<Boolean> Whether to overwrite currently registered slice or not.

@return <Module> The Slice module that has been setup.

@example Merb::Slices::register(FILE) @example Merb::Slices::register(’/path/to/my-slice/lib/my-slice.rb’)

[Source]

    # File merb-slices/lib/merb-slices/module.rb, line 34
34:       def register(slice_file, force = true)
35:         # do what filename2module does, but with intermediate variables
36:         identifier  = File.basename(slice_file, '.rb')
37:         underscored = identifier.gsub('-', '_')
38:         module_name = underscored.camel_case
39:         slice_path  = File.expand_path(File.dirname(slice_file) + '/..')
40:         # check if slice_path exists instead of just the module name - more flexible
41:         if !self.paths.include?(slice_path) || force
42:           Merb.logger.verbose!("Registered slice '#{module_name}' located at #{slice_path}") if force
43:           self.files[module_name] = slice_file
44:           self.paths[module_name] = slice_path
45:           slice_mod = setup_module(module_name)
46:           slice_mod.identifier = identifier
47:           slice_mod.identifier_sym = underscored.to_sym
48:           slice_mod.root = slice_path
49:           slice_mod.file = slice_file
50:           slice_mod.registered
51:           slice_mod
52:         else
53:           Merb.logger.info!("Already registered slice '#{module_name}' located at #{slice_path}")
54:           Object.full_const_get(module_name)
55:         end
56:       end

Look for any slices in Merb.root / ‘slices’ (the default) or if given, Merb::Plugins.config[:merb_slices][:search_path] (String/Array)

[Source]

    # File merb-slices/lib/merb-slices/module.rb, line 60
60:       def register_slices_from_search_path!
61:         slice_files_from_search_path.each do |slice_file|
62:           absolute_path = File.expand_path(slice_file)
63:           Merb.logger.info!("Found slice '#{File.basename(absolute_path, '.rb')}' in search path at #{absolute_path.relative_path_from(Merb.root)}")
64:           Merb::Slices::Loader.load_classes(absolute_path)
65:         end
66:       end

Reload a Slice at runtime

@param slice_module<to_s> The Slice module to reload.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 150
150:       def reload(slice_module)
151:         if slice = self[slice_module]
152:           deactivate slice.name
153:           activate_by_file slice.file
154:         end
155:       end

Reload a Slice at runtime by specifying its slice file

@param slice_file<String> The Slice location of the slice init file to reload.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 160
160:       def reload_by_file(slice_file)
161:         if slice = self.slices.find { |s| s.file == slice_file }
162:           reload(slice.name)
163:         end
164:       end

Slice file locations from all search paths; this default to host-app/slices.

Look for any slices in those default locations or if given, Merb::Plugins.config[:merb_slices][:search_path] (String/Array). Specify files, glob patterns or paths containing slices.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 270
270:       def slice_files_from_search_path
271:         search_paths = Array(Merb::Plugins.config[:merb_slices][:search_path] || [Merb.root / "slices"])
272:         search_paths.inject([]) do |files, path|
273:           # handle both Pathname and String
274:           path = path.to_s
275:           if File.file?(path) && File.extname(path) == ".rb"
276:             files << path
277:           elsif path.include?("*")
278:             files += glob_search_path(path)
279:           elsif File.directory?(path)
280:             files += glob_search_path(path / "**/lib/*.rb")
281:           end
282:           files
283:         end
284:       end

All registered Slice module names

@return <Array[String]> A sorted array of all slice module names.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 218
218:       def slice_names
219:         self.paths.keys.sort
220:       end

All registered Slice modules

@return <Array[Module]> A sorted array of all slice modules.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 209
209:       def slices
210:         slice_names.map do |name|
211:           Object.full_const_get(name) rescue nil
212:         end.compact
213:       end

Watch all specified search paths to dynamically load/unload slices at runtime

If a valid slice is found it‘s automatically registered and activated; once a slice is removed (or renamed to not match the convention), it will be unregistered and deactivated. Runs in a Thread.

@example Merb::BootLoader.after_app_loads { Merb::Slices.start_dynamic_loader! }

@param interval<Numeric>

  The interval in seconds of checking the search path(s) for changes.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 176
176:       def start_dynamic_loader!(interval = nil)
177:         DynamicLoader.start(interval)
178:       end

Stop watching search paths to dynamically load/unload slices at runtime

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 181
181:       def stop_dynamic_loader!
182:         DynamicLoader.stop
183:       end

Unregister a Slice at runtime

This clears the slice module from ObjectSpace and reloads the router. Since the router doesn‘t add routes for any disabled slices this will correctly reflect the app‘s routing state.

@param slice_module<to_s> The Slice module to unregister.

[Source]

    # File merb-slices/lib/merb-slices/module.rb, line 75
75:       def unregister(slice_module)
76:         if (slice = self[slice_module]) && self.paths.delete(module_name = slice.name)
77:           slice.loadable_files.each { |file| Merb::Slices::Loader.remove_classes_in_file file }
78:           Object.send(:remove_const, module_name)
79:           unless Object.const_defined?(module_name)
80:             Merb.logger.info!("Unregistered slice #{module_name}")
81:             Merb::Slices::Loader.reload_router!
82:           end
83:         end
84:       end

Private Class methods

Glob slice files

@param glob_pattern<String> A glob path with pattern @return <Array> Valid slice file paths.

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 304
304:       def glob_search_path(glob_pattern)
305:         # handle both Pathname and String
306:         glob_pattern = glob_pattern.to_s
307:         Dir[glob_pattern].inject([]) do |files, libfile|
308:           basename = File.basename(libfile, '.rb')
309:           files << libfile if File.basename(File.dirname(File.dirname(libfile))) == basename
310:           files
311:         end
312:       end

Prepare a module to be a proper Slice module

@param module_name<to_s> The name of the module to prepare

@return <Module> The module that has been setup

[Source]

     # File merb-slices/lib/merb-slices/module.rb, line 293
293:       def setup_module(module_name)
294:         Object.make_module(module_name)
295:         slice_mod = Object.full_const_get(module_name)
296:         slice_mod.extend(ModuleMixin)
297:         slice_mod
298:       end

[Validate]