hobo 0.6.2 → 0.6.3
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.
- data/bin/hobo +21 -22
- data/hobo_files/plugin/CHANGES.txt +429 -4
- data/hobo_files/plugin/Rakefile +2 -2
- data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +6 -5
- data/hobo_files/plugin/generators/hobo_front_controller/templates/search.dryml +2 -2
- data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +20 -15
- data/hobo_files/plugin/generators/hobo_model/templates/model.rb +1 -0
- data/hobo_files/plugin/generators/hobo_model_controller/templates/controller.rb +2 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_base.css +1 -2
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.css +4 -3
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +94 -12
- data/hobo_files/plugin/generators/hobo_rapid/templates/lowpro.js +5 -183
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/stylesheets/application.css +1 -1
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +23 -1
- data/hobo_files/plugin/generators/hobo_user_controller/templates/controller.rb +2 -0
- data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +3 -1
- data/hobo_files/plugin/init.rb +18 -7
- data/hobo_files/plugin/lib/active_record/has_many_association.rb +2 -2
- data/hobo_files/plugin/lib/extensions.rb +56 -12
- data/hobo_files/plugin/lib/hobo.rb +25 -88
- data/hobo_files/plugin/lib/hobo/composite_model.rb +2 -0
- data/hobo_files/plugin/lib/hobo/controller.rb +40 -20
- data/hobo_files/plugin/lib/hobo/dryml.rb +122 -106
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +2 -1
- data/hobo_files/plugin/lib/hobo/dryml/part_context.rb +3 -2
- data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +19 -3
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +40 -25
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +41 -20
- data/hobo_files/plugin/lib/hobo/email_address.rb +4 -1
- data/hobo_files/plugin/lib/hobo/enum_string.rb +50 -0
- data/hobo_files/plugin/lib/hobo/field_declaration_dsl.rb +36 -0
- data/hobo_files/plugin/lib/hobo/field_spec.rb +4 -7
- data/hobo_files/plugin/lib/hobo/hobo_helper.rb +47 -44
- data/hobo_files/plugin/lib/hobo/html_string.rb +2 -0
- data/hobo_files/plugin/lib/hobo/markdown_string.rb +2 -0
- data/hobo_files/plugin/lib/hobo/model.rb +158 -89
- data/hobo_files/plugin/lib/hobo/model_controller.rb +422 -376
- data/hobo_files/plugin/lib/hobo/model_queries.rb +1 -1
- data/hobo_files/plugin/lib/hobo/model_router.rb +174 -0
- data/hobo_files/plugin/lib/hobo/password_string.rb +2 -0
- data/hobo_files/plugin/lib/hobo/percentage.rb +14 -0
- data/hobo_files/plugin/lib/hobo/plugins.rb +4 -4
- data/hobo_files/plugin/lib/hobo/rapid_helper.rb +10 -2
- data/hobo_files/plugin/lib/hobo/text.rb +3 -3
- data/hobo_files/plugin/lib/hobo/textile_string.rb +2 -0
- data/hobo_files/plugin/lib/hobo/undefined.rb +3 -2
- data/hobo_files/plugin/lib/hobo/{authenticated_user.rb → user.rb} +10 -3
- data/hobo_files/plugin/lib/hobo/user_controller.rb +27 -23
- data/hobo_files/plugin/tags/core.dryml +8 -2
- data/hobo_files/plugin/tags/rapid.dryml +52 -40
- data/hobo_files/plugin/tags/rapid_document_tags.dryml +15 -11
- data/hobo_files/plugin/tags/rapid_editing.dryml +41 -9
- data/hobo_files/plugin/tags/rapid_forms.dryml +136 -36
- data/hobo_files/plugin/tags/rapid_navigation.dryml +2 -2
- data/hobo_files/plugin/tags/rapid_pages.dryml +204 -221
- data/hobo_files/plugin/tags/rapid_plus.dryml +8 -6
- data/hobo_files/plugin/tags/rapid_support.dryml +2 -3
- metadata +44 -42
- data/hobo_files/plugin/lib/hobo/define_tags.rb +0 -56
- data/hobo_files/plugin/lib/hobo/http_parameters.rb +0 -225
@@ -14,41 +14,41 @@ module Hobo
|
|
14
14
|
|
15
15
|
VIEWLIB_DIR = "taglibs"
|
16
16
|
|
17
|
-
|
17
|
+
PAGINATE_FORMATS = [ Mime::HTML, Mime::ALL ]
|
18
18
|
|
19
19
|
class << self
|
20
20
|
|
21
21
|
def included(base)
|
22
|
-
base.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
base.class_eval do
|
23
|
+
@auto_actions ||= {}
|
24
|
+
|
25
|
+
extend ClassMethods
|
26
|
+
helper_method :find_partial, :model, :current_user
|
27
|
+
before_filter :set_no_cache_headers
|
27
28
|
end
|
28
29
|
|
29
|
-
base.before_filter :set_no_cache_headers
|
30
|
-
|
31
30
|
Hobo::Controller.included_in_class(base)
|
32
31
|
end
|
33
32
|
|
34
33
|
def find_partial(klass, as)
|
35
|
-
find_model_template(klass, as, true)
|
34
|
+
find_model_template(klass, as, :is_parital => true)
|
36
35
|
end
|
37
36
|
|
38
37
|
|
39
38
|
def template_path(dir, name, is_partial)
|
40
39
|
fileRx = is_partial ? /^_#{name}\.[^.]+/ : /^#{name}\.[^.]+/
|
41
40
|
full_dir = "#{RAILS_ROOT}/app/views/#{dir}"
|
42
|
-
|
41
|
+
if File.exists?(full_dir) && Dir.entries(full_dir).grep(fileRx).any?
|
43
42
|
return "#{dir}/#{name}"
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
47
46
|
|
48
|
-
def find_model_template(klass, name,
|
47
|
+
def find_model_template(klass, name, options={})
|
49
48
|
while klass and klass != ActiveRecord::Base
|
50
49
|
dir = klass.name.underscore.pluralize
|
51
|
-
|
50
|
+
dir = File.join(options[:subsite], dir) if options[:subsite]
|
51
|
+
path = template_path(dir, name, options[:is_partial])
|
52
52
|
return path if path
|
53
53
|
|
54
54
|
klass = klass.superclass
|
@@ -56,33 +56,32 @@ module Hobo
|
|
56
56
|
nil
|
57
57
|
end
|
58
58
|
|
59
|
+
end
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
|
63
|
+
attr_writer :model
|
59
64
|
|
60
|
-
def add_collection_actions(
|
61
|
-
defined_methods =
|
65
|
+
def add_collection_actions(name)
|
66
|
+
defined_methods = instance_methods
|
62
67
|
|
63
68
|
show_collection_method = "show_#{name}".to_sym
|
64
|
-
|
65
|
-
|
69
|
+
if show_collection_method.not_in?(defined_methods) && include_action?(show_collection_method, true)
|
70
|
+
define_method show_collection_method do
|
66
71
|
hobo_show_collection(name)
|
67
|
-
end
|
72
|
+
end
|
68
73
|
end
|
69
74
|
|
70
|
-
if Hobo.simple_has_many_association?(
|
75
|
+
if Hobo.simple_has_many_association?(model.reflections[name])
|
71
76
|
new_method = "new_#{name.to_s.singularize}"
|
72
|
-
if new_method.not_in?(defined_methods)
|
73
|
-
|
77
|
+
if new_method.not_in?(defined_methods) && include_action?(new_method, true)
|
78
|
+
define_method new_method do
|
74
79
|
hobo_new_in_collection(name)
|
75
80
|
end
|
76
81
|
end
|
77
82
|
end
|
78
83
|
end
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
module ClassMethods
|
83
|
-
|
84
|
-
attr_writer :model
|
85
|
-
|
84
|
+
|
86
85
|
def web_methods
|
87
86
|
@web_methods ||= superclass.respond_to?(:web_methods) ? superclass.web_methods : []
|
88
87
|
end
|
@@ -91,6 +90,10 @@ module Hobo
|
|
91
90
|
@show_actions ||= superclass.respond_to?(:show_actions) ? superclass.show_actions : []
|
92
91
|
end
|
93
92
|
|
93
|
+
def index_actions
|
94
|
+
@index_actions ||= superclass.respond_to?(:index_actions) ? superclass.index_actions : []
|
95
|
+
end
|
96
|
+
|
94
97
|
def collections
|
95
98
|
# By default, all has_many associations are published
|
96
99
|
@collections ||= if superclass.respond_to?(:collections)
|
@@ -119,66 +122,99 @@ module Hobo
|
|
119
122
|
end
|
120
123
|
|
121
124
|
|
122
|
-
def web_method(web_name,
|
123
|
-
method_name ||= web_name
|
125
|
+
def web_method(web_name, options={}, &block)
|
124
126
|
web_methods << web_name.to_sym
|
125
|
-
|
127
|
+
method = options[:method] || web_name
|
128
|
+
got_block = block_given?
|
129
|
+
define_method web_name do
|
130
|
+
# Make sure we have a copy of the options - it is being mutated somewhere
|
131
|
+
opts = {}.merge(options)
|
132
|
+
@this = find_instance(opts) unless opts[:no_find]
|
133
|
+
permission_denied unless Hobo.can_call?(current_user, @this, method)
|
134
|
+
if got_block
|
135
|
+
instance_eval(&block)
|
136
|
+
else
|
137
|
+
@this.send(method)
|
138
|
+
end
|
139
|
+
hobo_ajax_response unless performed?
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def auto_actions(*args)
|
145
|
+
# auto_actions is either an array - the actions to provide, or a hash: { :except => [...] }
|
146
|
+
@auto_actions = case args.first
|
147
|
+
when :all then args.extract_options!
|
148
|
+
when :none then []
|
149
|
+
else args
|
150
|
+
end
|
151
|
+
|
152
|
+
self.class_eval do
|
153
|
+
def index; hobo_index end if include_action?(:index)
|
154
|
+
def show; hobo_show end if include_action?(:show)
|
155
|
+
def new; hobo_new end if include_action?(:new)
|
156
|
+
def create; hobo_create end if include_action?(:create)
|
157
|
+
def edit; hobo_edit end if include_action?(:edit)
|
158
|
+
def update; hobo_update end if include_action?(:update)
|
159
|
+
def destroy; hobo_destroy end if include_action?(:destroy)
|
160
|
+
|
161
|
+
def completions; hobo_completions end if include_action?(:completions)
|
162
|
+
|
163
|
+
collections.each { |c| add_collection_actions(c.to_sym) }
|
164
|
+
end
|
126
165
|
end
|
127
166
|
|
128
167
|
|
129
|
-
def show_action(*names)
|
168
|
+
def show_action(*names, &block)
|
130
169
|
show_actions.concat(names)
|
131
170
|
for name in names
|
132
|
-
|
171
|
+
if block
|
172
|
+
define_method(name, &block)
|
173
|
+
else
|
174
|
+
define_method(name) { hobo_show }
|
175
|
+
end
|
133
176
|
end
|
134
177
|
end
|
135
178
|
|
179
|
+
def index_action(*names, &block)
|
180
|
+
index_actions.concat(names)
|
181
|
+
for name in names
|
182
|
+
if block
|
183
|
+
define_method(name, &block)
|
184
|
+
else
|
185
|
+
define_method(name) { hobo_index }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
136
189
|
|
137
190
|
def publish_collection(*names)
|
138
191
|
collections.concat(names)
|
139
|
-
names.each {|n|
|
192
|
+
names.each {|n| add_collection_actions(n)}
|
140
193
|
end
|
141
194
|
|
142
195
|
|
143
|
-
def find_instance(id)
|
196
|
+
def find_instance(id, options={})
|
144
197
|
if model.id_name? and id !~ /^\d+$/
|
145
|
-
model.find_by_id_name(id)
|
198
|
+
model.find_by_id_name(id, options)
|
146
199
|
else
|
147
|
-
model.find(id)
|
200
|
+
model.find(id, options)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def include_action?(name, collection_action=false)
|
205
|
+
name = name.to_sym
|
206
|
+
if @auto_actions.is_a?(Array)
|
207
|
+
# White list
|
208
|
+
name.in?(@auto_actions) || (collection_action && :collections.in?(@auto_actions))
|
209
|
+
else
|
210
|
+
# Black list
|
211
|
+
except = Array(@auto_actions[:except])
|
212
|
+
name.not_in?(except) || (collection_action && :collections.not_in?(except))
|
148
213
|
end
|
149
214
|
end
|
150
215
|
|
151
216
|
end
|
152
217
|
|
153
|
-
include HttpParameters
|
154
|
-
|
155
|
-
# --- ACTIONS --- #
|
156
|
-
|
157
|
-
def index; hobo_index; end
|
158
|
-
def show; hobo_show; end
|
159
|
-
def new; hobo_new; end
|
160
|
-
def create; hobo_create; end
|
161
|
-
def edit; hobo_edit; end
|
162
|
-
def update; hobo_update; end
|
163
|
-
def destroy; hobo_destroy; end
|
164
|
-
|
165
|
-
def completions
|
166
|
-
opts = self.class.autocompleter(params[:for])
|
167
|
-
if opts
|
168
|
-
# Eval any defined filters
|
169
|
-
instance_eval(&opts[:data_filters_block]) if opts[:data_filters_block]
|
170
|
-
conditions = data_filter_conditions
|
171
|
-
q = params[:query]
|
172
|
-
items = model.find(:all) { all?(send("#{attr}_contains", q), conditions && block(conditions)) }
|
173
|
-
|
174
|
-
render :text => "<ul>\n" + items.map {|i| "<li>#{i.send(attr)}</li>\n"}.join + "</ul>"
|
175
|
-
else
|
176
|
-
render :text => "No completer for #{attr}", :status => 404
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
|
181
|
-
# --- END OF ACTIONS --- #
|
182
218
|
|
183
219
|
protected
|
184
220
|
|
@@ -187,51 +223,65 @@ module Hobo
|
|
187
223
|
@data_filters[name] = b
|
188
224
|
end
|
189
225
|
|
190
|
-
def search(*
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
226
|
+
def search(*args)
|
227
|
+
if args.first.is_a?(Class)
|
228
|
+
model, search_string, *columns = args
|
229
|
+
else
|
230
|
+
model = self.model
|
231
|
+
search_string, *columns = args
|
232
|
+
end
|
233
|
+
return nil if search_string.blank?
|
234
|
+
|
235
|
+
model.conditions do
|
236
|
+
words = search_string.split
|
237
|
+
terms = words.map do |word|
|
238
|
+
cols = columns.map do |c|
|
239
|
+
if c.is_a?(Symbol)
|
240
|
+
send("#{c}_contains", word)
|
241
|
+
elsif c.is_a?(Hash)
|
242
|
+
c.map do |k, v|
|
243
|
+
related = send(k)
|
244
|
+
v = [v] unless v.is_a?(Array)
|
245
|
+
v.map { |related_col| related.send("#{related_col}_contains", word) }
|
246
|
+
end
|
200
247
|
end
|
201
|
-
end
|
202
|
-
|
203
|
-
|
248
|
+
end.flatten
|
249
|
+
any?(*cols)
|
250
|
+
end
|
251
|
+
all?(*terms)
|
204
252
|
end
|
205
253
|
end
|
206
254
|
|
207
255
|
attr_accessor :data_filters
|
208
256
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
257
|
+
|
258
|
+
# --- Action implementation helpers --- #
|
259
|
+
|
260
|
+
def find_instance(*args)
|
261
|
+
options = args.extract_options!
|
262
|
+
res = self.class.find_instance(args.first || params[:id], options)
|
263
|
+
instance_variable_set("@#{model.name.underscore}", res)
|
264
|
+
res
|
217
265
|
end
|
218
266
|
|
219
|
-
# --- action implementations --- #
|
220
267
|
|
221
|
-
def
|
222
|
-
|
223
|
-
|
224
|
-
@this = options[:collection] || paginated_find(options)
|
268
|
+
def set_named_this!
|
269
|
+
instance_variable_set("@#{@this.class.name.underscore}", @this)
|
270
|
+
end
|
225
271
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
272
|
+
|
273
|
+
def response_block(&b)
|
274
|
+
if b
|
275
|
+
if b.arity == 1
|
276
|
+
respond_to {|wants| yield(wants) }
|
277
|
+
else
|
278
|
+
yield
|
279
|
+
end
|
280
|
+
performed?
|
231
281
|
end
|
232
282
|
end
|
233
|
-
|
234
283
|
|
284
|
+
|
235
285
|
def paginated_find(*args, &b)
|
236
286
|
options = args.extract_options!
|
237
287
|
filter_conditions = data_filter_conditions
|
@@ -242,33 +292,39 @@ module Hobo
|
|
242
292
|
end
|
243
293
|
|
244
294
|
@association = options.delete(:association) ||
|
245
|
-
if args.
|
295
|
+
if args.length == 1
|
296
|
+
scopes = args.first
|
297
|
+
@association = scopes.to_s.split(".").inject(model) { |m, name| m.send(name) }
|
298
|
+
elsif args.length == 2
|
246
299
|
owner, collection_name = args
|
247
300
|
@association = collection_name.to_s.split(".").inject(owner) { |m, name| m.send(name) }
|
248
301
|
end
|
249
302
|
@reflection = @association.proxy_reflection if @association._?.respond_to?(:proxy_reflection)
|
250
303
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
else
|
258
|
-
model.count(count_options, &conditions_proc)
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
puts total_number
|
263
|
-
|
304
|
+
model_or_assoc, @member_class = if @association
|
305
|
+
[@association, @association.member_class]
|
306
|
+
else
|
307
|
+
[model, model]
|
308
|
+
end
|
309
|
+
|
264
310
|
page_size = options.delete(:page_size) || 20
|
265
311
|
page = options.delete(:page) || params[:page]
|
266
|
-
|
312
|
+
|
313
|
+
paginate = options.fetch(:paginate, request.format.in?(PAGINATE_FORMATS))
|
314
|
+
options.delete(:paginate)
|
315
|
+
if paginate
|
316
|
+
total_number = options.delete(:total_number) ||
|
317
|
+
begin
|
318
|
+
# If there is a conditions block, it may depend on the includes
|
319
|
+
count_options = conditions_proc ? { :include => options[:include] } : {}
|
320
|
+
model_or_assoc.count(count_options, &conditions_proc)
|
321
|
+
end
|
322
|
+
|
323
|
+
@pages = ::ActionController::Pagination::Paginator.new(self, total_number, page_size, page)
|
267
324
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
}.merge(options)
|
325
|
+
options = options.reverse_merge(:limit => @pages.items_per_page,
|
326
|
+
:offset => @pages.current.offset)
|
327
|
+
end
|
272
328
|
|
273
329
|
unless options.has_key?(:order)
|
274
330
|
_, desc, field = *params[:sort]._?.match(/^(-)?([a-z_]+(?:\.[a-z_]+)?)$/)
|
@@ -277,307 +333,346 @@ module Hobo
|
|
277
333
|
@sort_direction = desc ? "desc" : "asc"
|
278
334
|
|
279
335
|
table, column = if field =~ /^(.*)\.(.*)$/
|
280
|
-
|
336
|
+
[$1.camelize.constantize.table_name, $2]
|
281
337
|
else
|
282
|
-
|
283
|
-
[sort_model.table_name, field]
|
338
|
+
[@member_class.table_name, field]
|
284
339
|
end
|
285
|
-
|
340
|
+
options[:order] = "#{table}.#{column} #{@sort_direction}"
|
286
341
|
elsif !@association
|
287
342
|
options[:order] = :default
|
288
343
|
end
|
289
344
|
end
|
290
345
|
|
291
|
-
|
292
|
-
|
346
|
+
model_or_assoc.find(:all, options, &conditions_proc)
|
347
|
+
end
|
348
|
+
|
349
|
+
|
350
|
+
def find_instance_or_not_found(this=nil)
|
351
|
+
begin
|
352
|
+
this || find_instance
|
353
|
+
rescue ActiveRecord::RecordNotFound
|
354
|
+
not_found
|
355
|
+
false
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def save_and_set_status!(record, original=nil)
|
360
|
+
can = if record.new_record?
|
361
|
+
Hobo.can_create?(current_user, record)
|
362
|
+
else
|
363
|
+
Hobo.can_update?(current_user, original, record)
|
364
|
+
end
|
365
|
+
status = if can
|
366
|
+
record.save ? :valid : :invalid
|
367
|
+
else
|
368
|
+
:not_allowed
|
369
|
+
end
|
370
|
+
set_status(status)
|
371
|
+
end
|
372
|
+
|
373
|
+
def set_status(status)
|
374
|
+
@status = status
|
375
|
+
end
|
376
|
+
|
377
|
+
def invalid?; @status == :invalid; end
|
378
|
+
|
379
|
+
def valid?; @status == :valid; end
|
380
|
+
|
381
|
+
def not_allowed?; @status == :not_allowed; end
|
382
|
+
|
383
|
+
|
384
|
+
def re_render_form(default_action)
|
385
|
+
if params[:page_path]
|
386
|
+
controller, view = Controller.controller_and_view_for(params[:page_path])
|
387
|
+
hobo_render(view, model_for(controller))
|
293
388
|
else
|
294
|
-
|
389
|
+
hobo_render(default_action)
|
295
390
|
end
|
296
391
|
end
|
297
392
|
|
298
393
|
|
299
|
-
def
|
300
|
-
|
301
|
-
options[this_option] || find_instance
|
302
|
-
rescue ActiveRecord::RecordNotFound
|
303
|
-
nil
|
304
|
-
end
|
305
|
-
|
306
|
-
not_found unless x
|
307
|
-
x
|
394
|
+
def model_for(controller_name)
|
395
|
+
"#{controller_name.camelize}Controller".constantize.model
|
308
396
|
end
|
309
397
|
|
310
398
|
|
311
|
-
|
399
|
+
# --- Action implementations --- #
|
400
|
+
|
401
|
+
def hobo_index(*args, &b)
|
402
|
+
options = args.extract_options!
|
403
|
+
options = LazyHash.new(options)
|
404
|
+
@model = model
|
405
|
+
collection = args.first
|
406
|
+
@this = if collection.blank?
|
407
|
+
paginated_find(options)
|
408
|
+
elsif collection.is_a?(String, Symbol)
|
409
|
+
paginated_find(collection, options) # a scope name
|
410
|
+
else
|
411
|
+
collection
|
412
|
+
end
|
413
|
+
instance_variable_set("@#{@model.name.pluralize.underscore}", @this)
|
414
|
+
response_block(&b) or hobo_render
|
415
|
+
end
|
416
|
+
|
417
|
+
|
418
|
+
def hobo_show(*args, &b)
|
419
|
+
options = args.extract_options!
|
312
420
|
options = LazyHash.new(options)
|
313
421
|
|
314
|
-
@this = find_instance_or_not_found(
|
315
|
-
|
316
|
-
|
422
|
+
@this = find_instance_or_not_found(args.first) and
|
423
|
+
begin
|
424
|
+
set_status(:not_allowed) unless Hobo.can_view?(current_user, @this)
|
317
425
|
set_named_this!
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
426
|
+
response_block(&b) or
|
427
|
+
if not_allowed?
|
428
|
+
permission_denied
|
429
|
+
else
|
430
|
+
hobo_render
|
431
|
+
end
|
322
432
|
end
|
323
|
-
end
|
324
433
|
end
|
325
434
|
|
326
435
|
|
327
|
-
def hobo_new(
|
436
|
+
def hobo_new(*args, &b)
|
437
|
+
options = args.extract_options!
|
328
438
|
options = LazyHash.new(options)
|
329
|
-
@this =
|
330
|
-
@this.set_creator(current_user)
|
439
|
+
@this = args.first || model.new
|
440
|
+
@this.set_creator(current_user) if options.fetch(:set_creator, true)
|
331
441
|
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
442
|
+
set_status(:not_allowed) unless Hobo.can_create?(current_user, @this)
|
443
|
+
set_named_this!
|
444
|
+
response_block(&b) or
|
445
|
+
if not_allowed?
|
446
|
+
permission_denied
|
447
|
+
else
|
448
|
+
hobo_render
|
449
|
+
end
|
339
450
|
end
|
340
451
|
|
341
452
|
|
342
|
-
def hobo_create(
|
453
|
+
def hobo_create(*args, &b)
|
454
|
+
options = args.extract_options!
|
343
455
|
options = LazyHash.new(options)
|
344
456
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
status = secure_change_transaction { initialize_record(@this, attributes) }
|
359
|
-
permission_denied(options) and return if status == :not_allowed
|
360
|
-
valid = status == :valid
|
361
|
-
end
|
362
|
-
|
457
|
+
@this = args.first ||
|
458
|
+
begin
|
459
|
+
create_model = if 'type'.in?(model.column_names) &&
|
460
|
+
(type_attr = params['type']) &&
|
461
|
+
type_attr.in?(model.send(:subclasses).every(:name))
|
462
|
+
type_attr.constantize
|
463
|
+
else
|
464
|
+
model
|
465
|
+
end
|
466
|
+
create_model.new(params[model.name.underscore])
|
467
|
+
end
|
468
|
+
@this.set_creator(current_user) if options.fetch(:set_creator, true)
|
469
|
+
save_and_set_status!(@this)
|
363
470
|
set_named_this!
|
364
471
|
|
365
|
-
if valid
|
366
|
-
|
367
|
-
|
368
|
-
|
472
|
+
flash[:notice] = "The #{model.name.titleize.downcase} was created successfully" if !request.xhr? && valid?
|
473
|
+
|
474
|
+
response_block(&b) or
|
475
|
+
if valid?
|
369
476
|
respond_to do |wants|
|
370
|
-
wants.html {
|
371
|
-
wants.js {
|
477
|
+
wants.html { redirect_to(params[:after_submit] || object_url(@this)) }
|
478
|
+
wants.js { hobo_ajax_response || render(:text => "") }
|
372
479
|
end
|
373
|
-
|
374
|
-
else
|
375
|
-
# Validation errors
|
376
|
-
unless options[:invalid_response]
|
480
|
+
elsif invalid?
|
377
481
|
respond_to do |wants|
|
378
|
-
wants.html {
|
379
|
-
wants.js
|
380
|
-
|
381
|
-
|
382
|
-
:text => ("There was a problem creating that #{create_model.name}.\n" +
|
383
|
-
@this.errors.full_messages.join("\n"))))
|
384
|
-
end
|
482
|
+
wants.html { re_render_form(:new) }
|
483
|
+
wants.js { render(:status => 500,
|
484
|
+
:text => ("There was a problem creating that #{create_model.name}.\n" +
|
485
|
+
@this.errors.full_messages.join("\n"))) }
|
385
486
|
end
|
487
|
+
elsif not_allowed?
|
488
|
+
permission_denied
|
386
489
|
end
|
387
|
-
end
|
388
490
|
end
|
389
491
|
|
390
|
-
|
391
|
-
def hobo_edit(
|
392
|
-
hobo_show(
|
393
|
-
end
|
492
|
+
|
493
|
+
def hobo_edit(*args, &b)
|
494
|
+
hobo_show(*args, &b)
|
495
|
+
end
|
394
496
|
|
395
497
|
|
396
|
-
def hobo_update(
|
498
|
+
def hobo_update(*args, &b)
|
499
|
+
options = args.extract_options!
|
397
500
|
options = LazyHash.new(options)
|
398
501
|
|
399
|
-
@this = find_instance_or_not_found(
|
400
|
-
|
502
|
+
@this = find_instance_or_not_found(args.first) or return
|
503
|
+
original = @this.duplicate
|
504
|
+
# 'duplicate' can cause these to be set, but they can conflict
|
505
|
+
# with the changes so we clear them
|
506
|
+
@this.send(:clear_aggregation_cache)
|
507
|
+
@this.send(:clear_association_cache)
|
401
508
|
|
402
509
|
changes = params[model.name.underscore]
|
403
|
-
|
510
|
+
@this.attributes = changes
|
511
|
+
save_and_set_status!(@this, original)
|
512
|
+
|
513
|
+
# Ensure current_user isn't out of date
|
514
|
+
@current_user = @this if @this == current_user
|
515
|
+
|
516
|
+
flash[:notice] = "Changes to the #{model.name.titleize.downcase} were saved" if !request.xhr? && valid?
|
404
517
|
|
405
518
|
set_named_this!
|
406
|
-
|
407
|
-
|
408
|
-
permission_denied(options)
|
409
|
-
return
|
410
|
-
|
411
|
-
when :valid
|
412
|
-
# Ensure current_user isn't out of date
|
413
|
-
@current_user = @this if @this == current_user
|
414
|
-
|
415
|
-
if block_given?
|
416
|
-
yield
|
417
|
-
else
|
519
|
+
response_block(&b) or
|
520
|
+
if valid?
|
418
521
|
respond_to do |wants|
|
419
522
|
wants.html do
|
420
|
-
|
523
|
+
redirect_to(params[:after_submit] || object_url(@this))
|
421
524
|
end
|
422
|
-
|
423
525
|
wants.js do
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
hobo_ajax_response(@this)
|
433
|
-
end
|
434
|
-
|
435
|
-
# Maybe no ajax requests were made
|
436
|
-
render :nothing => true unless performed?
|
526
|
+
if changes.size == 1
|
527
|
+
# Decreasingly hacky support for the scriptaculous in-place-editor
|
528
|
+
new_val = Hobo::Dryml.render_tag(@template, "view",
|
529
|
+
:with => @this, :field => changes.keys.first,
|
530
|
+
:no_wrapper => true)
|
531
|
+
hobo_ajax_response(@this, :new_field_value => new_val)
|
532
|
+
else
|
533
|
+
hobo_ajax_response(@this)
|
437
534
|
end
|
535
|
+
|
536
|
+
# Maybe no ajax requests were made
|
537
|
+
render :nothing => true unless performed?
|
438
538
|
end
|
439
539
|
end
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
overridable_response(options, :invalid_html_response) || render(:action => :edit)
|
447
|
-
end
|
448
|
-
|
449
|
-
wants.js do
|
450
|
-
overridable_response(options, :invalid_js_response) do
|
451
|
-
render(:status => 500,
|
452
|
-
:text => ("There was a problem with that change.\n" +
|
453
|
-
@this.errors.full_messages.join("\n")))
|
454
|
-
end
|
540
|
+
elsif invalid?
|
541
|
+
respond_to do |wants|
|
542
|
+
wants.html { re_render_form(:edit) }
|
543
|
+
wants.js { render(:status => 500,
|
544
|
+
:text => ("There was a problem with that change.\n" +
|
545
|
+
@this.errors.full_messages.join("\n"))) }
|
455
546
|
end
|
547
|
+
elsif not_allowed?
|
548
|
+
permission_denied
|
456
549
|
end
|
457
|
-
end
|
458
550
|
end
|
459
551
|
|
460
552
|
|
461
|
-
def hobo_destroy(
|
553
|
+
def hobo_destroy(*args, &b)
|
554
|
+
options = args.extract_options!
|
462
555
|
options = LazyHash.new(options)
|
463
|
-
@this = find_instance_or_not_found(
|
464
|
-
return unless @this
|
556
|
+
@this = find_instance_or_not_found(args.first) or return
|
465
557
|
|
466
558
|
set_named_this!
|
467
|
-
permission_denied(options) and return unless Hobo.can_delete?(current_user, @this)
|
468
559
|
|
469
|
-
@this
|
560
|
+
set_status(Hobo.can_delete?(current_user, @this) ? :valid : :not_allowed)
|
561
|
+
unless not_allowed?
|
562
|
+
@this.destroy
|
563
|
+
flash[:notice] = "The #{model.name.titleize.downcase} was deleted" unless request.xhr?
|
564
|
+
end
|
470
565
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
566
|
+
response_block(&b) or
|
567
|
+
if not_allowed?
|
568
|
+
permission_denied
|
569
|
+
else
|
570
|
+
respond_to do |wants|
|
571
|
+
wants.html { redirect_to(:action => "index") }
|
572
|
+
wants.js { hobo_ajax_response || render(:text => "") }
|
573
|
+
end
|
477
574
|
end
|
478
|
-
end
|
479
575
|
end
|
480
576
|
|
481
|
-
|
577
|
+
|
578
|
+
def hobo_show_collection(collection, options={}, &b)
|
482
579
|
options = LazyHash.new(options)
|
483
580
|
|
484
|
-
@owner = find_instance_or_not_found(options
|
485
|
-
return unless @owner
|
581
|
+
@owner = find_instance_or_not_found(options[:owner]) or return
|
486
582
|
|
487
|
-
|
488
|
-
|
489
|
-
@
|
490
|
-
|
491
|
-
if block_given?
|
492
|
-
yield
|
493
|
-
else
|
494
|
-
hobo_render(params[:action]) or hobo_render(:show_collection, @reflection.klass)
|
495
|
-
end
|
583
|
+
if collection.is_a?(Array)
|
584
|
+
@this = collection
|
585
|
+
@reflection = collection.proxy_reflection if collection.respond_to?(:proxy_reflection)
|
496
586
|
else
|
497
|
-
|
587
|
+
toplevel_collection = collection.to_s.split(".").first
|
588
|
+
set_status(:not_allowed) unless Hobo.can_view?(current_user, @owner, toplevel_collection)
|
589
|
+
@this = paginated_find(@owner, collection, options) unless not_allowed?
|
498
590
|
end
|
591
|
+
|
592
|
+
response_block(&b) or
|
593
|
+
if not_allowed?
|
594
|
+
permission_denied
|
595
|
+
else
|
596
|
+
hobo_render(params[:action]) or (@reflection and hobo_render(:show_collection, @reflection.klass))
|
597
|
+
end
|
499
598
|
end
|
500
599
|
|
501
600
|
|
502
|
-
def hobo_new_in_collection(collection,
|
601
|
+
def hobo_new_in_collection(collection, *args, &b)
|
602
|
+
options = args.extract_options!
|
603
|
+
this = args.first
|
503
604
|
options = LazyHash.new(options)
|
504
|
-
@owner = find_instance_or_not_found(options, :owner)
|
505
|
-
return unless @owner
|
506
|
-
|
507
|
-
permission_denied(options) and return unless Hobo.can_view?(current_user, @owner, collection)
|
508
605
|
|
509
|
-
@
|
510
|
-
@
|
511
|
-
@this
|
606
|
+
@owner = find_instance_or_not_found(options[:owner]) or return
|
607
|
+
@association = collection.is_a?(Array) ? collection : @owner.send(collection)
|
608
|
+
@this = this || @association.new
|
609
|
+
set_named_this!
|
610
|
+
@this.set_creator(current_user) if options.fetch(:set_creator, true)
|
512
611
|
|
513
|
-
|
612
|
+
set_status(:not_allowed) unless Hobo.can_create?(current_user, @this)
|
514
613
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
614
|
+
response_block(&b) or
|
615
|
+
if not_allowed?
|
616
|
+
permission_denied
|
617
|
+
else
|
618
|
+
hobo_render("new_#{collection.to_s.singularize}") or hobo_render("new_in_collection", @this.class)
|
619
|
+
end
|
520
620
|
end
|
521
621
|
|
522
|
-
|
523
|
-
# --- end action implementations --- #
|
524
622
|
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
623
|
+
def hobo_completions
|
624
|
+
opts = self.class.autocompleter(params[:for])
|
625
|
+
if opts
|
626
|
+
# Eval any defined filters
|
627
|
+
instance_eval(&opts[:data_filters_block]) if opts[:data_filters_block]
|
628
|
+
conditions = data_filter_conditions
|
629
|
+
q = params[:query]
|
630
|
+
items = model.find(:all) { all?(send("#{attr}_contains", q), conditions && block(conditions)) }
|
631
|
+
|
632
|
+
render :text => "<ul>\n" + items.map {|i| "<li>#{i.send(attr)}</li>\n"}.join + "</ul>"
|
633
|
+
else
|
634
|
+
render :text => "No completer for #{attr}", :status => 404
|
635
|
+
end
|
530
636
|
end
|
531
637
|
|
532
|
-
#
|
638
|
+
#def hobo_create_in_collection(collection, options={}, &b)
|
639
|
+
# hobo_create do
|
640
|
+
# hobo_new_in_collection(collection, :this => @this, &b)
|
641
|
+
# end
|
642
|
+
#end
|
533
643
|
|
644
|
+
|
645
|
+
# --- Response helpers --- #
|
534
646
|
|
535
|
-
|
536
|
-
headers["Pragma"] = "no-cache"
|
537
|
-
#headers["Cache-Control"] = ["must-revalidate", "no-cache", "no-store"]
|
538
|
-
#headers["Cache-Control"] = "no-cache"
|
539
|
-
headers["Cache-Control"] = "no-store"
|
540
|
-
headers["Expires"] ='0'
|
541
|
-
end
|
542
|
-
|
543
|
-
|
647
|
+
|
544
648
|
def permission_denied(options={})
|
545
|
-
if
|
546
|
-
# do nothing (callback handled by LazyHash)
|
547
|
-
elsif respond_to? :permission_denied_response
|
649
|
+
if respond_to? :permission_denied_response
|
548
650
|
permission_denied_response
|
651
|
+
elsif render_tag("PermissionDeniedPage", { :with => @this }, :status => 403)
|
652
|
+
# cool
|
549
653
|
else
|
550
654
|
message = options[:message] || "Permission Denied"
|
551
655
|
render :text => message, :status => 403
|
552
656
|
end
|
553
657
|
end
|
554
658
|
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
elsif respond_to? :not_found_response
|
659
|
+
|
660
|
+
def not_found
|
661
|
+
if respond_to? :not_found_response
|
559
662
|
not_found_response
|
663
|
+
elsif render_tag("NotFoundPage", { :with => @this }, :status => 404)
|
664
|
+
# cool
|
560
665
|
else
|
561
|
-
render(:text => "
|
666
|
+
render(:text => "The page you requested cannot be found.", :status => 404)
|
562
667
|
end
|
563
668
|
end
|
564
|
-
|
565
|
-
def find_instance(id=nil)
|
566
|
-
res = self.class.find_instance(id || params[:id])
|
567
|
-
instance_variable_set("@#{model.name.underscore}", res)
|
568
|
-
res
|
569
|
-
end
|
570
669
|
|
571
|
-
def set_named_this!
|
572
|
-
instance_variable_set("@#{model.name.underscore}", @this)
|
573
|
-
end
|
574
|
-
|
575
670
|
|
576
671
|
def hobo_render(page_kind = nil, page_model=nil)
|
577
672
|
page_kind ||= params[:action].to_sym
|
578
673
|
page_model ||= model
|
579
674
|
|
580
|
-
template =
|
675
|
+
template = ModelController.find_model_template(page_model, page_kind, :subsite => subsite)
|
581
676
|
|
582
677
|
begin
|
583
678
|
if template
|
@@ -601,17 +696,25 @@ module Hobo
|
|
601
696
|
end
|
602
697
|
end
|
603
698
|
|
699
|
+
|
700
|
+
# --- filters --- #
|
701
|
+
|
702
|
+
def set_no_cache_headers
|
703
|
+
headers["Pragma"] = "no-cache"
|
704
|
+
#headers["Cache-Control"] = ["must-revalidate", "no-cache", "no-store"]
|
705
|
+
#headers["Cache-Control"] = "no-cache"
|
706
|
+
headers["Cache-Control"] = "no-store"
|
707
|
+
headers["Expires"] ='0'
|
708
|
+
end
|
709
|
+
|
710
|
+
# --- end filters --- #
|
711
|
+
|
604
712
|
|
605
713
|
def model
|
606
714
|
self.class.model
|
607
715
|
end
|
608
716
|
|
609
717
|
|
610
|
-
def find_template
|
611
|
-
Hobo::ModelController.find_model_template(model, params[:action])
|
612
|
-
end
|
613
|
-
|
614
|
-
|
615
718
|
def data_filter_conditions
|
616
719
|
active_filters = data_filters && (params.keys & data_filters.keys)
|
617
720
|
filters = data_filters
|
@@ -621,63 +724,6 @@ module Hobo
|
|
621
724
|
end unless active_filters.blank?
|
622
725
|
end
|
623
726
|
|
624
|
-
|
625
|
-
def XXupdate_with_params(object, params)
|
626
|
-
return unless params
|
627
|
-
|
628
|
-
params.each_pair do |field,value|
|
629
|
-
field = field.to_sym
|
630
|
-
refl = object.class.reflections[field]
|
631
|
-
is_has_many = refl && Hobo.simple_has_many_association?(refl)
|
632
|
-
|
633
|
-
if is_has_many
|
634
|
-
items = if value.is_a? Array
|
635
|
-
value.map {|x| associated_record(object, refl, x) }
|
636
|
-
else
|
637
|
-
value.keys.every(:to_i).sort.map{|i| associated_record(object, refl, value[i.to_s]) }
|
638
|
-
end
|
639
|
-
object.send(field).target[0..-1] = items
|
640
|
-
else
|
641
|
-
ar_value = if refl
|
642
|
-
if refl.macro == :belongs_to
|
643
|
-
associated_record(object, refl, value)
|
644
|
-
else
|
645
|
-
raise HoboError, "association #{object.class}.#{refl.name} is not settable via parameters"
|
646
|
-
end
|
647
|
-
else
|
648
|
-
param_to_value(object.class.field_type(field), value)
|
649
|
-
end
|
650
|
-
object.send("#{field}=".to_sym, ar_value)
|
651
|
-
end
|
652
|
-
end
|
653
|
-
end
|
654
|
-
|
655
|
-
|
656
|
-
def XXassociated_record(owner, refl, value)
|
657
|
-
if value.is_a? String
|
658
|
-
if value.starts_with?('@')
|
659
|
-
Hobo.object_from_dom_id(value[1..-1])
|
660
|
-
elsif refl.klass.id_name?
|
661
|
-
refl.klass.find_by_id_name(value)
|
662
|
-
else
|
663
|
-
nil
|
664
|
-
end
|
665
|
-
else
|
666
|
-
if refl.macro == :belongs_to
|
667
|
-
new_from_params(refl.klass, value)
|
668
|
-
else
|
669
|
-
obj = owner.send(refl.name).new
|
670
|
-
initialize_from_params(obj, value)
|
671
|
-
obj
|
672
|
-
end
|
673
|
-
end
|
674
|
-
end
|
675
|
-
|
676
|
-
|
677
|
-
def XXobject_from_param(param)
|
678
|
-
Hobo.object_from_dom_id(param)
|
679
|
-
end
|
680
|
-
|
681
727
|
end
|
682
728
|
|
683
729
|
end
|