hobo 0.7.3 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/bin/hobo +1 -1
  2. data/hobo_files/plugin/CHANGES.txt +302 -0
  3. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +2 -9
  4. data/hobo_files/plugin/generators/hobo_model/templates/model.rb +1 -1
  5. data/hobo_files/plugin/generators/hobo_model_resource/hobo_model_resource_generator.rb +0 -2
  6. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.js +76 -46
  7. data/hobo_files/plugin/generators/hobo_rapid/templates/lowpro.js +25 -18
  8. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css +29 -11
  9. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +2 -2
  10. data/hobo_files/plugin/init.rb +0 -1
  11. data/hobo_files/plugin/lib/active_record/has_many_association.rb +3 -0
  12. data/hobo_files/plugin/lib/hobo.rb +12 -8
  13. data/hobo_files/plugin/lib/hobo/bundle.rb +1 -1
  14. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +1 -1
  15. data/hobo_files/plugin/lib/hobo/dryml/parser/attribute.rb +41 -0
  16. data/hobo_files/plugin/lib/hobo/dryml/parser/base_parser.rb +253 -0
  17. data/hobo_files/plugin/lib/hobo/dryml/parser/document.rb +26 -0
  18. data/hobo_files/plugin/lib/hobo/dryml/parser/element.rb +27 -0
  19. data/hobo_files/plugin/lib/hobo/dryml/parser/elements.rb +45 -0
  20. data/hobo_files/plugin/lib/hobo/dryml/parser/source.rb +58 -0
  21. data/hobo_files/plugin/lib/hobo/dryml/parser/text.rb +13 -0
  22. data/hobo_files/plugin/lib/hobo/dryml/parser/tree_parser.rb +67 -0
  23. data/hobo_files/plugin/lib/hobo/dryml/scoped_variables.rb +10 -5
  24. data/hobo_files/plugin/lib/hobo/dryml/template.rb +48 -27
  25. data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +28 -13
  26. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +3 -1
  27. data/hobo_files/plugin/lib/hobo/model.rb +70 -10
  28. data/hobo_files/plugin/lib/hobo/model_controller.rb +49 -34
  29. data/hobo_files/plugin/lib/hobo/model_router.rb +10 -2
  30. data/hobo_files/plugin/lib/hobo/rapid_helper.rb +1 -0
  31. data/hobo_files/plugin/lib/hobo/scopes.rb +15 -0
  32. data/hobo_files/plugin/lib/hobo/scopes/apply_scopes.rb +23 -0
  33. data/hobo_files/plugin/lib/hobo/scopes/association_proxy_extensions.rb +4 -2
  34. data/hobo_files/plugin/lib/hobo/scopes/automatic_scopes.rb +34 -7
  35. data/hobo_files/plugin/lib/hobo/scopes/defined_scope_proxy_extender.rb +3 -1
  36. data/hobo_files/plugin/lib/hobo/scopes/scoped_proxy.rb +1 -5
  37. data/hobo_files/plugin/taglibs/rapid.dryml +33 -24
  38. data/hobo_files/plugin/taglibs/rapid_editing.dryml +6 -5
  39. data/hobo_files/plugin/taglibs/rapid_forms.dryml +37 -31
  40. data/hobo_files/plugin/taglibs/rapid_generics.dryml +68 -27
  41. data/hobo_files/plugin/taglibs/rapid_navigation.dryml +5 -8
  42. data/hobo_files/plugin/taglibs/rapid_pages.dryml +71 -47
  43. data/hobo_files/plugin/taglibs/rapid_plus.dryml +4 -5
  44. data/hobo_files/plugin/taglibs/rapid_support.dryml +11 -4
  45. metadata +23 -6
  46. data/hobo_files/plugin/lib/rexml.rb +0 -443
@@ -130,6 +130,8 @@ module Hobo
130
130
  empty = true
131
131
  if this.respond_to?(:each_index)
132
132
  this.each_index {|i| empty = false; new_field_context(i) { res << yield } }
133
+ elsif this.is_a?(Hash)
134
+ this.map {|key, value| empty = false; self.this_key = key; new_object_context(value) { res << yield } }
133
135
  else
134
136
  this.map {|e| empty = false; new_object_context(e) { res << yield } }
135
137
  end
@@ -346,7 +348,7 @@ module Hobo
346
348
 
347
349
  Hobo::ModelRouter.linkable?(klass, action, options.reverse_merge(:subsite => subsite))
348
350
  end
349
-
351
+
350
352
 
351
353
  # Convenience helper for the default app
