hobo 1.0.3 → 1.1.0.pre0

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.
Files changed (64) hide show
  1. data/CHANGES.txt +0 -66
  2. data/README +3 -0
  3. data/Rakefile +7 -7
  4. data/doctest/model.rdoctest +0 -2
  5. data/doctest/multi_model_forms.rdoctest +0 -2
  6. data/doctest/scopes.rdoctest +13 -21
  7. data/lib/active_record/association_collection.rb +7 -16
  8. data/lib/hobo.rb +10 -65
  9. data/lib/hobo/accessible_associations.rb +1 -5
  10. data/lib/hobo/authentication_support.rb +1 -1
  11. data/lib/hobo/controller.rb +5 -5
  12. data/lib/hobo/hobo_helper.rb +0 -84
  13. data/lib/hobo/lifecycles/lifecycle.rb +37 -31
  14. data/lib/hobo/lifecycles/transition.rb +1 -2
  15. data/lib/hobo/model.rb +13 -21
  16. data/lib/hobo/model_controller.rb +8 -8
  17. data/lib/hobo/rapid_helper.rb +12 -1
  18. data/lib/hobo/scopes/automatic_scopes.rb +26 -13
  19. data/lib/hobo/scopes/named_scope_extensions.rb +16 -28
  20. data/lib/hobo/user_controller.rb +1 -0
  21. data/lib/hobo/view_hints.rb +1 -5
  22. data/rails_generators/hobo/templates/initializer.rb +1 -1
  23. data/rails_generators/hobo_front_controller/templates/summary.dryml +4 -2
  24. data/rails_generators/hobo_model/hobo_model_generator.rb +12 -0
  25. data/rails_generators/hobo_model/templates/model.rb +9 -2
  26. data/rails_generators/hobo_rapid/templates/hobo-rapid.js +98 -23
  27. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +1 -1
  28. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +3 -1
  29. data/{dryml_generators → rapid_generators}/rapid/cards.dryml.erb +0 -0
  30. data/{dryml_generators → rapid_generators}/rapid/forms.dryml.erb +0 -0
  31. data/{dryml_generators → rapid_generators}/rapid/pages.dryml.erb +1 -1
  32. data/taglibs/rapid.dryml +2 -0
  33. data/taglibs/rapid_core.dryml +10 -9
  34. data/taglibs/rapid_forms.dryml +70 -35
  35. data/taglibs/rapid_lifecycles.dryml +17 -4
  36. data/taglibs/rapid_plus.dryml +3 -3
  37. data/taglibs/rapid_summary.dryml +11 -0
  38. data/taglibs/rapid_user_pages.dryml +39 -28
  39. data/tasks/hobo_tasks.rake +1 -1
  40. metadata +45 -61
  41. data/hobo.gemspec +0 -226
  42. data/lib/hobo/dryml.rb +0 -188
  43. data/lib/hobo/dryml/dryml_builder.rb +0 -140
  44. data/lib/hobo/dryml/dryml_doc.rb +0 -159
  45. data/lib/hobo/dryml/dryml_generator.rb +0 -263
  46. data/lib/hobo/dryml/dryml_support_controller.rb +0 -13
  47. data/lib/hobo/dryml/parser.rb +0 -3
  48. data/lib/hobo/dryml/parser/attribute.rb +0 -41
  49. data/lib/hobo/dryml/parser/base_parser.rb +0 -254
  50. data/lib/hobo/dryml/parser/document.rb +0 -57
  51. data/lib/hobo/dryml/parser/element.rb +0 -27
  52. data/lib/hobo/dryml/parser/elements.rb +0 -45
  53. data/lib/hobo/dryml/parser/source.rb +0 -58
  54. data/lib/hobo/dryml/parser/text.rb +0 -13
  55. data/lib/hobo/dryml/parser/tree_parser.rb +0 -67
  56. data/lib/hobo/dryml/part_context.rb +0 -137
  57. data/lib/hobo/dryml/scoped_variables.rb +0 -42
  58. data/lib/hobo/dryml/tag_parameters.rb +0 -36
  59. data/lib/hobo/dryml/taglib.rb +0 -123
  60. data/lib/hobo/dryml/template.rb +0 -1019
  61. data/lib/hobo/dryml/template_environment.rb +0 -613
  62. data/lib/hobo/dryml/template_handler.rb +0 -187
  63. data/lib/hobo/static_tags +0 -98
  64. data/taglibs/core.dryml +0 -104
