hobo 1.0.3 → 1.1.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
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