cullender 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/javascripts/cullender/application.js +13 -0
  6. data/app/assets/javascripts/cullender/rules.js +46 -0
  7. data/app/assets/stylesheets/cullender/application.css +13 -0
  8. data/app/assets/stylesheets/cullender/rules.css.scss +15 -0
  9. data/app/controllers/cullender/rules_controller.rb +68 -0
  10. data/app/controllers/cullender_controller.rb +105 -0
  11. data/app/helpers/cullender/rules_helper.rb +136 -0
  12. data/app/models/cullender/rule.rb +204 -0
  13. data/app/views/cullender/rules/_form.html.erb +20 -0
  14. data/app/views/cullender/rules/_trigger_fields.html.erb +19 -0
  15. data/app/views/cullender/rules/create.js.erb +3 -0
  16. data/app/views/cullender/rules/index.html.erb +25 -0
  17. data/app/views/cullender/rules/new.html.erb +1 -0
  18. data/app/views/cullender/rules/show.html.erb +3 -0
  19. data/config/locales/en.yml +12 -0
  20. data/config/routes.rb +4 -0
  21. data/lib/cullender.rb +96 -0
  22. data/lib/cullender/controllers/filter_logic.rb +89 -0
  23. data/lib/cullender/controllers/scoped_views.rb +17 -0
  24. data/lib/cullender/core_ext/hash.rb +1 -0
  25. data/lib/cullender/core_ext/hash/deep_delete.rb +13 -0
  26. data/lib/cullender/engine.rb +15 -0
  27. data/lib/cullender/engine/routes.rb +306 -0
  28. data/lib/cullender/mapping.rb +139 -0
  29. data/lib/cullender/version.rb +3 -0
  30. data/lib/generators/cullender/cullender_generator.rb +66 -0
  31. data/lib/generators/cullender/install_generator.rb +18 -0
  32. data/lib/generators/cullender/orm_helpers.rb +32 -0
  33. data/lib/generators/cullender/templates/cullender.rb +3 -0
  34. data/lib/generators/cullender/templates/cullender_migration.rb +13 -0
  35. data/lib/tasks/cullender_tasks.rake +4 -0
  36. data/spec/controllers/cullender/rules_controller_spec.rb +180 -0
  37. data/spec/cullender/controllers/filter_logic_spec.rb +233 -0
  38. data/spec/dummy/README.rdoc +28 -0
  39. data/spec/dummy/Rakefile +6 -0
  40. data/spec/dummy/app/assets/javascripts/application.js +17 -0
  41. data/spec/dummy/app/assets/stylesheets/application.css +14 -0
  42. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  43. data/spec/dummy/app/controllers/events_controller.rb +60 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/models/event.rb +16 -0
  46. data/spec/dummy/app/views/cullender/rules/_form.html.erb +33 -0
  47. data/spec/dummy/app/views/cullender/rules/index.html.erb +24 -0
  48. data/spec/dummy/app/views/cullender/rules/new.html.erb +1 -0
  49. data/spec/dummy/app/views/cullender/rules/show.html.erb +3 -0
  50. data/spec/dummy/app/views/events/_form.html.erb +21 -0
  51. data/spec/dummy/app/views/events/edit.html.erb +6 -0
  52. data/spec/dummy/app/views/events/index.html.erb +27 -0
  53. data/spec/dummy/app/views/events/new.html.erb +5 -0
  54. data/spec/dummy/app/views/events/show.html.erb +10 -0
  55. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  56. data/spec/dummy/bin/bundle +3 -0
  57. data/spec/dummy/bin/rails +4 -0
  58. data/spec/dummy/bin/rake +4 -0
  59. data/spec/dummy/config.ru +4 -0
  60. data/spec/dummy/config/application.rb +23 -0
  61. data/spec/dummy/config/boot.rb +9 -0
  62. data/spec/dummy/config/database.yml +25 -0
  63. data/spec/dummy/config/environment.rb +5 -0
  64. data/spec/dummy/config/environments/development.rb +27 -0
  65. data/spec/dummy/config/environments/production.rb +80 -0
  66. data/spec/dummy/config/environments/test.rb +36 -0
  67. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  68. data/spec/dummy/config/initializers/cullender.rb +3 -0
  69. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  70. data/spec/dummy/config/initializers/inflections.rb +16 -0
  71. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  72. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  73. data/spec/dummy/config/initializers/session_store.rb +3 -0
  74. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  75. data/spec/dummy/config/locales/cullender.en.yml +12 -0
  76. data/spec/dummy/config/locales/en.yml +23 -0
  77. data/spec/dummy/config/routes.rb +5 -0
  78. data/spec/dummy/db/development.sqlite3 +0 -0
  79. data/spec/dummy/db/migrate/123344556676_create_cullender_tables.rb +12 -0
  80. data/spec/dummy/db/schema.rb +24 -0
  81. data/spec/dummy/db/test.sqlite3 +0 -0
  82. data/spec/dummy/log/development.log +9987 -0
  83. data/spec/dummy/log/test.log +19786 -0
  84. data/spec/dummy/public/404.html +27 -0
  85. data/spec/dummy/public/422.html +26 -0
  86. data/spec/dummy/public/500.html +26 -0
  87. data/spec/dummy/public/favicon.ico +0 -0
  88. data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  89. data/spec/dummy/tmp/cache/assets/development/sprockets/268a828ee4997cf5c19106e5f00fcfb8 +0 -0
  90. data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  91. data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  92. data/spec/dummy/tmp/cache/assets/development/sprockets/56b2eb9b46b9aca7e86b5132ddf0d9de +0 -0
  93. data/spec/dummy/tmp/cache/assets/development/sprockets/750887406b3e8dacb2b03f986330932d +0 -0
  94. data/spec/dummy/tmp/cache/assets/development/sprockets/750b42e431d194c41f5b1cde4a257e47 +0 -0
  95. data/spec/dummy/tmp/cache/assets/development/sprockets/921642fe740290e9e5e88a706e5a562c +0 -0
  96. data/spec/dummy/tmp/cache/assets/development/sprockets/a967ed3fc5f8406f3ff72180775f1b40 +0 -0
  97. data/spec/dummy/tmp/cache/assets/development/sprockets/ae74bf4fc3fd20e7f7b98860b8ef0f74 +0 -0
  98. data/spec/dummy/tmp/cache/assets/development/sprockets/c7dbd1f5acc2d5bc078363f4f3c70c54 +0 -0
  99. data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  100. data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  101. data/spec/dummy/tmp/cache/assets/development/sprockets/d995daf8d6f36c27b6e9d1b4672eaf1e +0 -0
  102. data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  103. data/spec/helpers/cullender/rules_helper_spec.rb +360 -0
  104. data/spec/models/cullender/rule_spec.rb +425 -0
  105. data/spec/requests/rules_spec.rb +19 -0
  106. data/spec/spec_helper.rb +60 -0
  107. metadata +319 -0
