cullender 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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