352
354
 
@@ -26,11 +26,13 @@ module Hobo
26
26
 
27
27
  class << base
28
28
  alias_method_chain :has_many, :defined_scopes
29
+ alias_method_chain :has_many, :join_record_management
29
30
  alias_method_chain :belongs_to, :creator_metadata
30
31
 
31
32
  alias_method_chain :has_one, :new_method
32
33
 
33
34
  def inherited(klass)
35
+ super
34
36
  fields do
35
37
  Hobo.register_model(klass)
36
38
  field(klass.inheritance_column, :string)
@@ -92,7 +94,7 @@ module Hobo
92
94
 
93
95
  def user_find(user, *args)
94
96
  record = find(*args)
95
- raise PermissionDeniedError unless Hobo.can_view?(user, self)
97
+ raise PermissionDeniedError unless Hobo.can_view?(user, record)
96
98
  record
97
99
  end
98
100
 
@@ -121,6 +123,11 @@ module Hobo
121
123
  end
122
124
 
123
125
 
126
+ def user_update(user, id, attributes={})
127
+ find(id).user_save_changes(user, attributes)
128
+ end
129
+
130
+
124
131
  def name_attribute
125
132
  @name_attribute ||= begin
126
133
  cols = columns.*.name
@@ -232,7 +239,7 @@ module Hobo
232
239
  # FIXME: This should really be a method on AssociationReflection
233
240
  def reverse_reflection(association_name)
234
241
  refl = reflections[association_name]
235
- return nil if refl.options[:conditions]
242
+ return nil if refl.options[:conditions] || refl.options[:polymorphic]
236
243
 
237
244
  reverse_macro = if refl.macro == :has_many
238
245
  :belongs_to
@@ -277,9 +284,36 @@ module Hobo
277
284
  "#{name.underscore.pluralize}"
278
285
  end
279
286
 
287
+
280
288
  def typed_id
281
289
  HoboFields.to_name(self) || name.underscore.gsub("/", "__")
282
290
  end
291
+
292
+
293
+ def manage_join_records(association)
294
+ through = reflections[association].through_reflection
295
+ source = reflections[association].source_reflection
296
+
297
+ method = "manage_join_records_for_#{association}"
298
+ after_save method
299
+ class_eval %{
300
+ def #{method}
301
+ current = #{through.name}.*.#{source.name}
302
+ to_delete = current - #{association}
303
+ to_add = #{association} - current
304
+ #{through.klass.name}.delete_all(["#{through.primary_key_name} = ? and #{source.primary_key_name} in (?)",
305
+ self.id, to_delete.*.id]) if to_delete.any?
306
+ to_add.each { |record| #{association} << record }
307
+ end
308
+ }
309
+ end
310
+
311
+ def has_many_with_join_record_management(name, options={}, &b)
312
+ manage = options.delete(:managed)
313
+ returning (has_many_without_join_record_management(name, options, &b)) do
314
+ manage_join_records(name) if manage
315
+ end
316
+ end
283
317
 
284
318
  end # --- of ClassMethods --- #
285
319
 
@@ -441,16 +475,12 @@ module Hobo
441
475
 
442
476
 
443
477
  def convert_type_for_mass_assignment(field_type, value)
444
- if field_type.is_a?(ActiveRecord::Reflection::AssociationReflection) &&
445
- field_type.macro.in?([:belongs_to, :has_one])
446
- if value.is_a?(String) && value.starts_with?('@')
447
- # TODO: This @foo_1 feature is rarely (never?) used - get rid of it
448
- Hobo.object_from_dom_id(value[1..-1])
449
- else
450
- value
451
- end
478
+ if field_type.is_a?(ActiveRecord::Reflection::AssociationReflection)
479
+ convert_associated_records_for_mass_assignment(field_type, value)
480
+
452
481
  elsif !field_type.is_a?(Class)
453
482
  value
483
+
454
484
  elsif field_type <= Date
455
485
  if value.is_a? Hash
456
486
  Date.new(*(%w{year month day}.map{|s| value[s].to_i}))
@@ -460,6 +490,7 @@ module Hobo
460
490
  else
461
491
  value
462
492
  end
493
+
463
494
  elsif field_type <= Time
464
495
  if value.is_a? Hash
465
496
  Time.local(*(%w{year month day hour minute}.map{|s| value[s].to_i}))
@@ -468,13 +499,42 @@ module Hobo
468
499
  else
469
500
  value
470
501
  end
502
+
471
503
  elsif field_type <= Hobo::Boolean
