merb-slices 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/Generators +4 -0
  2. data/LICENSE +20 -0
  3. data/README +102 -0
  4. data/Rakefile +65 -0
  5. data/TODO +0 -0
  6. data/lib/generators/base.rb +21 -0
  7. data/lib/generators/full.rb +21 -0
  8. data/lib/generators/templates/full/LICENSE +20 -0
  9. data/lib/generators/templates/full/README +170 -0
  10. data/lib/generators/templates/full/Rakefile +48 -0
  11. data/lib/generators/templates/full/TODO +15 -0
  12. data/lib/generators/templates/full/app/controllers/application.rb +5 -0
  13. data/lib/generators/templates/full/app/controllers/main.rb +7 -0
  14. data/lib/generators/templates/full/app/helpers/application_helper.rb +64 -0
  15. data/lib/generators/templates/full/app/views/layout/%symbol_name%.html.erb +16 -0
  16. data/lib/generators/templates/full/app/views/main/index.html.erb +1 -0
  17. data/lib/generators/templates/full/lib/%base_name%.rb +78 -0
  18. data/lib/generators/templates/full/lib/%base_name%/merbtasks.rb +166 -0
  19. data/lib/generators/templates/full/lib/%base_name%/slicetasks.rb +18 -0
  20. data/lib/generators/templates/full/public/javascripts/master.js +0 -0
  21. data/lib/generators/templates/full/public/stylesheets/master.css +2 -0
  22. data/lib/generators/templates/full/spec/%base_name%_spec.rb +130 -0
  23. data/lib/generators/templates/full/spec/controllers/main_spec.rb +61 -0
  24. data/lib/generators/templates/full/spec/spec_helper.rb +44 -0
  25. data/lib/generators/templates/full/stubs/app/controllers/application.rb +2 -0
  26. data/lib/generators/templates/full/stubs/app/controllers/main.rb +2 -0
  27. data/lib/generators/templates/thin/LICENSE +20 -0
  28. data/lib/generators/templates/thin/README +130 -0
  29. data/lib/generators/templates/thin/Rakefile +46 -0
  30. data/lib/generators/templates/thin/TODO +7 -0
  31. data/lib/generators/templates/thin/application.rb +36 -0
  32. data/lib/generators/templates/thin/lib/%base_name%.rb +93 -0
  33. data/lib/generators/templates/thin/lib/%base_name%/merbtasks.rb +106 -0
  34. data/lib/generators/templates/thin/lib/%base_name%/slicetasks.rb +18 -0
  35. data/lib/generators/templates/thin/public/javascripts/master.js +0 -0
  36. data/lib/generators/templates/thin/public/stylesheets/master.css +2 -0
  37. data/lib/generators/templates/thin/stubs/application.rb +9 -0
  38. data/lib/generators/templates/thin/views/layout/%symbol_name%.html.erb +16 -0
  39. data/lib/generators/templates/thin/views/main/index.html.erb +1 -0
  40. data/lib/generators/templates/very_thin/LICENSE +20 -0
  41. data/lib/generators/templates/very_thin/README +110 -0
  42. data/lib/generators/templates/very_thin/Rakefile +46 -0
  43. data/lib/generators/templates/very_thin/TODO +7 -0
  44. data/lib/generators/templates/very_thin/application.rb +36 -0
  45. data/lib/generators/templates/very_thin/lib/%base_name%.rb +89 -0
  46. data/lib/generators/templates/very_thin/lib/%base_name%/merbtasks.rb +106 -0
  47. data/lib/generators/templates/very_thin/lib/%base_name%/slicetasks.rb +18 -0
  48. data/lib/generators/thin.rb +21 -0
  49. data/lib/generators/very_thin.rb +21 -0
  50. data/lib/merb-slices.rb +126 -0
  51. data/lib/merb-slices/controller_mixin.rb +129 -0
  52. data/lib/merb-slices/merbtasks.rb +19 -0
  53. data/lib/merb-slices/module.rb +338 -0
  54. data/lib/merb-slices/module_mixin.rb +535 -0
  55. data/lib/merb-slices/router_ext.rb +75 -0
  56. data/spec/full_slice_generator_spec.rb +21 -0
  57. data/spec/merb-slice_spec.rb +7 -0
  58. data/spec/slice_generator_spec.rb +22 -0
  59. data/spec/spec_helper.rb +9 -0
  60. data/spec/thin_slice_generator_spec.rb +21 -0
  61. data/spec/very_thin_slice_generator_spec.rb +21 -0
  62. metadata +157 -0