@@ -157,70 +157,6 @@ module Hobo
157
157
  object.respond_to?(:typed_id) ? "model::#{typed_id(object, attribute).to_s.dasherize}" : ""
158
158
  end
159
159
 
160
-
161
- def context_map(enum = this)
162
- # TODO: Calls to respond_to? in here can cause the full collection hiding behind a scoped collection to get loaded
163
- res = []
164
- empty = true
165
- scope.new_scope(:repeat_collection => enum, :even_odd => 'odd', :repeat_item => nil) do
166
- if enum.respond_to?(:each_pair)
167
- enum.each_pair do |key, value|
168
- scope.repeat_item = value
169
- empty = false;
170
- self.this_key = key;
171
- new_object_context(value) { res << yield }
172
- scope.even_odd = scope.even_odd == "even" ? "odd" : "even"
173
- end
174
- else
175
- index = 0
176
- enum.each do |e|
177
- scope.repeat_item = e
178
- empty = false;
179
- if enum == this
180
- new_field_context(index, e) { res << yield }
181
- else
182
- new_object_context(e) { res << yield }
183
- end
184
- scope.even_odd = scope.even_odd == "even" ? "odd" : "even"
185
- index += 1
186
- end
187
- end
188
- Dryml.last_if = !empty
189
- end
190
- res
191
- end
192
-
193
-
194
- def first_item?
195
- if scope.repeat_collection.respond_to? :each_pair
196
- this == scope.repeat_collection.first.try.last
197
- else
198
- this == scope.repeat_collection.first
199
- end
200
- end
201
-
202
-
203
- def last_item?
204
- if scope.repeat_collection.respond_to? :each_pair
205
- this == scope.repeat_collection.last.try.last
206
- else
207
- this == scope.repeat_collection.last
208
- end
209
- end
210
-
211
-
212
- def comma_split(x)
213
- case x
214
- when nil
215
- []
216
- when String
217
- x.strip.split(/\s*,\s*/)
218
- else
219
- x.compact.map{|e| comma_split(e)}.flatten
220
- end
221
- end
222
-
223
-
224
160
  def can_create?(object=this)
225
161
  if object.is_a?(Class) and object < ActiveRecord::Base
226
162
  object = object.new
@@ -358,26 +294,6 @@ module Hobo
358
294
  s.to_s.gsub("\n", "<br/>") if s
359
295
  end
360
296
 
361
-
362
- def param_name_for(path)
363
- field_path = field_path.to_s.split(".") if field_path.is_one_of?(String, Symbol)
364
- attrs = path.rest.map{|part| "[#{part.to_s.sub /\?$/, ''}]"}.join
365
- "#{path.first}#{attrs}"
366
- end
367
-
368
-
369
- def param_name_for_this(foreign_key=false)
370
- return "" unless form_this
371
- name = if foreign_key && (refl = this_field_reflection) && refl.macro == :belongs_to
372
- param_name_for(path_for_form_field[0..-2] + [refl.primary_key_name])
373
- else
374
- param_name_for(path_for_form_field)
375
- end
376
- register_form_field(name)
377
- name
378
- end
379
-
380
-
381
297
  def transpose_with_field(field, collection=nil)
382
298
  collection ||= this
383
299
  matrix = collection.map {|obj| obj.send(field) }
@@ -24,36 +24,45 @@ module Hobo
24
24
 
25
25
  def self.def_state(name, on_enter)
26
26
  name = name.to_sym
27
- class_eval "def #{name}_state?; state_name == :#{name} end"
28
- states[name] = Lifecycles::State.new(name, on_enter)
27
+ returning(Lifecycles::State.new(name, on_enter)) do |s|
28
+ states[name] = s
29
+ class_eval "def #{name}_state?; state_name == :#{name} end"
30
+ end
29
31
  end