472
504
  (value.is_a?(String) && value.strip.downcase.in?(['0', 'false']) || value.blank?) ? false : true
505
+
473
506
  else
474
507
  # primitive field
475
508
  value
476
509
  end
477
510
  end
511
+
512
+ def convert_associated_records_for_mass_assignment(reflection, value)
513
+ if reflection.macro.in?([:belongs_to, :has_one])
514
+ if value.is_a?(String) && value.starts_with?('@')
515
+ # TODO: This @foo_1 feature is rarely (never?) used - get rid of it
516
+ Hobo.object_from_dom_id(value[1..-1])
517
+ else
518
+ value
519
+ end
520
+ elsif reflection.macro == :has_many
521
+ if reflection.klass.try.name_attribute
522
+ value.map do |x|
523
+ if x.is_a?(String)
524
+ reflection.klass[x] unless x.blank?
525
+ else
526
+ x
527
+ end
528
+ end.compact
529
+ else
530
+ value
531
+ end
532
+
533
+ else
534
+ # unknown kind of accociation - no conversion
535
+ value
536
+ end
537
+ end
478
538
 
479
539
  end
480
540
 
@@ -138,9 +138,11 @@ module Hobo
138
138
  def destroy; hobo_destroy end if include_action?(:destroy)
139
139
 
140
140
  def completions; hobo_completions end if include_action?(:completions)
141
+
142
+ def reorder; hobo_reorder end if include_action?(:reorder)
141
143
  end
142
144
 
143
- collections.each { |c| def_collection_actions(c.to_sym) }
145
+ collections.each { |c| def_collection_actions(c.to_sym) }
144
146
  end
145
147
 
146
148
 
@@ -205,7 +207,10 @@ module Hobo
205
207
 
206
208
 
207
209
  def available_auto_actions
208
- READ_ONLY_ACTIONS + WRITE_ONLY_ACTIONS + FORM_ACTIONS + available_auto_collection_actions
210
+ (available_auto_read_actions +
211
+ available_auto_write_actions +
212
+ FORM_ACTIONS +
213
+ available_auto_collection_actions).uniq
209
214
  end
210
215
 
211
216
 
@@ -215,7 +220,11 @@ module Hobo
215
220
 
216
221
 
217
222
  def available_auto_write_actions
218
- WRITE_ONLY_ACTIONS
223
+ if "position_column".in?(model.instance_methods)
224
+ WRITE_ONLY_ACTIONS + [:reorder]
225
+ else
226
+ WRITE_ONLY_ACTIONS
227
+ end
219
228
  end
220
229
 
221
230
 
@@ -229,38 +238,17 @@ module Hobo
229
238
  protected
230
239
 
231
240
 
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
239
- end
240
- finder
241
- end
242
-
243
-
244
- def sort_fields(*args)
245
- finder = args.first.is_a?(Class) ? args.shift : model
246
-
241
+ def parse_sort_param(*sort_fields)
247
242
  _, desc, field = *params[:sort]._?.match(/^(-)?([a-z_]+(?:\.[a-z_]+)?)$/)
248
243
 
249
244
  if field
250
- fields = args.*.to_s
251
- if field.in?(fields)
245
+ if field.in?(sort_fields.*.to_s)
252
246
  @sort_field = field
253
247
  @sort_direction = desc ? "desc" : "asc"
254
248
 
255
- table, column = if field =~ /^(.*)\.(.*)$/
256
- [$1.camelize.constantize.table_name, $2]
257
- else
258
- [finder.table_name, field]
259
- end
260
- finder = finder.order("#{table}.#{column}", @sort_direction)
249
+ [@sort_field, @sort_direction]
261
250
  end
262
251
  end
263
- finder
264
252
  end
265
253
 
266
254
 
@@ -301,7 +289,7 @@ module Hobo
301
289
  object_url(@this) ||
302
290
 
303
291
  # Then the show page of the 'owning' object if there is one
304
- (@this.class.default_dependent_on && object_url(@this.class.default_dependent_on)) ||
292
+ (@this.class.default_dependent_on && object_url(@this.send(@this.class.default_dependent_on))) ||
305
293
 
306
294
  # Last try - the index page for this model
307
295
  object_url(@this.class) ||
@@ -322,8 +310,18 @@ module Hobo
322
310
  end
323
311
  end
324
312
 
313
+
314
+ def request_requires_pagination?
315
+ # Internet explorer has a penchant for saying it would mostly
316
+ # like an image, if you clicked on an image link
317
+ request.format.in?(PAGINATE_FORMATS) || request.format.to_s =~ %r(image/)
318
+ end
319
+
320
+
325
321
  def find_or_paginate(finder, options)