@@ -0,0 +1,106 @@
1
+ namespace :slices do
2
+ namespace :<%= symbol_name %> do
3
+
4
+ desc "Install <%= module_name %>"
5
+ task :install => [:preflight, :setup_directories, :copy_assets, :migrate]
6
+
7
+ desc "Test for any dependencies"
8
+ task :preflight do # see slicetasks.rb
9
+ end
10
+
11
+ desc "Setup directories"
12
+ task :setup_directories do
13
+ puts "Creating directories for host application"
14
+ [:application, :view, :model, :controller, :helper, :mailer, :part, :public].each do |type|
15
+ if File.directory?(<%= module_name %>.dir_for(type))
16
+ if !File.directory?(dst_path = <%= module_name %>.app_dir_for(type))
17
+ relative_path = dst_path.relative_path_from(Merb.root)
18
+ puts "- creating directory :#{type} #{File.basename(Merb.root) / relative_path}"
19
+ mkdir_p(dst_path)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ desc "Copy stub files to host application"
26
+ task :stubs do
27
+ puts "Copying stubs for <%= module_name %> - resolves any collisions"
28
+ copied, preserved = <%= module_name %>.mirror_stubs!
29
+ puts "- no files to copy" if copied.empty? && preserved.empty?
30
+ copied.each { |f| puts "- copied #{f}" }
31
+ preserved.each { |f| puts "! preserved override as #{f}" }
32
+ end
33
+
34
+ desc "Copy stub files and views to host application"
35
+ task :patch => [ "stubs", "freeze:views" ]
36
+
37
+ desc "Copy public assets to host application"
38
+ task :copy_assets do
39
+ puts "Copying assets for <%= module_name %> - resolves any collisions"
40
+ copied, preserved = <%= module_name %>.mirror_public!
41
+ puts "- no files to copy" if copied.empty? && preserved.empty?
42
+ copied.each { |f| puts "- copied #{f}" }
43
+ preserved.each { |f| puts "! preserved override as #{f}" }
44
+ end
45
+
46
+ desc "Migrate the database"
47
+ task :migrate do # see slicetasks.rb
48
+ end
49
+
50
+ desc "Freeze <%= module_name %> into your app (only <%= base_name %>/app)"
51
+ task :freeze => [ "freeze:app" ]
52
+
53
+ namespace :freeze do
54
+
55
+ desc "Freezes <%= module_name %> by installing the gem into application/gems using merb-freezer"
56
+ task :gem do
57
+ begin
58
+ Object.const_get(:Freezer).freeze(ENV["GEM"] || "<%= base_name %>", ENV["UPDATE"], ENV["MODE"] || 'rubygems')
59
+ rescue NameError
60
+ puts "! dependency 'merb-freezer' missing"
61
+ end
62
+ end
63
+
64
+ desc "Freezes <%= module_name %> by copying all files from <%= base_name %>/app to your application"
65
+ task :app do
66
+ puts "Copying all <%= base_name %>/app files to your application - resolves any collisions"
67
+ copied, preserved = <%= module_name %>.mirror_app!
68
+ puts "- no files to copy" if copied.empty? && preserved.empty?
69
+ copied.each { |f| puts "- copied #{f}" }
70
+ preserved.each { |f| puts "! preserved override as #{f}" }
71
+ end
72
+
73
+ desc "Freeze all views into your application for easy modification"
74
+ task :views do
75
+ puts "Copying all view templates to your application - resolves any collisions"
76
+ copied, preserved = <%= module_name %>.mirror_files_for :view
77
+ puts "- no files to copy" if copied.empty? && preserved.empty?
78
+ copied.each { |f| puts "- copied #{f}" }
79
+ preserved.each { |f| puts "! preserved override as #{f}" }
80
+ end
81
+
82
+ desc "Freeze all models into your application for easy modification"
83
+ task :models do
84
+ puts "Copying all models to your application - resolves any collisions"
85
+ copied, preserved = <%= module_name %>.mirror_files_for :model
86
+ puts "- no files to copy" if copied.empty? && preserved.empty?
87
+ copied.each { |f| puts "- copied #{f}" }
88
+ preserved.each { |f| puts "! preserved override as #{f}" }
89
+ end
90
+
91
+ desc "Freezes <%= module_name %> as a gem and copies over <%= base_name %>/app"
92
+ task :app_with_gem => [:gem, :app]
93
+
94
+ desc "Freezes <%= module_name %> by unpacking all files into your application"
95
+ task :unpack do
96
+ puts "Unpacking <%= module_name %> files to your application - resolves any collisions"
97
+ copied, preserved = <%= module_name %>.unpack_slice!
98
+ puts "- no files to copy" if copied.empty? && preserved.empty?
99
+ copied.each { |f| puts "- copied #{f}" }
100
+ preserved.each { |f| puts "! preserved override as #{f}" }
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,18 @@
1
+ namespace :slices do
2
+ namespace :<%= symbol_name %> do
3
+
4
+ # add your own <%= base_name %> tasks here
5
+
6
+ # implement this to test for structural/code dependencies
7
+ # like certain directories or availability of other files
8
+ desc "Test for any dependencies"
9
+ task :preflight do
10
+ end
11
+
12
+ # implement this to perform any database related setup steps
13
+ desc "Migrate the database"
14
+ task :migrate do
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module Merb::Generators
2
+
3
+ class ThinSliceGenerator < NamedGenerator
4
+
5
+ def self.source_root
6
+ File.join(File.dirname(__FILE__), 'templates', 'thin')
7
+ end
8
+
9
+ glob!
10
+
11
+ first_argument :name, :required => true
12
+
13
+ def destination_root
14
+ File.join(@destination_root, base_name)
15
+ end
16
+
17
+ end
18
+
19
+ add_private :thin_slice, ThinSliceGenerator
20
+
21
+ end
@@ -0,0 +1,21 @@
1
+ module Merb::Generators
2
+
3
+ class VeryThinSliceGenerator < NamedGenerator
4
+
5
+ def self.source_root
6
+ File.join(File.dirname(__FILE__), 'templates', 'very_thin')
7
+ end
8
+
9
+ glob!
10
+
11
+ first_argument :name, :required => true
12
+
13
+ def destination_root
14
+ File.join(@destination_root, base_name)
15
+ end
16
+
17
+ end
18
+
19
+ add_private :very_thin_slice, VeryThinSliceGenerator
20
+
21
+ end
@@ -0,0 +1,126 @@
1
+ require 'merb-slices/module'
2
+
3
+ if defined?(Merb::Plugins)
4
+
5
+ Merb::Plugins.add_rakefiles "merb-slices/merbtasks"
6
+
7
+ Merb::Plugins.config[:merb_slices] ||= {}
8
+
9
+ require "merb-slices/module_mixin"
10
+ require "merb-slices/controller_mixin"
11
+ require "merb-slices/router_ext"
12
+
13
+ # Enable slice behaviour for any class inheriting from AbstractController.
14
+ # To use this call controller_for_slice in your controller class.
15
+ Merb::AbstractController.send(:include, Merb::Slices::ControllerMixin)
16
+
17
+ # Load Slice classes before the app's classes are loaded.
18
+ #
19
+ # This allows the application to override/merge any slice-level classes.
20
+ class Merb::Slices::Loader < Merb::BootLoader
21
+
22
+ before LoadClasses
23
+
24
+ class << self
25
+
26
+ # Gather all slices from search path and gems and load their classes.
27
+ def run
28
+ Merb::Slices.register_slices_from_search_path! if auto_register?
29
+ Merb::Slices.each_slice { |slice| slice.load_slice }
30
+ end
31
+
32
+ # Load a single file and its requirements.
33
+ #
34
+ # @param file<String> The file to load.
35
+ def load_file(file)
36
+ Merb::BootLoader::LoadClasses.load_file file
37
+ end
38
+
39
+ # Remove a single file and the classes loaded by it from ObjectSpace.
40
+ #
41
+ # @param file<String> The file to load.
42
+ def remove_file(file)
43
+ Merb::BootLoader::LoadClasses.remove_file file
44
+ end
45
+
46
+ # Load classes from given paths - using path/glob pattern.
47
+ #
48
+ # @param *paths <Array> Array of paths to load classes from - may contain glob pattern
49
+ def load_classes(*paths)
50
+ Merb::BootLoader::LoadClasses.load_classes paths
51
+ end
52
+
53
+ # Reload the router - takes all_slices into account to load slices at runtime.
54
+ def reload_router!
55
+ Merb::BootLoader::LoadClasses.reload_router!
56
+ end
57
+
58
+ # Slice-level paths for all loaded slices.
59
+ #
60
+ # @return <Array[String]> Any slice-level paths that have been loaded.
61
+ def slice_paths
62
+ paths = []
63
+ Merb::Slices.each_slice { |slice| paths += slice.collected_slice_paths }
64
+ paths
65
+ end
66
+
67
+ # App-level paths for all loaded slices.
68
+ #
69
+ # @return <Array[String]> Any app-level paths that have been loaded.
70
+ def app_paths
71
+ paths = []
72
+ Merb::Slices.each_slice { |slice| paths += slice.collected_app_paths }
73
+ paths
74
+ end
75
+
76
+ private
77
+
78
+ # Whether slices from search paths should be registered automatically.
79
+ # Defaults to true if not explicitly set.
80
+ def auto_register?
81
+ Merb::Plugins.config[:merb_slices][:auto_register] || !Merb::Plugins.config[:merb_slices].key?(:auto_register)
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
88
+ # Call initialization method for each registered Slice.
89
+ #
90
+ # This is done just before the app's after_load_callbacks are run.
91
+ # The application has been practically loaded completely, letting
92
+ # the callbacks work with what has been loaded.
93
+ class Merb::Slices::Initialize < Merb::BootLoader
94
+
95
+ before AfterAppLoads
96
+
97
+ def self.run
98
+ Merb::Slices.each_slice do |slice|
99
+ Merb.logger.info!("Initializing slice '#{slice}' ...")
100
+ slice.init if slice.respond_to?(:init)
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ # Call activation method for each registered Slice.
107
+ #
108
+ # This is done right after the app's after_load_callbacks are run.
109
+ # Any settings can be taken into account in the activation step.
110
+ #
111
+ # @note Activation will only take place if the slice has been routed;
112
+ # this means you need have at least one slice route setup.
113
+ class Merb::Slices::Activate < Merb::BootLoader
114
+
115
+ after AfterAppLoads
116
+
117
+ def self.run
118
+ Merb::Slices.each_slice do |slice|
119
+ Merb.logger.info!("Activating slice '#{slice}' ...")
120
+ slice.activate if slice.respond_to?(:activate) && slice.routed?
121
+ end
122
+ end
123
+
124
+ end
125
+
126
+ end
@@ -0,0 +1,129 @@
1
+ module Merb
2
+ module Slices
3
+
4
+ module ControllerMixin
5
+
6
+ def self.included(klass)
7
+ klass.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ # Setup a controller to reference a slice and its template roots
13
+ #
14
+ # This method is available to any class inheriting from Merb::AbstractController;
15
+ # it enabled correct location of templates, as well as access to the slice module.
16
+ #
17
+ # @param slice_module<#to_s> The slice module to use; defaults to current module.
18
+ # @param options<Hash>
19
+ # Optional parameters to set which component path is used (defaults to :view) and
20
+ # the :path option lets you specify a subdirectory of that component path.
21
+ # When :layout is set, then this is used instead of the config's :layout setting.
22
+ #
23
+ # @example controller_for_slice # uses current module
24
+ # @example controller_for_slice SliceMod # defaults to :view templates and no subdirectory
25
+ # @example controller_for_slice :templates_for => :mailer, :path => 'views' # for Merb::Mailer
26
+ # @example controller_for_slice SliceMod, :templates_for => :mailer, :path => 'views' # for Merb::Mailer
27
+ def controller_for_slice(slice_module = nil, options = {})
28
+ options, slice_module = slice_module.merge(options), nil if slice_module.is_a?(Hash)
29
+ slice_module ||= self.name.split('::').first
30
+ options[:templates_for] = :view unless options.key?(:templates_for)
31
+ if slice_mod = Merb::Slices[slice_module.to_s]
32
+ # Include the instance methods
33
+ unless self.kind_of?(Merb::Slices::ControllerMixin::MixinMethods)
34
+ self.send(:extend, Merb::Slices::ControllerMixin::MixinMethods)
35
+ end
36
+ # Reference this controller's slice module
37
+ self.class_inheritable_accessor :slice
38
+ self.slice = slice_mod
39
+ # Setup template roots
40
+ if options[:templates_for]
41
+ self._template_root = join_template_path(slice_mod.dir_for(options[:templates_for]), options[:path])
42
+ self._template_roots = []
43
+ # app-level app/views directory for shared and fallback views, layouts and partials
44
+ self._template_roots << [join_template_path(Merb.dir_for(options[:templates_for]), options[:path]), :_template_location] if Merb.dir_for(options[:templates_for])
45
+ # slice-level app/views for the standard supplied views
46
+ self._template_roots << [self._template_root, :_slice_template_location]
47
+ # app-level slices/<slice>/app/views for specific overrides
48
+ self._template_roots << [join_template_path(slice_mod.app_dir_for(options[:templates_for]), options[:path]), :_slice_template_location]
49
+ # additional template roots for specific overrides (optional)
50
+ self._template_roots += Array(options[:template_roots]) if options[:template_roots]
51
+ end
52
+ # Set the layout for this slice controller
53
+ layout_for_slice(options[:layout])
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def join_template_path(*segments)
60
+ File.join(*segments.compact)
61
+ end
62
+
63
+ end
64
+
65
+ module MixinMethods
66
+
67
+ def self.extended(klass)
68
+ klass.send(:include, InstanceMethods)
69
+ klass.hide_action :slice
70
+ end
71
+
72
+ # Use the slice's layout - defaults to underscored identifier.
73
+ #
74
+ # This is set for generated stubs that support layouts.
75
+ #
76
+ # @param layout<#to_s> The layout name to use.
77
+ def layout_for_slice(layout = nil)
78
+ layout(layout || self.slice.config[:layout]) if layout || self.slice.config.key?(:layout)
79
+ end
80
+
81
+ module InstanceMethods
82
+
83
+ # Reference this controller's slice module directly.
84
+ #
85
+ # @return <Module> A slice module.
86
+ def slice; self.class.slice; end
87
+
88
+ # Generate a url - takes the slice's :path_prefix into account.
89
+ def slice_url(name, rparams={})
90
+ self.slice.url(name, rparams, {
91
+ :controller => controller_name,
92
+ :action => action_name,
93
+ :format => params[:format]
94
+ })
95
+ end
96
+
97
+ private
98
+
99
+ # This is called after the controller is instantiated to figure out where to
100
+ # for templates under the _template_root. This helps the controllers
101
+ # of a slice to locate templates without looking in a subdirectory with
102
+ # the name of the module. Instead it will just be app/views/controller/*
103
+ #
104
+ # @param context<#to_str> The controller context (the action or template name).
105
+ # @param type<#to_str> The content type. Defaults to nil.
106
+ # @param controller<#to_str> The name of the controller. Defaults to controller_name.
107
+ #
108
+ # @return <String>
109
+ # Indicating where to look for the template for the current controller,
110
+ # context, and content-type.
111
+ def _slice_template_location(context, type = nil, controller = controller_name)
112
+ if controller && controller.include?('/')
113
+ # skip first segment if given (which is the module name)
114
+ segments = controller.split('/')
115
+ "#{segments[1,segments.length-1].join('/')}/#{context}.#{type}"
116
+ else
117
+ # default template location logic
118
+ _template_location(context, type, controller)
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,19 @@
1
+ desc "Show information on application slices"
2
+ task :slices => [ "slices:list" ]
3
+
4
+ namespace :slices do
5
+
6
+ desc "Get a suitable slices environment up"
7
+ task :env do
8
+ Merb::Slices.register_slices_from_search_path!
9
+ end
10
+
11
+ desc "List known slices for the current application"
12
+ task :list => [:env] do
13
+ puts "Application slices:\n"
14
+ Merb::Slices.each_slice do |slice|
15
+ puts "#{slice.name} (v. #{slice.version}) - #{slice.description}\n"
16
+ end
17
+ end
18
+
19
+ end