hobo 0.7.2 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/hobo +24 -7
- data/hobo_files/plugin/CHANGES.txt +501 -0
- data/hobo_files/plugin/generators/hobo/hobo_generator.rb +8 -6
- data/hobo_files/plugin/generators/hobo/templates/application.dryml +3 -0
- data/hobo_files/plugin/generators/hobo/templates/dryml-support.js +132 -0
- data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +4 -5
- data/hobo_files/plugin/generators/hobo_model_resource/hobo_model_resource_generator.rb +75 -0
- data/hobo_files/plugin/generators/hobo_model_resource/templates/controller.rb +7 -0
- data/hobo_files/plugin/generators/hobo_model_resource/templates/functional_test.rb +8 -0
- data/hobo_files/plugin/generators/hobo_model_resource/templates/helper.rb +2 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.js +30 -11
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css +149 -92
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +0 -48
- data/hobo_files/plugin/init.rb +45 -13
- data/hobo_files/plugin/lib/action_view_extensions/base.rb +4 -3
- data/hobo_files/plugin/lib/active_record/association_proxy.rb +18 -0
- data/hobo_files/plugin/lib/active_record/association_reflection.rb +5 -0
- data/hobo_files/plugin/lib/active_record/has_many_association.rb +7 -11
- data/hobo_files/plugin/lib/active_record/has_many_through_association.rb +8 -0
- data/hobo_files/plugin/lib/extensions/test_case.rb +1 -1
- data/hobo_files/plugin/lib/hobo.rb +38 -60
- data/hobo_files/plugin/lib/hobo/authentication_support.rb +1 -1
- data/hobo_files/plugin/lib/hobo/bundle.rb +131 -34
- data/hobo_files/plugin/lib/hobo/composite_model.rb +1 -1
- data/hobo_files/plugin/lib/hobo/controller.rb +7 -8
- data/hobo_files/plugin/lib/hobo/dev_controller.rb +21 -0
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +14 -8
- data/hobo_files/plugin/lib/hobo/dryml/dryml_support_controller.rb +13 -0
- data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +6 -7
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +207 -73
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +67 -55
- data/hobo_files/plugin/lib/hobo/dryml/template_handler.rb +53 -3
- data/hobo_files/plugin/lib/hobo/hobo_helper.rb +75 -107
- data/hobo_files/plugin/lib/hobo/model.rb +236 -429
- data/hobo_files/plugin/lib/hobo/model_controller.rb +277 -437
- data/hobo_files/plugin/lib/hobo/model_router.rb +62 -29
- data/hobo_files/plugin/lib/hobo/rapid_helper.rb +48 -9
- data/hobo_files/plugin/lib/hobo/scopes.rb +98 -0
- data/hobo_files/plugin/lib/hobo/scopes/association_proxy_extensions.rb +31 -0
- data/hobo_files/plugin/lib/hobo/scopes/automatic_scopes.rb +282 -0
- data/hobo_files/plugin/lib/hobo/scopes/defined_scope_proxy_extender.rb +88 -0
- data/hobo_files/plugin/lib/hobo/scopes/scope_reflection.rb +18 -0
- data/hobo_files/plugin/lib/hobo/scopes/scoped_proxy.rb +59 -0
- data/hobo_files/plugin/lib/hobo/undefined.rb +2 -0
- data/hobo_files/plugin/lib/hobo/user.rb +31 -14
- data/hobo_files/plugin/lib/hobo/user_controller.rb +41 -27
- data/hobo_files/plugin/taglibs/core.dryml +9 -11
- data/hobo_files/plugin/taglibs/rapid.dryml +51 -108
- data/hobo_files/plugin/taglibs/rapid_editing.dryml +25 -25
- data/hobo_files/plugin/taglibs/rapid_forms.dryml +111 -79
- data/hobo_files/plugin/taglibs/rapid_generics.dryml +74 -0
- data/hobo_files/plugin/taglibs/rapid_navigation.dryml +23 -21
- data/hobo_files/plugin/taglibs/rapid_pages.dryml +83 -169
- data/hobo_files/plugin/taglibs/rapid_plus.dryml +16 -2
- data/hobo_files/plugin/taglibs/rapid_support.dryml +3 -3
- data/hobo_files/plugin/taglibs/rapid_user_pages.dryml +104 -0
- metadata +60 -55
- data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +0 -276
- data/hobo_files/plugin/generators/hobo_migration/templates/migration.rb +0 -9
- data/hobo_files/plugin/lib/active_record/table_definition.rb +0 -34
- data/hobo_files/plugin/lib/extensions.rb +0 -375
- data/hobo_files/plugin/lib/hobo/email_address.rb +0 -12
- data/hobo_files/plugin/lib/hobo/enum_string.rb +0 -50
- data/hobo_files/plugin/lib/hobo/field_declaration_dsl.rb +0 -43
- data/hobo_files/plugin/lib/hobo/field_spec.rb +0 -68
- data/hobo_files/plugin/lib/hobo/html_string.rb +0 -7
- data/hobo_files/plugin/lib/hobo/lazy_hash.rb +0 -40
- data/hobo_files/plugin/lib/hobo/markdown_string.rb +0 -11
- data/hobo_files/plugin/lib/hobo/migrations.rb +0 -12
- data/hobo_files/plugin/lib/hobo/model_queries.rb +0 -117
- data/hobo_files/plugin/lib/hobo/password_string.rb +0 -7
- data/hobo_files/plugin/lib/hobo/percentage.rb +0 -14
- data/hobo_files/plugin/lib/hobo/predicate_dispatch.rb +0 -78
- data/hobo_files/plugin/lib/hobo/proc_binding.rb +0 -32
- data/hobo_files/plugin/lib/hobo/text.rb +0 -3
- data/hobo_files/plugin/lib/hobo/textile_string.rb +0 -25
- data/hobo_files/plugin/lib/hobo/where_fragment.rb +0 -28
@@ -8,6 +8,10 @@ module Hobo
|
|
8
8
|
|
9
9
|
PAGINATE_FORMATS = [ Mime::HTML, Mime::ALL ]
|
10
10
|
|
11
|
+
READ_ONLY_ACTIONS = [:index, :show]
|
12
|
+
WRITE_ONLY_ACTIONS = [:create, :update, :destroy]
|
13
|
+
FORM_ACTIONS = [:new, :edit]
|
14
|
+
|
11
15
|
class << self
|
12
16
|
|
13
17
|
def included(base)
|
@@ -18,6 +22,13 @@ module Hobo
|
|
18
22
|
|
19
23
|
helper_method :model, :current_user
|
20
24
|
before_filter :set_no_cache_headers
|
25
|
+
|
26
|
+
rescue_from ActiveRecord::RecordNotFound, :with => :not_found
|
27
|
+
|
28
|
+
rescue_from Hobo::Model::PermissionDeniedError, :with => :permission_denied
|
29
|
+
|
30
|
+
alias_method_chain :render, :hobo_model
|
31
|
+
|
21
32
|
end
|
22
33
|
base.template_path_cache = {}
|
23
34
|
|
@@ -25,45 +36,29 @@ module Hobo
|
|
25
36
|
end
|
26
37
|
|
27
38
|
end
|
39
|
+
|
28
40
|
|
29
41
|
module ClassMethods
|
30
42
|
|
31
43
|
attr_writer :model
|
32
44
|
|
33
|
-
attr_accessor :template_path_cache
|
34
|
-
|
35
|
-
def add_collection_actions(name)
|
36
|
-
defined_methods = instance_methods
|
37
|
-
|
38
|
-
show_collection_method = "show_#{name}".to_sym
|
39
|
-
if show_collection_method.not_in?(defined_methods) && include_action?(show_collection_method, true)
|
40
|
-
define_method show_collection_method do
|
41
|
-
hobo_show_collection(name)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
if Hobo.simple_has_many_association?(model.reflections[name])
|
46
|
-
new_method = "new_#{name.to_s.singularize}"
|
47
|
-
if new_method.not_in?(defined_methods) && include_action?(new_method, true)
|
48
|
-
define_method new_method do
|
49
|
-
hobo_new_in_collection(name)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
45
|
+
attr_accessor :template_path_cache
|
46
|
+
|
55
47
|
def web_methods
|
56
48
|
@web_methods ||= superclass.respond_to?(:web_methods) ? superclass.web_methods : []
|
57
49
|
end
|
58
50
|
|
51
|
+
|
59
52
|
def show_actions
|
60
53
|
@show_actions ||= superclass.respond_to?(:show_actions) ? superclass.show_actions : []
|
61
54
|
end
|
62
55
|
|
56
|
+
|
63
57
|
def index_actions
|
64
58
|
@index_actions ||= superclass.respond_to?(:index_actions) ? superclass.index_actions : []
|
65
59
|
end
|
66
60
|
|
61
|
+
|
67
62
|
def collections
|
68
63
|
# By default, all has_many associations are published
|
69
64
|
@collections ||= if superclass.respond_to?(:collections)
|
@@ -73,72 +68,103 @@ module Hobo
|
|
73
68
|
end
|
74
69
|
end
|
75
70
|
|
71
|
+
|
76
72
|
def model
|
77
73
|
@model ||= name.sub(/Controller$/, "").singularize.constantize
|
78
74
|
end
|
79
|
-
|
80
|
-
|
81
|
-
def autocomplete_for(
|
75
|
+
|
76
|
+
|
77
|
+
def autocomplete_for(field, options={})
|
82
78
|
options = options.reverse_merge(:limit => 15)
|
83
|
-
|
84
|
-
|
85
|
-
|
79
|
+
index_action "complete_#{field}" do
|
80
|
+
hobo_completions(model.limit(options[:limit]).send("#{field}_contains", params[:query]))
|
81
|
+
end
|
86
82
|
end
|
87
83
|
|
88
84
|
|
89
|
-
def autocompleter(name)
|
90
|
-
(@completers && @completers[name]) ||
|
91
|
-
(superclass.respond_to?(:autocompleter) && superclass.autocompleter(name))
|
92
|
-
end
|
93
|
-
|
94
|
-
|
95
85
|
def web_method(web_name, options={}, &block)
|
96
86
|
web_methods << web_name.to_sym
|
97
|
-
method = options
|
87
|
+
method = options.delete(:method) || web_name
|
98
88
|
got_block = block_given?
|
99
89
|
define_method web_name do
|
100
90
|
# Make sure we have a copy of the options - it is being mutated somewhere
|
101
91
|
opts = {}.merge(options)
|
102
92
|
@this = find_instance(opts) unless opts[:no_find]
|
103
|
-
|
104
|
-
|
105
|
-
if not_allowed?
|
106
|
-
permission_denied
|
107
|
-
elsif got_block
|
93
|
+
raise Hobo::Model::PermissionDeniedError unless Hobo.can_call?(current_user, @this, method)
|
94
|
+
if got_block
|
108
95
|
instance_eval(&block)
|
109
96
|
else
|
110
97
|
@this.send(method)
|
111
98
|
end
|
112
99
|
|
113
|
-
hobo_ajax_response unless performed?
|
100
|
+
hobo_ajax_response || render(:nothing => true) unless performed?
|
114
101
|
end
|
115
102
|
end
|
116
103
|
|
117
104
|
|
118
105
|
def auto_actions(*args)
|
119
|
-
|
106
|
+
options = args.extract_options!
|
107
|
+
|
120
108
|
@auto_actions = case args.first
|
121
|
-
when :all
|
122
|
-
when :
|
109
|
+
when :all then available_auto_actions
|
110
|
+
when :write_only then available_auto_write_actions + args.rest
|
111
|
+
when :read_only then available_auto_read_actions + args.rest
|
123
112
|
else args
|
124
113
|
end
|
125
114
|
|
115
|
+
except = Array(options[:except])
|
116
|
+
except_actions = except.map do |arg|
|
117
|
+
if arg == :collections
|
118
|
+
available_auto_collection_actions
|
119
|
+
else
|
120
|
+
arg
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
@auto_actions -= except_actions
|
125
|
+
|
126
|
+
def_auto_actions
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
def def_auto_actions
|
126
131
|
self.class_eval do
|
127
132
|
def index; hobo_index end if include_action?(:index)
|
128
133
|
def show; hobo_show end if include_action?(:show)
|
129
134
|
def new; hobo_new end if include_action?(:new)
|
130
135
|
def create; hobo_create end if include_action?(:create)
|
131
|
-
def edit;
|
136
|
+
def edit; hobo_show end if include_action?(:edit)
|
132
137
|
def update; hobo_update end if include_action?(:update)
|
133
138
|
def destroy; hobo_destroy end if include_action?(:destroy)
|
134
139
|
|
135
140
|
def completions; hobo_completions end if include_action?(:completions)
|
136
|
-
|
137
|
-
collections.each { |c| add_collection_actions(c.to_sym) }
|
138
141
|
end
|
142
|
+
|
143
|
+
collections.each { |c| def_collection_actions(c.to_sym) }
|
139
144
|
end
|
140
145
|
|
141
146
|
|
147
|
+
def def_collection_actions(name)
|
148
|
+
defined_methods = instance_methods
|
149
|
+
|
150
|
+
show_collection_method = name
|
151
|
+
if show_collection_method.not_in?(defined_methods) && include_action?(show_collection_method)
|
152
|
+
define_method show_collection_method do
|
153
|
+
hobo_show_collection(name)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
if Hobo.simple_has_many_association?(model.reflections[name])
|
158
|
+
new_method = "new_#{name.to_s.singularize}"
|
159
|
+
if new_method.not_in?(defined_methods) && include_action?(new_method)
|
160
|
+
define_method new_method do
|
161
|
+
hobo_new_in_collection(name)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
142
168
|
def show_action(*names, &block)
|
143
169
|
options = names.extract_options!
|
144
170
|
show_actions.concat(names)
|
@@ -158,35 +184,43 @@ module Hobo
|
|
158
184
|
if block
|
159
185
|
define_method(name, &block)
|
160
186
|
else
|
161
|
-
|
187
|
+
if scope = options.delete(:scope)
|
188
|
+
define_method(name) { hobo_index scope, options }
|
189
|
+
else
|
190
|
+
define_method(name) { hobo_index options }
|
191
|
+
end
|
162
192
|
end
|
163
193
|
end
|
164
194
|
end
|
165
195
|
|
166
196
|
def publish_collection(*names)
|
167
197
|
collections.concat(names)
|
168
|
-
names.each {|n|
|
198
|
+
names.each {|n| def_collection_actions(n)}
|
169
199
|
end
|
170
200
|
|
171
201
|
|
172
|
-
def
|
173
|
-
|
174
|
-
model.find_by_id_name(id, options)
|
175
|
-
else
|
176
|
-
model.find(id, options)
|
177
|
-
end
|
202
|
+
def include_action?(name)
|
203
|
+
name.to_sym.in?(@auto_actions)
|
178
204
|
end
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
205
|
+
|
206
|
+
|
207
|
+
def available_auto_actions
|
208
|
+
READ_ONLY_ACTIONS + WRITE_ONLY_ACTIONS + FORM_ACTIONS + available_auto_collection_actions
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
def available_auto_read_actions
|
213
|
+
READ_ONLY_ACTIONS + collections
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
def available_auto_write_actions
|
218
|
+
WRITE_ONLY_ACTIONS
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
def available_auto_collection_actions
|
223
|
+
collections + collections.map {|c| "new_#{c.to_s.singularize}".to_sym}
|
190
224
|
end
|
191
225
|
|
192
226
|
end
|
@@ -194,468 +228,295 @@ module Hobo
|
|
194
228
|
|
195
229
|
protected
|
196
230
|
|
197
|
-
def data_filter(name, &b)
|
198
|
-
@data_filters ||= HashWithIndifferentAccess.new
|
199
|
-
@data_filters[name] = b
|
200
|
-
end
|
201
231
|
|
202
|
-
def
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
return nil if search_string.blank?
|
210
|
-
|
211
|
-
model.conditions do
|
212
|
-
words = search_string.split
|
213
|
-
terms = words.map do |word|
|
214
|
-
cols = columns.map do |c|
|
215
|
-
if c.is_a?(Symbol)
|
216
|
-
send("#{c}_contains", word)
|
217
|
-
elsif c.is_a?(Hash)
|
218
|
-
c.map do |k, v|
|
219
|
-
related = send(k)
|
220
|
-
v = [v] unless v.is_a?(Array)
|
221
|
-
v.map { |related_col| related.send("#{related_col}_contains", word) }
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end.flatten
|
225
|
-
any?(*cols)
|
226
|
-
end
|
227
|
-
all?(*terms)
|
232
|
+
def filter_by(*args)
|
233
|
+
filters = args.extract_options!
|
234
|
+
finder = args.first || self.model
|
235
|
+
|
236
|
+
filters.each_pair do |scope, arg|
|
237
|
+
dont_filter = arg.is_a?(Array) ? arg.compact.empty? : arg.nil?
|
238
|
+
finder = finder.send(scope, arg) unless dont_filter
|
228
239
|
end
|
240
|
+
finder
|
229
241
|
end
|
230
242
|
|
231
|
-
attr_accessor :data_filters
|
232
243
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
def find_instance(*args)
|
237
|
-
options = args.extract_options!
|
238
|
-
res = self.class.find_instance(args.first || params[:id], options)
|
239
|
-
instance_variable_set("@#{model.name.underscore}", res)
|
240
|
-
res
|
241
|
-
end
|
242
|
-
|
243
|
-
|
244
|
-
def set_named_this!
|
245
|
-
instance_variable_set("@#{@this.class.name.underscore}", @this)
|
246
|
-
end
|
247
|
-
|
248
|
-
|
249
|
-
def response_block(&b)
|
250
|
-
if b
|
251
|
-
if b.arity == 1
|
252
|
-
respond_to {|wants| yield(wants) }
|
253
|
-
else
|
254
|
-
yield
|
255
|
-
end
|
256
|
-
performed?
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
|
261
|
-
def paginated_find(*args, &b)
|
262
|
-
options = args.extract_options!
|
263
|
-
filter_conditions = data_filter_conditions
|
264
|
-
conditions_proc = if b && filter_conditions
|
265
|
-
proc { block(b) & block(filter_conditions) }
|
266
|
-
else
|
267
|
-
b || filter_conditions
|
268
|
-
end
|
244
|
+
def sort_fields(*args)
|
245
|
+
finder = args.first.is_a?(Class) ? args.shift : model
|
269
246
|
|
270
|
-
|
271
|
-
if args.length == 1
|
272
|
-
scopes = args.first
|
273
|
-
@association = scopes.to_s.split(".").inject(model) { |m, name| m.send(name) }
|
274
|
-
elsif args.length == 2
|
275
|
-
owner, collection_name = args
|
276
|
-
@association = collection_name.to_s.split(".").inject(owner) { |m, name| m.send(name) }
|
277
|
-
end
|
278
|
-
@reflection = @association.proxy_reflection if @association._?.respond_to?(:proxy_reflection)
|
279
|
-
|
280
|
-
model_or_assoc, @member_class = if @association
|
281
|
-
[@association, @association.member_class]
|
282
|
-
else
|
283
|
-
[model, model]
|
284
|
-
end
|
285
|
-
|
286
|
-
page_size = options.delete(:page_size) || 20
|
287
|
-
page = options.delete(:page) || params[:page]
|
288
|
-
|
289
|
-
paginate = options.fetch(:paginate, request.format.in?(PAGINATE_FORMATS))
|
290
|
-
options.delete(:paginate)
|
291
|
-
if paginate
|
292
|
-
total_number = options.delete(:total_number) ||
|
293
|
-
begin
|
294
|
-
# If there is a conditions block, it may depend on the includes
|
295
|
-
count_options = conditions_proc ? { :include => options[:include] } : {}
|
296
|
-
model_or_assoc.count(count_options, &conditions_proc)
|
297
|
-
end
|
298
|
-
|
299
|
-
@pages = ::ActionController::Pagination::Paginator.new(self, total_number, page_size, page)
|
247
|
+
_, desc, field = *params[:sort]._?.match(/^(-)?([a-z_]+(?:\.[a-z_]+)?)$/)
|
300
248
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
unless options.has_key?(:order)
|
306
|
-
_, desc, field = *params[:sort]._?.match(/^(-)?([a-z_]+(?:\.[a-z_]+)?)$/)
|
307
|
-
if field
|
249
|
+
if field
|
250
|
+
fields = args.*.to_s
|
251
|
+
if field.in?(fields)
|
308
252
|
@sort_field = field
|
309
253
|
@sort_direction = desc ? "desc" : "asc"
|
310
|
-
|
254
|
+
|
311
255
|
table, column = if field =~ /^(.*)\.(.*)$/
|
312
256
|
[$1.camelize.constantize.table_name, $2]
|
313
257
|
else
|
314
|
-
[
|
315
|
-
|
316
|
-
|
317
|
-
elsif !@association
|
318
|
-
options[:order] = :default
|
258
|
+
[finder.table_name, field]
|
259
|
+
end
|
260
|
+
finder = finder.order("#{table}.#{column}", @sort_direction)
|
319
261
|
end
|
320
262
|
end
|
321
|
-
|
322
|
-
model_or_assoc.find(:all, options, &conditions_proc)
|
263
|
+
finder
|
323
264
|
end
|
324
|
-
|
325
265
|
|
326
|
-
def find_instance_or_not_found(this=nil)
|
327
|
-
begin
|
328
|
-
this || find_instance
|
329
|
-
rescue ActiveRecord::RecordNotFound
|
330
|
-
not_found
|
331
|
-
false
|
332
|
-
end
|
333
|
-
end
|
334
266
|
|
335
|
-
|
336
|
-
|
337
|
-
Hobo.can_create?(current_user, record)
|
338
|
-
else
|
339
|
-
Hobo.can_update?(current_user, original, record)
|
340
|
-
end
|
341
|
-
status = if can
|
342
|
-
record.save ? :valid : :invalid
|
343
|
-
else
|
344
|
-
:not_allowed
|
345
|
-
end
|
346
|
-
set_status(status)
|
347
|
-
end
|
267
|
+
# --- Action implementation helpers --- #
|
268
|
+
|
348
269
|
|
349
|
-
def
|
350
|
-
|
270
|
+
def find_instance(*args)
|
271
|
+
options = args.extract_options!
|
272
|
+
id = args.first || params[:id]
|
273
|
+
model.user_find(current_user, id, options)
|
351
274
|
end
|
352
|
-
|
353
|
-
def invalid?; @status == :invalid; end
|
354
275
|
|
355
|
-
def valid?; @status == :valid; end
|
356
|
-
|
357
|
-
def not_allowed?; @status == :not_allowed; end
|
358
276
|
|
277
|
+
def invalid?; !valid?; end
|
278
|
+
|
279
|
+
|
280
|
+
def valid?; this.errors.empty?; end
|
281
|
+
|
359
282
|
|
360
283
|
def re_render_form(default_action)
|
361
284
|
if params[:page_path]
|
362
285
|
controller, view = Controller.controller_and_view_for(params[:page_path])
|
363
286
|
view = default_action if view == Dryml::EMPTY_PAGE
|
364
|
-
|
287
|
+
render :action => view, :controller => controller
|
365
288
|
else
|
366
|
-
|
289
|
+
render :action => default_action
|
367
290
|
end
|
368
291
|
end
|
369
292
|
|
370
293
|
|
371
|
-
def
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
def destination_after_create(record)
|
294
|
+
def destination_after_submit(record=nil)
|
295
|
+
record ||= this
|
296
|
+
|
377
297
|
# The after_submit post parameter takes priority
|
378
298
|
params[:after_submit] ||
|
379
299
|
|
380
|
-
# Then try the
|
381
|
-
object_url(@this
|
300
|
+
# Then try the record's show page
|
301
|
+
object_url(@this) ||
|
382
302
|
|
383
303
|
# Then the show page of the 'owning' object if there is one
|
384
|
-
(@this.
|
304
|
+
(@this.class.default_dependent_on && object_url(@this.class.default_dependent_on)) ||
|
385
305
|
|
386
306
|
# Last try - the index page for this model
|
387
|
-
object_url(@this.class
|
307
|
+
object_url(@this.class) ||
|
388
308
|
|
389
309
|
# Give up
|
390
310
|
home_page
|
391
311
|
end
|
392
312
|
|
393
313
|
|
314
|
+
def response_block(&b)
|
315
|
+
if b
|
316
|
+
if b.arity == 1
|
317
|
+
respond_to {|wants| yield(wants) }
|
318
|
+
else
|
319
|
+
yield
|
320
|
+
end
|
321
|
+
performed?
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def find_or_paginate(finder, options)
|
326
|
+
do_pagination = options.delete(:paginate) != false && request.format.in?(PAGINATE_FORMATS)
|
327
|
+
if do_pagination && !finder.respond_to?(:paginate)
|
328
|
+
do_pagination = false
|
329
|
+
logger.warn "Hobo::ModelController: Pagination is not available. To enable, please install will_paginate or a duck-type compatible paginator"
|
330
|
+
end
|
331
|
+
|
332
|
+
if do_pagination
|
333
|
+
finder.paginate(options.reverse_merge(:page => params[:page] || 1))
|
334
|
+
else
|
335
|
+
finder.all(options)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
|
394
340
|
# --- Action implementations --- #
|
395
341
|
|
396
342
|
def hobo_index(*args, &b)
|
397
343
|
options = args.extract_options!
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
@this = if collection.blank?
|
402
|
-
paginated_find(options)
|
403
|
-
elsif collection.is_a?(String, Symbol)
|
404
|
-
paginated_find(collection, options) # a scope name
|
405
|
-
else
|
406
|
-
collection
|
407
|
-
end
|
408
|
-
instance_variable_set("@#{@model.name.pluralize.underscore}", @this)
|
409
|
-
response_block(&b) or hobo_render
|
344
|
+
finder = args.first || model
|
345
|
+
self.this = find_or_paginate(finder, options)
|
346
|
+
response_block(&b)
|
410
347
|
end
|
411
348
|
|
412
349
|
|
413
350
|
def hobo_show(*args, &b)
|
414
351
|
options = args.extract_options!
|
415
|
-
|
416
|
-
|
417
|
-
@this = find_instance_or_not_found(args.first) and
|
418
|
-
begin
|
419
|
-
set_status(:not_allowed) unless Hobo.can_view?(current_user, @this)
|
420
|
-
set_named_this!
|
421
|
-
response_block(&b) or
|
422
|
-
if not_allowed?
|
423
|
-
permission_denied
|
424
|
-
else
|
425
|
-
hobo_render
|
426
|
-
end
|
427
|
-
end
|
352
|
+
self.this = find_instance(options)
|
353
|
+
response_block(&b)
|
428
354
|
end
|
429
|
-
|
430
|
-
|
431
|
-
def hobo_new(
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
@this.set_creator(current_user) if options.fetch(:set_creator, true)
|
436
|
-
|
437
|
-
set_status(:not_allowed) unless Hobo.can_create?(current_user, @this)
|
438
|
-
set_named_this!
|
439
|
-
response_block(&b) or
|
440
|
-
if not_allowed?
|
441
|
-
permission_denied
|
442
|
-
else
|
443
|
-
hobo_render
|
444
|
-
end
|
355
|
+
|
356
|
+
|
357
|
+
def hobo_new(new_record=nil, &b)
|
358
|
+
self.this = new_record || model.new
|
359
|
+
this.user_changes!(current_user) # set_creator and permission check
|
360
|
+
response_block(&b)
|
445
361
|
end
|
446
362
|
|
447
|
-
|
363
|
+
|
448
364
|
def hobo_create(*args, &b)
|
449
365
|
options = args.extract_options!
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
366
|
+
self.this = args.first || new_for_create
|
367
|
+
this.user_save_changes(current_user, options[:attributes] || attribute_parameters)
|
368
|
+
create_response(&b)
|
369
|
+
end
|
370
|
+
|
371
|
+
|
372
|
+
def attribute_parameters
|
373
|
+
params[this.class.name.underscore]
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
def new_for_create
|
378
|
+
if model.has_inheritance_column? && (type_attr = params['type']) && type_attr.in?(model.send(:subclasses).*.name)
|
379
|
+
type_attr.constantize
|
380
|
+
else
|
381
|
+
model
|
382
|
+
end.new
|
383
|
+
end
|
384
|
+
|
385
|
+
|
386
|
+
def create_response(&b)
|
387
|
+
flash[:notice] = "The #{@this.class.name.titleize.downcase} was created successfully" if !request.xhr? && valid?
|
468
388
|
|
469
389
|
response_block(&b) or
|
470
390
|
if valid?
|
471
391
|
respond_to do |wants|
|
472
|
-
wants.html { redirect_to
|
392
|
+
wants.html { redirect_to destination_after_submit }
|
473
393
|
wants.js { hobo_ajax_response || render(:text => "") }
|
474
394
|
end
|
475
|
-
|
395
|
+
else
|
476
396
|
respond_to do |wants|
|
477
397
|
wants.html { re_render_form(:new) }
|
478
398
|
wants.js { render(:status => 500,
|
479
|
-
:text => ("
|
480
|
-
|
399
|
+
:text => ("Couldn't create the #{this.class.name.titleize.downcase}.\n" +
|
400
|
+
this.errors.full_messages.join("\n"))) }
|
481
401
|
end
|
482
|
-
elsif not_allowed?
|
483
|
-
permission_denied
|
484
402
|
end
|
485
403
|
end
|
486
404
|
|
487
405
|
|
488
|
-
def hobo_edit(*args, &b)
|
489
|
-
hobo_show(*args, &b)
|
490
|
-
end
|
491
|
-
|
492
|
-
|
493
406
|
def hobo_update(*args, &b)
|
494
407
|
options = args.extract_options!
|
495
|
-
options = LazyHash.new(options)
|
496
408
|
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
# with the changes so we clear them
|
501
|
-
@this.send(:clear_aggregation_cache)
|
502
|
-
@this.send(:clear_association_cache)
|
503
|
-
|
504
|
-
changes = params[@this.class.name.underscore]
|
505
|
-
@this.attributes = changes
|
506
|
-
save_and_set_status!(@this, original)
|
409
|
+
self.this ||= args.first || find_instance
|
410
|
+
changes = options[:attributes] || attribute_parameters
|
411
|
+
this.user_save_changes(current_user, changes)
|
507
412
|
|
508
413
|
# Ensure current_user isn't out of date
|
509
414
|
@current_user = @this if @this == current_user
|
510
415
|
|
511
|
-
|
416
|
+
in_place_edit_field = changes.keys.first if changes.size == 1 && params[:render]
|
417
|
+
update_response(in_place_edit_field, &b)
|
418
|
+
end
|
419
|
+
|
420
|
+
|
421
|
+
def update_response(in_place_edit_field=nil, &b)
|
422
|
+
flash[:notice] = "Changes to the #{@this.class.name.humanize.downcase} were saved" if !request.xhr? && valid?
|
512
423
|
|
513
|
-
set_named_this!
|
514
424
|
response_block(&b) or
|
515
425
|
if valid?
|
516
426
|
respond_to do |wants|
|
517
427
|
wants.html do
|
518
|
-
redirect_to
|
428
|
+
redirect_to destination_after_submit
|
519
429
|
end
|
520
430
|
wants.js do
|
521
|
-
if
|
431
|
+
if in_place_edit_field
|
522
432
|
# Decreasingly hacky support for the scriptaculous in-place-editor
|
523
|
-
new_val = Hobo::Dryml.render_tag(@template, "view",
|
524
|
-
|
525
|
-
:no_wrapper => true)
|
526
|
-
hobo_ajax_response(@this, :new_field_value => new_val)
|
433
|
+
new_val = Hobo::Dryml.render_tag(@template, "view", :field => in_place_edit_field, :no_wrapper => true)
|
434
|
+
hobo_ajax_response(this, :new_field_value => new_val)
|
527
435
|
else
|
528
|
-
hobo_ajax_response(
|
436
|
+
hobo_ajax_response(this)
|
529
437
|
end
|
530
438
|
|
531
439
|
# Maybe no ajax requests were made
|
532
440
|
render :nothing => true unless performed?
|
533
441
|
end
|
534
442
|
end
|
535
|
-
|
443
|
+
else
|
536
444
|
respond_to do |wants|
|
537
445
|
wants.html { re_render_form(:edit) }
|
538
446
|
wants.js { render(:status => 500,
|
539
447
|
:text => ("There was a problem with that change.\n" +
|
540
448
|
@this.errors.full_messages.join("\n"))) }
|
541
449
|
end
|
542
|
-
elsif not_allowed?
|
543
|
-
permission_denied
|
544
450
|
end
|
545
451
|
end
|
546
452
|
|
547
453
|
|
548
454
|
def hobo_destroy(*args, &b)
|
549
455
|
options = args.extract_options!
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
flash[:notice] = "The #{model.name.titleize.downcase} was deleted" unless request.xhr?
|
559
|
-
end
|
560
|
-
|
456
|
+
self.this ||= args.first || find_instance
|
457
|
+
this.user_destroy(current_user)
|
458
|
+
flash[:notice] = "The #{model.name.titleize.downcase} was deleted" unless request.xhr?
|
459
|
+
destroy_response(&b)
|
460
|
+
end
|
461
|
+
|
462
|
+
|
463
|
+
def destroy_response(&b)
|
561
464
|
response_block(&b) or
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
respond_to do |wants|
|
566
|
-
wants.html { redirect_to(:action => "index") }
|
567
|
-
wants.js { hobo_ajax_response || render(:text => "") }
|
568
|
-
end
|
465
|
+
respond_to do |wants|
|
466
|
+
wants.html { redirect_to(:action => "index") }
|
467
|
+
wants.js { hobo_ajax_response || render(:text => "") }
|
569
468
|
end
|
570
469
|
end
|
571
|
-
|
470
|
+
|
572
471
|
|
573
|
-
def hobo_show_collection(
|
574
|
-
options =
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
if collection.is_a?(Array)
|
579
|
-
@this = collection
|
580
|
-
@reflection = collection.proxy_reflection if collection.respond_to?(:proxy_reflection)
|
581
|
-
else
|
582
|
-
toplevel_collection = collection.to_s.split(".").first
|
583
|
-
set_status(:not_allowed) unless Hobo.can_view?(current_user, @owner, toplevel_collection)
|
584
|
-
@this = paginated_find(@owner, collection, options) unless not_allowed?
|
472
|
+
def hobo_show_collection(association, *args, &b)
|
473
|
+
options = args.extract_options!
|
474
|
+
association = find_instance.send(association) if association.is_a?(String, Symbol)
|
475
|
+
if association.respond_to?(:origin)
|
476
|
+
association.origin.user_view(current_user, association.origin_attribute) # permission check
|
585
477
|
end
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
permission_denied
|
590
|
-
else
|
591
|
-
hobo_render(params[:action]) or (@reflection and hobo_render(:show_collection, @reflection.klass))
|
592
|
-
end
|
478
|
+
self.this = find_or_paginate(association, options)
|
479
|
+
dryml_fallback_tag("show_collection_page")
|
480
|
+
response_block(&b)
|
593
481
|
end
|
594
482
|
|
595
483
|
|
596
|
-
|
484
|
+
# TODO: This action needs some more tidying up
|
485
|
+
def hobo_new_in_collection(association, *args, &b)
|
597
486
|
options = args.extract_options!
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
@this = this || @association.new
|
604
|
-
set_named_this!
|
605
|
-
@this.set_creator(current_user) if options.fetch(:set_creator, true)
|
606
|
-
|
607
|
-
set_status(:not_allowed) unless Hobo.can_create?(current_user, @this)
|
608
|
-
|
609
|
-
response_block(&b) or
|
610
|
-
if not_allowed?
|
611
|
-
permission_denied
|
612
|
-
else
|
613
|
-
hobo_render("new_#{collection.to_s.singularize}") or hobo_render("new_in_collection", @this.class)
|
614
|
-
end
|
487
|
+
@association = association.is_a?(String, Symbol) ? find_instance.send(association) : association
|
488
|
+
self.this = args.first || @association.new
|
489
|
+
this.user_changes(current_user) # set_creator and permission check
|
490
|
+
dryml_fallback_tag("new_in_collection_page")
|
491
|
+
response_block(&b)
|
615
492
|
end
|
616
493
|
|
617
494
|
|
618
|
-
def hobo_completions
|
619
|
-
|
620
|
-
|
621
|
-
# Eval any defined filters
|
622
|
-
instance_eval(&opts[:data_filters_block]) if opts[:data_filters_block]
|
623
|
-
conditions = data_filter_conditions
|
624
|
-
q = params[:query]
|
625
|
-
items = model.find(:all) { all?(send("#{attr}_contains", q), conditions && block(conditions)) }
|
626
|
-
|
627
|
-
render :text => "<ul>\n" + items.map {|i| "<li>#{i.send(attr)}</li>\n"}.join + "</ul>"
|
628
|
-
else
|
629
|
-
render :text => "No completer for #{attr}", :status => 404
|
630
|
-
end
|
495
|
+
def hobo_completions(finder)
|
496
|
+
items = finder.find(:all)
|
497
|
+
render :text => "<ul>\n" + items.map {|i| "<li>#{i.send(attr)}</li>\n"}.join + "</ul>"
|
631
498
|
end
|
632
499
|
|
633
|
-
#def hobo_create_in_collection(collection, options={}, &b)
|
634
|
-
# hobo_create do
|
635
|
-
# hobo_new_in_collection(collection, :this => @this, &b)
|
636
|
-
# end
|
637
|
-
#end
|
638
500
|
|
639
501
|
|
640
502
|
# --- Response helpers --- #
|
641
503
|
|
642
504
|
|
643
|
-
def permission_denied(
|
505
|
+
def permission_denied(error)
|
644
506
|
if respond_to? :permission_denied_response
|
645
507
|
permission_denied_response
|
646
|
-
elsif render_tag("permission-denied-page", {
|
647
|
-
#
|
508
|
+
elsif render_tag("permission-denied-page", { }, :status => 403)
|
509
|
+
# job done
|
648
510
|
else
|
649
|
-
|
650
|
-
render :text => message, :status => 403
|
511
|
+
render :text => "Permission Denied", :status => 403
|
651
512
|
end
|
652
513
|
end
|
653
514
|
|
654
515
|
|
655
|
-
def not_found
|
516
|
+
def not_found(error)
|
656
517
|
if respond_to? :not_found_response
|
657
518
|
not_found_response
|
658
|
-
elsif render_tag("not-found-page", {
|
519
|
+
elsif render_tag("not-found-page", {}, :status => 404)
|
659
520
|
# cool
|
660
521
|
else
|
661
522
|
render(:text => "The page you requested cannot be found.", :status => 404)
|
@@ -663,46 +524,34 @@ module Hobo
|
|
663
524
|
end
|
664
525
|
|
665
526
|
|
666
|
-
def
|
667
|
-
|
668
|
-
|
669
|
-
begin
|
670
|
-
full_dir = "#{RAILS_ROOT}/app/views/#{dir}"
|
671
|
-
!Dir["#{full_dir}/#{name}.*"].empty?
|
672
|
-
end)
|
527
|
+
def this
|
528
|
+
@this ||= (instance_variable_get("@#{model.name.underscore}") ||
|
529
|
+
instance_variable_get("@#{model.name.underscore.pluralize}"))
|
673
530
|
end
|
674
|
-
|
675
531
|
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
end
|
685
|
-
nil
|
532
|
+
|
533
|
+
def this=(object)
|
534
|
+
ivar = if object.is_a?(Array)
|
535
|
+
(object.try.member_class || model).name.underscore.pluralize
|
536
|
+
else
|
537
|
+
model.name.underscore
|
538
|
+
end
|
539
|
+
@this = instance_variable_set("@#{ivar}", object)
|
686
540
|
end
|
687
|
-
|
688
541
|
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
if hobo_template_exists?(controller_path, page_kind)
|
694
|
-
render :action => page_kind
|
695
|
-
true
|
696
|
-
elsif (template = find_model_template(page_model, page_kind))
|
697
|
-
render :template => template
|
698
|
-
true
|
699
|
-
else
|
700
|
-
# This returns false if no such tag exists
|
701
|
-
render_tag("#{page_kind.to_s.dasherize}-page", :with => @this)
|
702
|
-
end
|
542
|
+
|
543
|
+
def dryml_context
|
544
|
+
this
|
703
545
|
end
|
704
546
|
|
705
547
|
|
548
|
+
def render_with_hobo_model(*args, &block)
|
549
|
+
options = args.extract_options!
|
550
|
+
self.this = options[:object] if options[:object]
|
551
|
+
this.user_view(current_user) if this && this.respond_to?(:user_view)
|
552
|
+
render_without_hobo_model(*args + [options], &block)
|
553
|
+
end
|
554
|
+
|
706
555
|
# --- filters --- #
|
707
556
|
|
708
557
|
def set_no_cache_headers
|
@@ -715,21 +564,12 @@ module Hobo
|
|
715
564
|
|
716
565
|
# --- end filters --- #
|
717
566
|
|
567
|
+
public
|
718
568
|
|
719
569
|
def model
|
720
570
|
self.class.model
|
721
571
|
end
|
722
572
|
|
723
|
-
|
724
|
-
def data_filter_conditions
|
725
|
-
active_filters = data_filters && (params.keys & data_filters.keys)
|
726
|
-
filters = data_filters
|
727
|
-
params = self.params
|
728
|
-
proc do
|
729
|
-
all?(*active_filters.map {|f| instance_exec(params[f], &filters[f])})
|
730
|
-
end unless active_filters.blank?
|
731
|
-
end
|
732
|
-
|
733
573
|
end
|
734
574
|
|
735
575
|
end
|