326
- do_pagination = options.delete(:paginate) != false && request.format.in?(PAGINATE_FORMATS)
322
+ options = options.reverse_merge(:paginate => request_requires_pagination?)
323
+ do_pagination = options.delete(:paginate)
324
+
327
325
  if do_pagination && !finder.respond_to?(:paginate)
328
326
  do_pagination = false
329
327
  logger.warn "Hobo::ModelController: Pagination is not available. To enable, please install will_paginate or a duck-type compatible paginator"
@@ -419,7 +417,7 @@ module Hobo
419
417
 
420
418
 
421
419
  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?
420
+ flash[:notice] = "Changes to the #{@this.class.name.titleize.downcase} were saved" if !request.xhr? && valid?
423
421
 
424
422
  response_block(&b) or
425
423
  if valid?
@@ -464,7 +462,7 @@ module Hobo
464
462
  response_block(&b) or
465
463
  respond_to do |wants|
466
464
  wants.html { redirect_to(:action => "index") }
467
- wants.js { hobo_ajax_response || render(:text => "") }
465
+ wants.js { hobo_ajax_response || render(:nothing => true) }
468
466
  end
469
467
  end
470
468
 
@@ -498,6 +496,14 @@ module Hobo
498
496
  end
499
497
 
500
498
 
499
+ def hobo_reorder
500
+ params["#{model.name.underscore}_ordering"].each_with_index do |id, position|
501
+ model.user_update(current_user, id, :position => position+1)
502
+ end
503
+ hobo_ajax_response || render(:nothing => true)
504
+ end
505
+
506
+
501
507
 
502
508
  # --- Response helpers --- #
503
509
 
@@ -505,10 +511,19 @@ module Hobo
505
511
  def permission_denied(error)
506
512
  if respond_to? :permission_denied_response
507
513
  permission_denied_response
508
- elsif render_tag("permission-denied-page", { }, :status => 403)
509
- # job done
510
514
  else
511
- render :text => "Permission Denied", :status => 403
515
+ respond_to do |wants|
516
+ wants.html do
517
+ if render_tag("permission-denied-page", { }, :status => 403)
518
+ # job done
519
+ else
520
+ render :text => "Permission Denied", :status => 403
521
+ end
522
+ end
523
+ wants.js do
524
+ render :text => "Permission Denied", :status => 403
525
+ end
526
+ end
512
527
  end
513
528
  end
514
529
 
@@ -10,8 +10,10 @@ class ActionController::Routing::RouteSet
10
10
 
11
11
  # temporay hack -- reload assemble.rb whenever routes need reloading
12
12
  def reload_with_hobo_assemble
13
- load "#{RAILS_ROOT}/app/assemble.rb" if File.exists? "#{RAILS_ROOT}/app/assemble.rb"
14
- reload_without_hobo_assemble
13
+ if defined? ::ApplicationController
14
+ load "#{RAILS_ROOT}/app/assemble.rb" if File.exists? "#{RAILS_ROOT}/app/assemble.rb"
15
+ reload_without_hobo_assemble
16
+ end
15
17
  end
16
18
  alias_method_chain :reload, :hobo_assemble
17
19
 
@@ -131,6 +133,7 @@ module Hobo
131
133
  collection_routes
132
134
  web_method_routes
133
135
  show_action_routes
136
+ reorder_route
134
137
  user_routes if controller < Hobo::UserController
135
138
  end
136
139
  end
@@ -192,6 +195,11 @@ module Hobo
192
195
  end
193
196
  end
194
197
 
198
+
199
+ def reorder_route
200
+ linkable_route("reorder_#{plural}", "#{plural}/reorder", 'reorder', :conditions => { :method => :post })
201
+ end
202
+
195
203
 
196
204
  def user_routes
197
205
  prefix = plural == "users" ? "" : "#{singular}_"
@@ -143,4 +143,5 @@ module Hobo::RapidHelper
143
143
 
144
144
  names - through_collection_names
145
145
  end
146
+
146
147
  end
@@ -13,6 +13,8 @@ module Hobo
13
13
  module ClassMethods
14
14
 
15
15
  include AutomaticScopes
16
+
17
+ include ApplyScopes
16
18
 
17
19
  def defined_scopes
18
20
  @defined_scopes
@@ -29,6 +31,19 @@ module Hobo
29
31
  end
30
32
 