30
32
 
31
33
 
32
34
  def self.def_creator(name, on_create, options)
33
- class_eval %{
34
- def self.#{name}(user, attributes=nil)
35
- create(:#{name}, user, attributes)
36
- end
37
- def self.can_#{name}?(user, attributes=nil)
38
- can_create?(:#{name}, user)
39
- end
40
- }
41
- Creator.new(self, name.to_s, on_create, options)
35
+ name = name.to_sym
36
+ returning(Creator.new(self, name, on_create, options)) do |creator|
37
+
38
+ class_eval %{
39
+ def self.#{name}(user, attributes=nil)
40
+ create(:#{name}, user, attributes)
41
+ end
42
+ def self.can_#{name}?(user, attributes=nil)
43
+ can_create?(:#{name}, user)
44
+ end
45
+ }
46
+
47
+ end
42
48
  end
43
49
 
44
50
  def self.def_transition(name, start_state, end_states, on_transition, options)
45
- class_eval %{
46
- def #{name}!(user, attributes=nil)
47
- transition(:#{name}, user, attributes)
48
- end
49
- def can_#{name}?(user, attributes=nil)
50
- can_transition?(:#{name}, user)
51
- end
52
- def valid_for_#{name}?
53
- valid_for_transition?(:#{name})
54
- end
55
- }
56
- Transition.new(self, name.to_s, start_state, end_states, on_transition, options)
51
+ returning(Transition.new(self, name.to_s, start_state, end_states, on_transition, options)) do |t|
52
+
53
+ class_eval %{
54
+ def #{name}!(user, attributes=nil)
55
+ transition(:#{name}, user, attributes)
56
+ end
57
+ def can_#{name}?(user, attributes=nil)
58
+ can_transition?(:#{name}, user)
59
+ end
60
+ def valid_for_#{name}?
61
+ valid_for_transition?(:#{name})
62
+ end
63
+ }
64
+
65
+ end
57
66
  end
58
67
 
59
68
  def self.state_names
@@ -84,7 +93,7 @@ module Hobo
84
93
 
85
94
 
86
95
  def self.create(name, user, attributes=nil)
87
- creator = creators[name.to_sym]
96
+ creator = creators[name.to_sym] or raise LifecycleError, "No creator #{name} available"
88
97
  creator.run!(user, attributes)
89
98
  end
90
99
 
@@ -116,8 +125,8 @@ module Hobo
116
125
 
117
126
 
118
127
  def transition(name, user, attributes)
119
- transition = find_transition(name, user)
120
- transition.run!(record, user, attributes) unless transition.nil?
128
+ transition = find_transition(name, user) or raise LifecycleError, "No transition #{name} available"
129
+ transition.run!(record, user, attributes)
121
130
  end
122
131
 
123
132
 
@@ -168,6 +177,7 @@ module Hobo
168
177
  def become(state_name, validate=true)
169
178
  state_name = state_name.to_sym
170
179
  record.write_attribute self.class.state_field, state_name.to_s
180
+
171
181
  if state_name == :destroy
172
182
  record.destroy
173
183
  true
@@ -209,7 +219,7 @@ module Hobo
209
219
  timestamp = record.read_attribute(key_timestamp_field)
210
220
  if timestamp
211
221
  timestamp = timestamp.getutc
212
- Digest::SHA1.hexdigest("#{record.id}-#{state_name}-#{timestamp}-#{ActionController::Base.session_options[:secret]}")
222
+ Digest::SHA1.hexdigest("#{record.id}-#{state_name}-#{timestamp}")
213
223
  end
214
224
  end
215
225
 
@@ -222,10 +232,6 @@ module Hobo
222
232
  provided_key && provided_key == key && !key_expired?
223
233
  end
224
234
 
225
- def clear_key
226
- record.write_attribute key_timestamp_field, nil
227
- end
228
-
229
235
  def invariants_satisfied?
230
236
  self.class.invariants.all? { |i| record.instance_eval(&i) }
231
237
  end
@@ -34,8 +34,7 @@ module Hobo
34
34
 
35
35
 
36
36
  def change_state(record)
37
- record.lifecycle.clear_key unless options[:new_key] || options[:keep_key]
38
- return record.lifecycle.become(get_state(record, end_state))
37
+ record.lifecycle.become(get_state(record, end_state))
39
38
  end
40
39
 
41
40
 
@@ -34,17 +34,13 @@ module Hobo
34
34
 
35
35
  alias_method_chain :has_one, :new_method
36
36
 
37
- # eval avoids the ruby 1.9.2 "super from singleton method ..." error
38
- # see LH#840
39
- eval %(
40
- def inherited(klass)
41
- super
42
- fields(false) do
43
- Hobo.register_model(klass)
44
- field(klass.inheritance_column, :string)
45
- end
37
+ def inherited(klass)
38
+ super
39
+ fields(false) do
40
+ Hobo.register_model(klass)
41
+ field(klass.inheritance_column, :string)
46
42
  end
47
- )
43
+ end
48
44
  end
49
45
 
50
46
  base.fields(false) # force hobofields to load
@@ -61,11 +57,11 @@ module Hobo
61
57
 
62
58
  WillPaginate::Finder::ClassMethods.class_eval do
63
59
  def paginate_with_hobo_metadata(*args, &block)
64
- collection = paginate_without_hobo_metadata(*args, &block)
65
- collection.member_class = self
66
- collection.origin = try.proxy_owner
67
- collection.origin_attribute = try.proxy_reflection._?.name
68
- collection
60
+ returning paginate_without_hobo_metadata(*args, &block) do |collection|
61
+ collection.member_class = self
62
+ collection.origin = try.proxy_owner
63
+ collection.origin_attribute = try.proxy_reflection._?.name
64
+ end
69
65
  end
70
66
  alias_method_chain :paginate, :hobo_metadata
71
67
 
@@ -125,12 +121,8 @@ module Hobo
125
121
 
126
122
  ActiveRecord::Base.class_eval do
127
123
  def self.hobo_model
128
- if self.ancestors.include?(Hobo::Model)
129
- Rails.logger.error "#{self}: Do not call hobo_model in derived classes."
130
- else
131
- include Hobo::Model
132
- fields(false) # force hobofields to load
133
- end
124
+ include Hobo::Model
125
+ fields(false) # force hobofields to load
134
126
  end
135
127
  def self.hobo_user_model
136
128
  include Hobo::Model
@@ -521,7 +521,7 @@ module Hobo
521
521
  this.user_update_attributes(current_user, attributes)
522
522
  else
523
523
  self.this = new_for_create(attributes)
524
- this.user_save(current_user)
524
+ this.save
525
525
  end
526
526
  create_response(:new, options, &b)
527
527
  end
@@ -590,11 +590,10 @@ module Hobo
590
590
 
591
591
  self.this ||= args.first || find_instance
592
592
  changes = options[:attributes] || attribute_parameters or raise RuntimeError, ht(:"hobo.messages.update.no_attribute_error", :default=>["No update specified in params"])
593
-
594
- if this.user_update_attributes(current_user, changes)
595
- # Ensure current_user isn't out of date
596
- @current_user = @this if @this == current_user
597
- end
593
+ this.user_update_attributes(current_user, changes)
594
+
595
+ # Ensure current_user isn't out of date
596
+ @current_user = @this if @this == current_user
598
597
 
599
598
  in_place_edit_field = changes.keys.first if changes.size == 1 && params[:render]
600
599
  update_response(in_place_edit_field, options, &b)
@@ -755,7 +754,8 @@ module Hobo
755
754
  ordering = params["#{model.name.underscore}_ordering"]
756
755
  if ordering
757
756
  ordering.each_with_index do |id, position|
758
- model.find(id).user_update_attributes(current_user, :position => position+1)
757
+ object = model.find(id)
758
+ object.user_update_attributes!(current_user, object.position_column => position+1)
759
759
  end
760
760
  hobo_ajax_response || render(:nothing => true)
761
761
  else
@@ -772,7 +772,7 @@ module Hobo
772
772
  logger.info "Hobo: Permission Denied!"
773
773
  @permission_error = error
774
774
  if self.class.superclass.method_defined?("permission_denied")
775
- super
775
+ self.class.superclass.instance_method(:permission_denied).bind(self).call(error)
776
776
  else
777
777
  respond_to do |wants|
778
778
  wants.html do
@@ -1,5 +1,16 @@
1
1
  module Hobo::RapidHelper
2
2
 
3
+ def comma_split(x)
4
+ case x
5
+ when nil
6
+ []
7
+ when String
8
+ x.strip.split(/\s*,\s*/)
9
+ else
10
+ x.compact.map{|e| comma_split(e)}.flatten
11
+ end
12
+ end
13
+
3
14
  def rapid_build_callbacks(options)
4
15
  callbacks = {}
5
16
  options.each do |callback, code|
@@ -113,7 +124,7 @@ module Hobo::RapidHelper
113
124
 
114
125
  AJAX_CALLBACKS = [ :before, :success, :failure, :complete ]
115
126
 
116
- AJAX_ATTRS = AJAX_CALLBACKS + [ :type, :method,
127
+ AJAX_ATTRS = AJAX_CALLBACKS + [ :update, :type, :method,
117
128
  :script, :form, :params, :confirm, :message,
118
129
  :reset_form, :refocus_form, :result_update, :spinner_next_to ]
119
130
 
@@ -56,6 +56,19 @@ module Hobo
56
56
  { :conditions => [exists_sql, record] }
57
57
  end
58
58
 
59
+ # any_of_players(player1, player2)
60
+ elsif name =~ /^any_of_(.*)/ && (refl = reflection($1))
61
+
62
+ def_scope do |*records|
63
+ if records.empty?
64
+ { :conditions => exists_sql_condition(refl, true) }
65
+ else
66
+ records = records.flatten.compact.map {|r| find_if_named(refl, r) }
67
+ exists_sql = ([exists_sql_condition(refl)] * records.length).join(" OR ")
68
+ { :conditions => [exists_sql] + records }
69
+ end
70
+ end
71
+
59
72
  # without_players(player1, player2)
60
73
  elsif name =~ /^without_(.*)/ && (refl = reflection($1))
61
74
 
@@ -255,7 +268,7 @@ module Hobo
255
268
  type = klass.attr_type(field)
256
269
  if type.nil? #a virtual attribute from an SQL alias, e.g., 'total' from 'COUNT(*) AS total'
257
270
  colspec = "#{field}" # don't prepend the table name
258
- elsif type.respond_to?(:name_attribute) && (name = type.name_attribute)
271
+ elsif type.respond_to?(:table_name) && (name = type.name_attribute)
259
272
  include = field
260
273
  colspec = "#{type.table_name}.#{name}"
261
274
  else
@@ -270,17 +283,14 @@ module Hobo
270
283
  { :include => inclusions }
271
284
  end
272
285
 
273
- when "includes"
274
- def_scope do |inclusions|
275
- { :include => inclusions }
276
- end
277
-
278
286
  when "search"
279
287
  def_scope do |query, *fields|
288
+ match_keyword = ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
289
+
280
290
  words = query.split
281
291
  args = []
282
292
  word_queries = words.map do |word|
283
- field_query = '(' + fields.map { |field| "(#{@klass.table_name}.#{field} like ?)" }.join(" OR ") + ')'
293
+ field_query = '(' + fields.map { |field| "(#{@klass.table_name+'.' unless field.to_s.index('.')}#{field} #{match_keyword} ?)" }.join(" OR ") + ')'
284
294
  args += ["%#{word}%"] * fields.length
285
295
  field_query
286
296
  end
@@ -305,32 +315,35 @@ module Hobo
305
315
  def exists_sql_condition(reflection, any=false)
306
316
  owner = @klass
307
317
  owner_primary_key = "#{owner.table_name}.#{owner.primary_key}"
318
+
308
319
  if reflection.options[:through]
309
320
  join_table = reflection.through_reflection.klass.table_name
310
321
  owner_fkey = reflection.through_reflection.primary_key_name
322
+ conditions = reflection.options[:conditions].blank? ? '' : " AND #{reflection.through_reflection.klass.send(:sanitize_sql_for_conditions, reflection.options[:conditions])}"
323
+
311
324
  if any
312
- "EXISTS (SELECT * FROM #{join_table} WHERE #{join_table}.#{owner_fkey} = #{owner_primary_key})"
325
+ "EXISTS (SELECT * FROM #{join_table} WHERE #{join_table}.#{owner_fkey} = #{owner_primary_key}#{conditions})"
313
326
  else
314
327
  source_fkey = reflection.source_reflection.primary_key_name
315
328
  "EXISTS (SELECT * FROM #{join_table} " +
316
- "WHERE #{join_table}.#{source_fkey} = ? AND #{join_table}.#{owner_fkey} = #{owner_primary_key})"
329
+ "WHERE #{join_table}.#{source_fkey} = ? AND #{join_table}.#{owner_fkey} = #{owner_primary_key}#{conditions})"
317
330
  end
318
331
  else
319
332
  foreign_key = reflection.primary_key_name
320
333
  related = reflection.klass
321
-
334
+ conditions = reflection.options[:conditions].blank? ? '' : " AND #{reflection.klass.send(:sanitize_sql_for_conditions, reflection.options[:conditions])}"
335
+
322
336
  if any
323
337
  "EXISTS (SELECT * FROM #{related.table_name} " +
324
- "WHERE #{related.table_name}.#{foreign_key} = #{owner_primary_key})"
338
+ "WHERE #{related.table_name}.#{foreign_key} = #{owner_primary_key}#{conditions})"
325
339
  else
326
340
  "EXISTS (SELECT * FROM #{related.table_name} " +
327
341
  "WHERE #{related.table_name}.#{foreign_key} = #{owner_primary_key} AND " +
328
- "#{related.table_name}.#{related.primary_key} = ?)"
342
+ "#{related.table_name}.#{related.primary_key} = ?#{conditions})"
329
343
  end
330
344
  end
331
345
  end
332
346
 
333
-
334
347
  def find_if_named(reflection, string_or_record)
335
348
  if string_or_record.is_a?(String)
336
349
  name = string_or_record
@@ -1,39 +1,27 @@
1
1
  module ActiveRecord
2
2
  module NamedScope
3
-
4
- class Scope
5
- delegate :member_class, :to => :proxy_found
6
- include Hobo::Scopes::ApplyScopes
7
- end
3
+ class Scope
8
4
 
9
- module ClassMethods
10
- def scopes
11
- hash = read_inheritable_attribute(:scopes)
12
- if hash.nil?
13
- if respond_to?(:create_automatic_scope)
14
- write_inheritable_attribute(:scopes, new_automatic_scoping_hash(self))
15
- else
16
- # add a default_proc to optimize the next condition
17
- write_inheritable_attribute(:scopes, Hash.new { |hash, key| nil })
18
- end
19
- elsif hash.default_proc.nil? && respond_to?(:create_automatic_scope)
20
- write_inheritable_attribute(:scopes, new_automatic_scoping_hash(self).merge!(hash))
21
- else
22
- hash
23
- end
24
- end
5
+ delegate :member_class, :to => :proxy_found
25
6
 
26
- private
7
+ include Hobo::Scopes::ApplyScopes
27
8
 
28
- def new_automatic_scoping_hash(o)
29
- hash = Hash.new { |hash, key| o.create_automatic_scope(key) && hash[key] }
30
- hash.meta_eval do
31
- define_method :include? do |key, *args|
32
- super(key, *args) || o.create_automatic_scope(key)
9
+ def respond_to?(method, include_private=false)
10
+ super || scopes.include?(method) || proxy_scope.respond_to?(method, include_private)
11
+ end
12
+
13
+ private
14
+
15
+ def method_missing(method, *args, &block)
16
+ if respond_to?(method) && scopes.include?(method)
17
+ scopes[method].call(self, *args)
18
+ else
19
+ with_scope :find => proxy_options do
20
+ proxy_scope.send(method, *args, &block)
33
21
  end
34
22
  end
35
- hash
36
23
  end
24
+
37
25
  end
38
26
  end
39
27
  end