@@ -0,0 +1,17 @@
1
+ module Cullender
2
+ module Controllers
3
+ module ScopedViews
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def scoped_views?
8
+ defined?(@scoped_views) ? @scoped_views : Cullender.scoped_views
9
+ end
10
+
11
+ def scoped_views=(value)
12
+ @scoped_views = value
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1 @@
1
+ require 'cullender/core_ext/hash/deep_delete'
@@ -0,0 +1,13 @@
1
+ class Hash
2
+ def deep_delete(hash)
3
+ hash.each do |key, value|
4
+ if self.has_key?(key)
5
+ if value.is_a?( Hash ) && self[key].is_a?(Hash)
6
+ self[key].deep_delete(value)
7
+ else
8
+ self.delete(key)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ require 'cullender/engine/routes'
2
+
3
+ module Cullender
4
+ class Engine < ::Rails::Engine
5
+ # isolate_namespace Cullender
6
+ # config.cullender = Cullender
7
+
8
+ # Force routes to be loaded if we are doing any eager load.
9
+ config.before_eager_load { |app| app.reload_routes! }
10
+
11
+ # initializer "devise.url_helpers" do
12
+ # Cullender.include_helpers(Devise::Controllers)
13
+ # end
14
+ end
15
+ end
@@ -0,0 +1,306 @@
1
+ require "active_support/core_ext/object/try"
2
+ require "active_support/core_ext/hash/slice"
3
+
4
+ module ActionDispatch::Routing
5
+ class RouteSet #:nodoc:
6
+ # Ensure Cullender modules are included only after loading routes, because we
7
+ # need cullender_for mappings already declared to create filters and helpers.
8
+ def finalize_with_cullender!
9
+ result = finalize_without_cullender!
10
+
11
+ @cullender_finalized ||= begin
12
+ if Cullender.router_name.nil? && defined?(@cullender_finalized) && self != Rails.application.try(:routes)
13
+ warn "[Cullender] We have detected that you are using cullender_for inside engine routes. " \
14
+ "In this case, you probably want to set Cullender.router_name = MOUNT_POINT, where " \
15
+ "MOUNT_POINT is a symbol representing where this engine will be mounted at. For " \
16
+ "now Cullender will default the mount point to :main_app. You can explicitly set it" \
17
+ " to :main_app as well in case you want to keep the current behavior."
18
+ end
19
+
20
+ Cullender.regenerate_helpers!
21
+ true
22
+ end
23
+
24
+ result
25
+ end
26
+ alias_method_chain :finalize!, :cullender
27
+ end
28
+
29
+ class Mapper
30
+ # Includes cullender_for method for routes. This method is responsible to
31
+ # generate all needed routes for cullender, based on what modules you have
32
+ # defined in your model.
33
+ #
34
+ # ==== Examples
35
+ #
36
+ # Let's say you have an User model configured to use authenticatable,
37
+ # confirmable and recoverable modules. After creating this inside your routes:
38
+ #
39
+ # cullender_for :users
40
+ #
41
+ # This method is going to look inside your User model and create the
42
+ # needed routes:
43
+ #
44
+ # # Session routes for Authenticatable (default)
45
+ # new_user_session GET /users/sign_in {:controller=>"cullender/sessions", :action=>"new"}
46
+ # user_session POST /users/sign_in {:controller=>"cullender/sessions", :action=>"create"}
47
+ # destroy_user_session DELETE /users/sign_out {:controller=>"cullender/sessions", :action=>"destroy"}
48
+ #
49
+ # # Password routes for Recoverable, if User model has :recoverable configured
50
+ # new_user_password GET /users/password/new(.:format) {:controller=>"cullender/passwords", :action=>"new"}
51
+ # edit_user_password GET /users/password/edit(.:format) {:controller=>"cullender/passwords", :action=>"edit"}
52
+ # user_password PUT /users/password(.:format) {:controller=>"cullender/passwords", :action=>"update"}
53
+ # POST /users/password(.:format) {:controller=>"cullender/passwords", :action=>"create"}
54
+ #
55
+ # # Confirmation routes for Confirmable, if User model has :confirmable configured
56
+ # new_user_confirmation GET /users/confirmation/new(.:format) {:controller=>"cullender/confirmations", :action=>"new"}
57
+ # user_confirmation GET /users/confirmation(.:format) {:controller=>"cullender/confirmations", :action=>"show"}
58
+ # POST /users/confirmation(.:format) {:controller=>"cullender/confirmations", :action=>"create"}
59
+ #
60
+ # ==== Options
61
+ #
62
+ # You can configure your routes with some options:
63
+ #
64
+ # * :class_name => setup a different class to be looked up by cullender, if it cannot be
65
+ # properly found by the route name.
66
+ #
67
+ # cullender_for :users, :class_name => 'Account'
68
+ #
69
+ # * :path => allows you to setup path name that will be used, as rails routes does.
70
+ # The following route configuration would setup your route as /accounts instead of /users:
71
+ #
72
+ # cullender_for :users, :path => 'accounts'
73
+ #
74
+ # * :singular => setup the singular name for the given resource. This is used as the instance variable
75
+ # name in controller, as the name in routes and the scope given to warden.
76
+ #
77
+ # cullender_for :users, :singular => :user
78
+ #
79
+ # * :path_names => configure different path names to overwrite defaults :sign_in, :sign_out, :sign_up,
80
+ # :password, :confirmation, :unlock.
81
+ #
82
+ # cullender_for :users, :path_names => { :sign_in => 'login', :sign_out => 'logout', :password => 'secret', :confirmation => 'verification' }
83
+ #
84
+ # * :controllers => the controller which should be used. All routes by default points to Cullender controllers.
85
+ # However, if you want them to point to custom controller, you should do:
86
+ #
87
+ # cullender_for :users, :controllers => { :sessions => "users/sessions" }
88
+ #
89
+ # * :failure_app => a rack app which is invoked whenever there is a failure. Strings representing a given
90
+ # are also allowed as parameter.
91
+ #
92
+ # * :sign_out_via => the HTTP method(s) accepted for the :sign_out action (default: :get),
93
+ # if you wish to restrict this to accept only :post or :delete requests you should do:
94
+ #
95
+ # cullender_for :users, :sign_out_via => [ :post, :delete ]
96
+ #
97
+ # You need to make sure that your sign_out controls trigger a request with a matching HTTP method.
98
+ #
99
+ # * :module => the namespace to find controllers (default: "cullender", thus
100
+ # accessing cullender/sessions, cullender/registrations, and so on). If you want
101
+ # to namespace all at once, use module:
102
+ #
103
+ # cullender_for :users, :module => "users"
104
+ #
105
+ # Notice that whenever you use namespace in the router DSL, it automatically sets the module.
106
+ # So the following setup:
107
+ #
108
+ # namespace :publisher do
109
+ # cullender_for :account
110
+ # end
111
+ #
112
+ # Will use publisher/sessions controller instead of cullender/sessions controller. You can revert
113
+ # this by providing the :module option to cullender_for.
114
+ #
115
+ # Also pay attention that when you use a namespace it will affect all the helpers and methods for controllers
116
+ # and views. For example, using the above setup you'll end with following methods:
117
+ # current_publisher_account, authenticate_publisher_account!, publisher_account_signed_in, etc.
118
+ #
119
+ # * :skip => tell which controller you want to skip routes from being created:
120
+ #
121
+ # cullender_for :users, :skip => :sessions
122
+ #
123
+ # * :only => the opposite of :skip, tell which controllers only to generate routes to:
124
+ #
125
+ # cullender_for :users, :only => :sessions
126
+ #
127
+ # * :skip_helpers => skip generating Cullender url helpers like new_session_path(@user).
128
+ # This is useful to avoid conflicts with previous routes and is false by default.
129
+ # It accepts true as option, meaning it will skip all the helpers for the controllers
130
+ # given in :skip but it also accepts specific helpers to be skipped:
131
+ #
132
+ # cullender_for :users, :skip => [:registrations, :confirmations], :skip_helpers => true
133
+ # cullender_for :users, :skip_helpers => [:registrations, :confirmations]
134
+ #
135
+ # * :format => include "(.:format)" in the generated routes? true by default, set to false to disable:
136
+ #
137
+ # cullender_for :users, :format => false
138
+ #
139
+ # * :constraints => works the same as Rails' constraints
140
+ #
141
+ # * :defaults => works the same as Rails' defaults
142
+ #
143
+ # ==== Scoping
144
+ #
145
+ # Following Rails 3 routes DSL, you can nest cullender_for calls inside a scope:
146
+ #
147
+ # scope "/my" do
148
+ # cullender_for :users
149
+ # end
150
+ #
151
+ # However, since Cullender uses the request path to retrieve the current user,
152
+ # this has one caveat: If you are using a dynamic segment, like so ...
153
+ #
154
+ # scope ":locale" do
155
+ # cullender_for :users
156
+ # end
157
+ #
158
+ # you are required to configure default_url_options in your
159
+ # ApplicationController class, so Cullender can pick it:
160
+ #
161
+ # class ApplicationController < ActionController::Base
162
+ # def self.default_url_options
163
+ # { :locale => I18n.locale }
164
+ # end
165
+ # end
166
+ #
167
+ # ==== Adding custom actions to override controllers
168
+ #
169
+ # You can pass a block to cullender_for that will add any routes defined in the block to Cullender's
170
+ # list of known actions. This is important if you add a custom action to a controller that
171
+ # overrides an out of the box Cullender controller.
172
+ # For example:
173
+ #
174
+ # class RegistrationsController < Cullender::RegistrationsController
175
+ # def update
176
+ # # do something different here
177
+ # end
178
+ #
179
+ # def deactivate
180
+ # # not a standard action
181
+ # # deactivate code here
182
+ # end
183
+ # end
184
+ #
185
+ # In order to get Cullender to recognize the deactivate action, your cullender_scope entry should look like this:
186
+ #
187
+ # cullender_scope :owner do
188
+ # post "deactivate", :to => "registrations#deactivate", :as => "deactivate_registration"
189
+ # end
190
+ #
191
+ def cullender_for(*resources)
192
+
193
+ @cullender_finalized = false
194
+ options = resources.extract_options!
195
+
196
+ options[:as] ||= @scope[:as] if @scope[:as].present?
197
+ options[:module] ||= @scope[:module] if @scope[:module].present?
198
+ options[:path_prefix] ||= @scope[:path] if @scope[:path].present?
199
+ options[:path_names] = (@scope[:path_names] || {}).merge(options[:path_names] || {})
200
+ options[:constraints] = (@scope[:constraints] || {}).merge(options[:constraints] || {})
201
+ options[:defaults] = (@scope[:defaults] || {}).merge(options[:defaults] || {})
202
+ options[:options] = @scope[:options] || {}
203
+ options[:options][:format] = false if options[:format] == false
204
+
205
+ resources.map!(&:to_sym)
206
+
207
+ resources.each do |resource|
208
+ mapping = Cullender.add_mapping(resource, options)
209
+
210
+ # begin
211
+ # raise_no_cullender_method_error!(mapping.class_name) unless mapping.to.respond_to?(:cullender)
212
+ # rescue NameError => e
213
+ # raise unless mapping.class_name == resource.to_s.classify
214
+ # warn "[WARNING] You provided cullender_for #{resource.inspect} but there is " <<
215
+ # "no model #{mapping.class_name} defined in your application"
216
+ # next
217
+ # rescue NoMethodError => e
218
+ # raise unless e.message.include?("undefined method `cullender'")
219
+ # raise_no_cullender_method_error!(mapping.class_name)
220
+ # end
221
+
222
+
223
+ routes = mapping.used_routes
224
+
225
+ cullender_scope mapping.name do
226
+ if block_given?
227
+ ActiveSupport::Deprecation.warn "Passing a block to cullender_for is deprecated. " \
228
+ "Please remove the block from cullender_for (only the block, the call to " \
229
+ "cullender_for must still exist) and call cullender_scope :#{mapping.name} do ... end " \
230
+ "with the block instead", caller
231
+ yield
232
+ end
233
+ # debugger
234
+ with_cullender_exclusive_scope mapping.fullpath, mapping.name, options do
235
+ routes.each do |mod|
236
+ send("cullender_#{mod}", mapping, mapping.controllers)
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ # Sets the cullender scope to be used in the controller. If you have custom routes,
244
+ # you are required to call this method (also aliased as :as) in order to specify
245
+ # to which controller it is targetted.
246
+ #
247
+ # as :user do
248
+ # get "sign_in", :to => "cullender/sessions#new"
249
+ # end
250
+ #
251
+ # Notice you cannot have two scopes mapping to the same URL. And remember, if
252
+ # you try to access a cullender controller without specifying a scope, it will
253
+ # raise ActionNotFound error.
254
+ #
255
+ # Also be aware of that 'cullender_scope' and 'as' use the singular form of the
256
+ # noun where other cullender route commands expect the plural form. This would be a
257
+ # good and working example.
258
+ #
259
+ # cullender_scope :user do
260
+ # match "/some/route" => "some_cullender_controller"
261
+ # end
262
+ # cullender_for :users
263
+ #
264
+ # Notice and be aware of the differences above between :user and :users
265
+ def cullender_scope(scope)
266
+ constraint = lambda do |request|
267
+ request.env["cullender.mapping"] = Cullender.mappings[scope]
268
+ true
269
+ end
270
+
271
+ constraints(constraint) do
272
+ yield
273
+ end
274
+ end
275
+ alias :as :cullender_scope
276
+
277
+ protected
278
+
279
+ def cullender_rule(mapping, controllers) #:nodoc:
280
+ # debugger
281
+ resources :rules, :except => [:edit],
282
+ :path => mapping.path_names[:rule], :controller => controllers[:rules]
283
+ end
284
+
285
+ CULLENDER_SCOPE_KEYS = [:as, :path, :module, :constraints, :defaults, :options]
286
+
287
+ def with_cullender_exclusive_scope(new_path, new_as, options) #:nodoc:
288
+ old = {}
289
+ CULLENDER_SCOPE_KEYS.each { |k| old[k] = @scope[k] }
290
+
291
+ new = { :as => new_as, :path => new_path, :module => nil }
292
+ new.merge!(options.slice(:constraints, :defaults, :options))
293
+
294
+ @scope.merge!(new)
295
+ yield
296
+ ensure
297
+ @scope.merge!(old)
298
+ end
299
+
300
+ def raise_no_cullender_method_error!(klass) #:nodoc:
301
+ raise "#{klass} does not respond to 'cullender' method. This usually means you haven't " \
302
+ "loaded your ORM file or it's being loaded too late. To fix it, be sure to require 'cullender/orm/YOUR_ORM' " \
303
+ "inside 'config/initializers/cullender.rb' or before your application definition in 'config/application.rb'"
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,139 @@
1
+ module Cullender
2
+ # Responsible for handling cullender mappings and routes configuration. Each
3
+ # resource configured by cullender_for in routes is actually creating a mapping
4
+ # object. You can refer to cullender_for in routes for usage options.
5
+ #
6
+ # The required value in cullender_for is actually not used internally, but it's
7
+ # inflected to find all other values.
8
+ #
9
+ # map.cullender_for :events
10
+ # mapping = Cullender.mappings[:event]
11
+ #
12
+ # mapping.name #=> :event
13
+ # # is the scope used in controllers and warden, given in the route as :singular.
14
+ #
15
+ # mapping.as #=> "events"
16
+ # # how the mapping should be search in the path, given in the route as :as.
17
+ #
18
+ # mapping.to #=> Event
19
+ # # is the class to be loaded from routes, given in the route as :class_name.
20
+ #
21
+ # mapping.modules #=> [:authenticatable]
22
+ # # is the modules included in the class
23
+ #
24
+ class Mapping #:nodoc:
25
+ attr_reader :singular, :scoped_path, :path, :controllers, :path_names,
26
+ :class_name, :sign_out_via, :format, :used_routes, :used_helpers, :failure_app
27
+
28
+ alias :name :singular
29
+
30
+ # Receives an object and find a scope for it. If a scope cannot be found,
31
+ # raises an error. If a symbol is given, it's considered to be the scope.
32
+ def self.find_scope!(duck)
33
+ case duck
34
+ when String, Symbol
35
+ return duck
36
+ when Class
37
+ Cullender.mappings.each_value { |m| return m.name if duck <= m.to }
38
+ else
39
+ Cullender.mappings.each_value { |m| return m.name if duck.is_a?(m.to) }
40
+ end
41
+
42
+ raise "Could not find a valid mapping for #{duck.inspect}"
43
+ end
44
+
45
+ def self.find_by_path!(path, path_type=:fullpath)
46
+ Cullender.mappings.each_value { |m| return m if path.include?(m.send(path_type)) }
47
+ raise "Could not find a valid mapping for path #{path.inspect}"
48
+ end
49
+
50
+ def initialize(name, options) #:nodoc:
51
+ @scoped_path = options[:as] ? "#{options[:as]}/#{name}" : name.to_s
52
+ @singular = (options[:singular] || @scoped_path.tr('/', '_').singularize).to_sym
53
+
54
+ @class_name = (options[:class_name] || name.to_s.classify).to_s
55
+ @klass = Cullender.ref(@class_name)
56
+
57
+ @path = (options[:path] || name).to_s
58
+ @path_prefix = options[:path_prefix]
59
+
60
+ @format = options[:format]
61
+
62
+ # default_failure_app(options)
63
+ default_controllers(options)
64
+ default_path_names(options)
65
+ default_used_route(options)
66
+ default_used_helpers(options)
67
+ end
68
+
69
+ # Gives the class the mapping points to.
70
+ def to
71
+ @klass.get
72
+ end
73
+
74
+ def routes
75
+ @routes ||= ["rule"]#.compact.uniq
76
+ end
77
+
78
+ def fullpath
79
+ "/#{@path_prefix}/#{@path}".squeeze("/")
80
+ end
81
+
82
+ private
83
+
84
+ def default_failure_app(options)
85
+ @failure_app = options[:failure_app] || Cullender::FailureApp
86
+ if @failure_app.is_a?(String)
87
+ ref = Cullender.ref(@failure_app)
88
+ @failure_app = lambda { |env| ref.get.call(env) }
89
+ end
90
+ end
91
+
92
+ def default_controllers(options)
93
+ mod = options[:module] || "cullender"
94
+ @controllers = Hash.new { |h,k| h[k] = "#{mod}/#{k}" }
95
+ @controllers.merge!(options[:controllers]) if options[:controllers]
96
+ @controllers.each { |k,v| @controllers[k] = v.to_s }
97
+ end
98
+
99
+ def default_path_names(options)
100
+ @path_names = Hash.new { |h,k| h[k] = k.to_s }
101
+ @path_names[:rules] = ""
102
+ @path_names.merge!(options[:path_names]) if options[:path_names]
103
+ end
104
+
105
+ def default_constraints(options)
106
+ @constraints = Hash.new
107
+ @constraints.merge!(options[:constraints]) if options[:constraints]
108
+ end
109
+
110
+ def default_defaults(options)
111
+ @defaults = Hash.new
112
+ @defaults.merge!(options[:defaults]) if options[:defaults]
113
+ end
114
+
115
+ def default_used_route(options)
116
+ singularizer = lambda { |s| s.to_s.singularize.to_sym }
117
+
118
+ if options.has_key?(:only)
119
+ @used_routes = self.routes & Array(options[:only]).map(&singularizer)
120
+ elsif options[:skip] == :all
121
+ @used_routes = []
122
+ else
123
+ @used_routes = self.routes - Array(options[:skip]).map(&singularizer)
124
+ end
125
+ end
126
+
127
+ def default_used_helpers(options)
128
+ singularizer = lambda { |s| s.to_s.singularize.to_sym }
129
+
130
+ if options[:skip_helpers] == true
131
+ @used_helpers = @used_routes
132
+ elsif skip = options[:skip_helpers]
133
+ @used_helpers = self.routes - Array(skip).map(&singularizer)
134
+ else
135
+ @used_helpers = self.routes
136
+ end
137
+ end
138
+ end
139
+ end