31
33
 
34
+ def apply_scopes(scopes)
35
+ result = self
36
+ scopes.each_pair do |scope, arg|
37
+ if arg.is_a?(Array)
38
+ result = result.send(scope, *arg) unless arg.first.blank?
39
+ else
40
+ result = result.send(scope, arg) unless arg.blank?
41
+ end
42
+ end
43
+ result
44
+ end
45
+
46
+
32
47
  def alias_scope(new_name, old_name)
33
48
  metaclass.send(:alias_method, new_name, old_name)
34
49
  defined_scopes[new_name] = defined_scopes[old_name]
@@ -0,0 +1,23 @@
1
+ module Hobo
2
+
3
+ module Scopes
4
+
5
+ module ApplyScopes
6
+
7
+ def apply_scopes(scopes)
8
+ result = self
9
+ scopes.each_pair do |scope, arg|
10
+ if arg.is_a?(Array)
11
+ result = result.send(scope, *arg) unless arg.first.blank?
12
+ else
13
+ result = result.send(scope, arg) unless arg.blank?
14
+ end
15
+ end
16
+ result
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -18,9 +18,11 @@ module Hobo
18
18
  target_class.send(scope_name).scope(:find)[:conditions]
19
19
  end
20
20
  if scope_conditions && conditions_without_hobo_scopes
21
- "#{conditions_without_hobo_scopes} AND #{scope_conditions}"
21
+ "(#{sanitize_sql conditions_without_hobo_scopes}) AND (#{sanitize_sql scope_conditions})"
22
+ elsif scope_conditions
23
+ sanitize_sql scope_conditions
22
24
  else
23
- scope_conditions || conditions_without_hobo_scopes
25
+ conditions_without_hobo_scopes
24
26
  end
25
27
  end
26
28
 
@@ -152,17 +152,17 @@ module Hobo
152
152
  end
153
153
 
154
154
  # published
155
- elsif (col = column($1)) && (col.type == :boolean)
155
+ elsif (col = column(name)) && (col.type == :boolean)
156
156
 
157
157
  def_scope do
158
- { :conditions => "#{column_sql(col)} = 1" }
158
+ { :conditions => "#{column_sql(col)}" }
159
159
  end
160
160
 
161
161
  # not_published
162
- elsif (col = column($1)) && (col.type == :boolean)
162
+ elsif name =~ /^not_(.*)$/ && (col = column($1)) && (col.type == :boolean)
163
163
 
164
164
  def_scope do
165
- { :conditions => "#{column_sql(col)} <> 1" }
165
+ { :conditions => "NOT #{column_sql(col)}" }
166
166
  end
167
167
 
168
168
  # published_before(time)
@@ -200,11 +200,38 @@ module Hobo
200
200
  def_scope do |count|
201
201
  { :limit => count }
202
202
  end
203
-
203
+
204
204
  when "order_by"
205
+ klass = @klass
205
206
  def_scope do |*args|
206
207
  field, asc = args
207
- { :order => "#{field} #{asc._?.upcase}" }
208
+ type = klass.attr_type(field)
209
+ if type.respond_to?(:table_name) && (name = type.name_attribute)
210
+ include = field
211
+ colspec = "#{type.table_name}.#{name}"
212
+ else
213
+ colspec = "#{klass.table_name}.#{field}"
214
+ end
215
+ { :order => "#{colspec} #{asc._?.upcase}", :include => include }
216
+ end
217
+
218
+
219
+ when "include"
220
+ def_scope do |inclusions|
221
+ { :include => inclusions }
222
+ end
223
+
224
+ when "search"
225
+ def_scope do |query, *fields|
226
+ words = query.split
227
+ args = []
228
+ word_queries = words.map do |word|
229
+ field_query = '(' + fields.map { |field| "(#{@klass.table_name}.#{field} like ?)" }.join(" OR ") + ')'
230
+ args += ["%#{word}%"] * fields.length
231
+ field_query
232
+ end
233
+
234
+ { :conditions => [word_queries.join(" OR ")] + args }
208
235
  end
209
236
 
210
237
  else
@@ -236,7 +263,7 @@ module Hobo
236
263
 
237
264
  "EXISTS (SELECT * FROM #{related.table_name} " +
238
265
  "WHERE #{related.table_name}.#{foreign_key} = #{owner_primary_key} AND " +
239
- "#{related.table_name}.#{related.primary_key} = ?"
266
+ "#{related.table_name}.#{related.primary_key} = ?)"
240
267
  end
241
268
  end
242
269