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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/cullender/application.js +13 -0
- data/app/assets/javascripts/cullender/rules.js +46 -0
- data/app/assets/stylesheets/cullender/application.css +13 -0
- data/app/assets/stylesheets/cullender/rules.css.scss +15 -0
- data/app/controllers/cullender/rules_controller.rb +68 -0
- data/app/controllers/cullender_controller.rb +105 -0
- data/app/helpers/cullender/rules_helper.rb +136 -0
- data/app/models/cullender/rule.rb +204 -0
- data/app/views/cullender/rules/_form.html.erb +20 -0
- data/app/views/cullender/rules/_trigger_fields.html.erb +19 -0
- data/app/views/cullender/rules/create.js.erb +3 -0
- data/app/views/cullender/rules/index.html.erb +25 -0
- data/app/views/cullender/rules/new.html.erb +1 -0
- data/app/views/cullender/rules/show.html.erb +3 -0
- data/config/locales/en.yml +12 -0
- data/config/routes.rb +4 -0
- data/lib/cullender.rb +96 -0
- data/lib/cullender/controllers/filter_logic.rb +89 -0
- data/lib/cullender/controllers/scoped_views.rb +17 -0
- data/lib/cullender/core_ext/hash.rb +1 -0
- data/lib/cullender/core_ext/hash/deep_delete.rb +13 -0
- data/lib/cullender/engine.rb +15 -0
- data/lib/cullender/engine/routes.rb +306 -0
- data/lib/cullender/mapping.rb +139 -0
- data/lib/cullender/version.rb +3 -0
- data/lib/generators/cullender/cullender_generator.rb +66 -0
- data/lib/generators/cullender/install_generator.rb +18 -0
- data/lib/generators/cullender/orm_helpers.rb +32 -0
- data/lib/generators/cullender/templates/cullender.rb +3 -0
- data/lib/generators/cullender/templates/cullender_migration.rb +13 -0
- data/lib/tasks/cullender_tasks.rake +4 -0
- data/spec/controllers/cullender/rules_controller_spec.rb +180 -0
- data/spec/cullender/controllers/filter_logic_spec.rb +233 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +17 -0
- data/spec/dummy/app/assets/stylesheets/application.css +14 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/events_controller.rb +60 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/event.rb +16 -0
- data/spec/dummy/app/views/cullender/rules/_form.html.erb +33 -0
- data/spec/dummy/app/views/cullender/rules/index.html.erb +24 -0
- data/spec/dummy/app/views/cullender/rules/new.html.erb +1 -0
- data/spec/dummy/app/views/cullender/rules/show.html.erb +3 -0
- data/spec/dummy/app/views/events/_form.html.erb +21 -0
- data/spec/dummy/app/views/events/edit.html.erb +6 -0
- data/spec/dummy/app/views/events/index.html.erb +27 -0
- data/spec/dummy/app/views/events/new.html.erb +5 -0
- data/spec/dummy/app/views/events/show.html.erb +10 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +9 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +27 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cullender.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/cullender.en.yml +12 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/123344556676_create_cullender_tables.rb +12 -0
- data/spec/dummy/db/schema.rb +24 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +9987 -0
- data/spec/dummy/log/test.log +19786 -0
- data/spec/dummy/public/404.html +27 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/268a828ee4997cf5c19106e5f00fcfb8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/56b2eb9b46b9aca7e86b5132ddf0d9de +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/750887406b3e8dacb2b03f986330932d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/750b42e431d194c41f5b1cde4a257e47 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/921642fe740290e9e5e88a706e5a562c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a967ed3fc5f8406f3ff72180775f1b40 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ae74bf4fc3fd20e7f7b98860b8ef0f74 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c7dbd1f5acc2d5bc078363f4f3c70c54 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d995daf8d6f36c27b6e9d1b4672eaf1e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/helpers/cullender/rules_helper_spec.rb +360 -0
- data/spec/models/cullender/rule_spec.rb +425 -0
- data/spec/requests/rules_spec.rb +19 -0
- data/spec/spec_helper.rb +60 -0
- 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,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
|