hobo 0.7.5 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. data/{hobo_files/plugin/CHANGES.txt → CHANGES.txt} +391 -0
  2. data/Manifest +146 -0
  3. data/{hobo_files/plugin/README → README} +0 -0
  4. data/bin/hobo +13 -26
  5. data/dryml_generators/rapid/cards.dryml.erb +55 -0
  6. data/dryml_generators/rapid/forms.dryml.erb +43 -0
  7. data/dryml_generators/rapid/pages.dryml.erb +284 -0
  8. data/hobo.gemspec +168 -0
  9. data/init.rb +9 -0
  10. data/lib/action_view_extensions/helpers/tag_helper.rb +7 -0
  11. data/lib/active_record/association_collection.rb +54 -0
  12. data/{hobo_files/plugin/lib → lib}/active_record/association_proxy.rb +12 -4
  13. data/{hobo_files/plugin/lib → lib}/active_record/association_reflection.rb +7 -1
  14. data/{hobo_files/plugin/lib → lib}/extensions/test_case.rb +17 -17
  15. data/{hobo_files/plugin/lib → lib}/hobo.rb +193 -100
  16. data/{hobo_files/plugin/lib → lib}/hobo/authentication_support.rb +8 -8
  17. data/{hobo_files/plugin/lib → lib}/hobo/bundle.rb +90 -89
  18. data/{hobo_files/plugin/lib → lib}/hobo/composite_model.rb +21 -21
  19. data/{hobo_files/plugin/lib → lib}/hobo/controller.rb +38 -25
  20. data/{hobo_files/plugin/lib → lib}/hobo/dev_controller.rb +10 -6
  21. data/lib/hobo/dryml.rb +167 -0
  22. data/{hobo_files/plugin/lib → lib}/hobo/dryml/dryml_builder.rb +28 -25
  23. data/lib/hobo/dryml/dryml_generator.rb +210 -0
  24. data/{hobo_files/plugin/lib → lib}/hobo/dryml/dryml_support_controller.rb +1 -1
  25. data/lib/hobo/dryml/parser.rb +3 -0
  26. data/{hobo_files/plugin/lib → lib}/hobo/dryml/parser/attribute.rb +6 -6
  27. data/{hobo_files/plugin/lib → lib}/hobo/dryml/parser/base_parser.rb +16 -16
  28. data/lib/hobo/dryml/parser/document.rb +57 -0
  29. data/{hobo_files/plugin/lib → lib}/hobo/dryml/parser/element.rb +7 -7
  30. data/{hobo_files/plugin/lib → lib}/hobo/dryml/parser/elements.rb +9 -9
  31. data/{hobo_files/plugin/lib → lib}/hobo/dryml/parser/source.rb +3 -3
  32. data/{hobo_files/plugin/lib → lib}/hobo/dryml/parser/text.rb +3 -3
  33. data/{hobo_files/plugin/lib → lib}/hobo/dryml/parser/tree_parser.rb +3 -3
  34. data/{hobo_files/plugin/lib → lib}/hobo/dryml/part_context.rb +26 -26
  35. data/{hobo_files/plugin/lib → lib}/hobo/dryml/scoped_variables.rb +15 -15
  36. data/{hobo_files/plugin/lib → lib}/hobo/dryml/tag_parameters.rb +10 -10
  37. data/{hobo_files/plugin/lib → lib}/hobo/dryml/taglib.rb +43 -37
  38. data/{hobo_files/plugin/lib → lib}/hobo/dryml/template.rb +290 -208
  39. data/{hobo_files/plugin/lib → lib}/hobo/dryml/template_environment.rb +173 -115
  40. data/{hobo_files/plugin/lib → lib}/hobo/dryml/template_handler.rb +19 -24
  41. data/lib/hobo/find_for.rb +95 -0
  42. data/{hobo_files/plugin/lib → lib}/hobo/generator.rb +2 -1
  43. data/{hobo_files/plugin/lib → lib}/hobo/guest.rb +12 -4
  44. data/{hobo_files/plugin/lib → lib}/hobo/hobo_helper.rb +146 -117
  45. data/lib/hobo/include_in_save.rb +43 -0
  46. data/lib/hobo/lifecycles.rb +94 -0
  47. data/lib/hobo/lifecycles/actions.rb +73 -0
  48. data/lib/hobo/lifecycles/creator.rb +76 -0
  49. data/lib/hobo/lifecycles/lifecycle.rb +205 -0
  50. data/lib/hobo/lifecycles/state.rb +23 -0
  51. data/lib/hobo/lifecycles/transition.rb +66 -0
  52. data/{hobo_files/plugin/lib → lib}/hobo/model.rb +306 -217
  53. data/{hobo_files/plugin/lib → lib}/hobo/model_controller.rb +342 -213
  54. data/{hobo_files/plugin/lib → lib}/hobo/model_router.rb +151 -120
  55. data/{hobo_files/plugin/lib → lib}/hobo/model_support.rb +9 -9
  56. data/{hobo_files/plugin/lib → lib}/hobo/rapid_helper.rb +30 -23
  57. data/{hobo_files/plugin/lib → lib}/hobo/scopes.rb +22 -68
  58. data/{hobo_files/plugin/lib → lib}/hobo/scopes/apply_scopes.rb +5 -5
  59. data/lib/hobo/scopes/association_proxy_extensions.rb +47 -0
  60. data/{hobo_files/plugin/lib → lib}/hobo/scopes/automatic_scopes.rb +104 -79
  61. data/lib/hobo/scopes/named_scope_extensions.rb +27 -0
  62. data/{hobo_files/plugin/lib → lib}/hobo/static_tags +1 -11
  63. data/{hobo_files/plugin/lib → lib}/hobo/undefined.rb +1 -1
  64. data/{hobo_files/plugin/lib → lib}/hobo/undefined_access_error.rb +0 -0
  65. data/{hobo_files/plugin/lib → lib}/hobo/user.rb +27 -25
  66. data/{hobo_files/plugin/lib → lib}/hobo/user_controller.rb +80 -34
  67. data/{hobo_files/plugin/generators → rails_generators}/hobo/hobo_generator.rb +7 -7
  68. data/rails_generators/hobo/templates/application.css +0 -0
  69. data/{hobo_files/plugin/generators → rails_generators}/hobo/templates/application.dryml +0 -2
  70. data/{hobo_files/plugin/generators → rails_generators}/hobo/templates/dryml-support.js +0 -0
  71. data/{hobo_files/plugin/generators → rails_generators}/hobo/templates/guest.rb +0 -0
  72. data/rails_generators/hobo/templates/initializer.rb +9 -0
  73. data/{hobo_files/plugin/generators → rails_generators}/hobo_front_controller/USAGE +0 -0
  74. data/{hobo_files/plugin/generators → rails_generators}/hobo_front_controller/hobo_front_controller_generator.rb +1 -3
  75. data/{hobo_files/plugin/generators → rails_generators}/hobo_front_controller/templates/controller.rb +1 -1
  76. data/{hobo_files/plugin/generators → rails_generators}/hobo_front_controller/templates/functional_test.rb +0 -0
  77. data/{hobo_files/plugin/generators → rails_generators}/hobo_front_controller/templates/helper.rb +0 -0
  78. data/rails_generators/hobo_front_controller/templates/index.dryml +25 -0
  79. data/{hobo_files/plugin/generators → rails_generators}/hobo_model/USAGE +0 -0
  80. data/{hobo_files/plugin/generators → rails_generators}/hobo_model/hobo_model_generator.rb +0 -0
  81. data/{hobo_files/plugin/generators → rails_generators}/hobo_model/templates/fixtures.yml +0 -0
  82. data/{hobo_files/plugin/generators → rails_generators}/hobo_model/templates/model.rb +0 -0
  83. data/{hobo_files/plugin/generators → rails_generators}/hobo_model/templates/unit_test.rb +0 -0
  84. data/{hobo_files/plugin/generators → rails_generators}/hobo_model_controller/USAGE +0 -0
  85. data/{hobo_files/plugin/generators → rails_generators}/hobo_model_controller/hobo_model_controller_generator.rb +0 -7
  86. data/{hobo_files/plugin/generators → rails_generators}/hobo_model_controller/templates/controller.rb +0 -0
  87. data/{hobo_files/plugin/generators → rails_generators}/hobo_model_controller/templates/functional_test.rb +0 -0
  88. data/{hobo_files/plugin/generators → rails_generators}/hobo_model_controller/templates/helper.rb +0 -0
  89. data/{hobo_files/plugin/generators → rails_generators}/hobo_model_resource/hobo_model_resource_generator.rb +0 -0
  90. data/{hobo_files/plugin/generators → rails_generators}/hobo_model_resource/templates/controller.rb +0 -0
  91. data/{hobo_files/plugin/generators → rails_generators}/hobo_model_resource/templates/functional_test.rb +0 -0
  92. data/{hobo_files/plugin/generators → rails_generators}/hobo_model_resource/templates/helper.rb +0 -0
  93. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/hobo_rapid_generator.rb +21 -11
  94. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/IE7.js +1 -1
  95. data/rails_generators/hobo_rapid/templates/blank.gif +0 -0
  96. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/hobo-rapid.css +0 -0
  97. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/hobo-rapid.js +175 -104
  98. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/lowpro.js +0 -0
  99. data/rails_generators/hobo_rapid/templates/nicEditorIcons.gif +0 -0
  100. data/rails_generators/hobo_rapid/templates/nicedit.js +91 -0
  101. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/reset.css +0 -0
  102. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/themes/clean/public/images/fieldbg.gif +0 -0
  103. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/themes/clean/public/images/pencil.png +0 -0
  104. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/themes/clean/public/images/small_close.png +0 -0
  105. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/themes/clean/public/images/spinner.gif +0 -0
  106. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +80 -71
  107. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +6 -1
  108. data/{hobo_files/plugin/generators → rails_generators}/hobo_rapid/templates/themes/clean/views/clean.dryml +4 -4
  109. data/rails_generators/hobo_subsite/hobo_subsite_generator.rb +73 -0
  110. data/rails_generators/hobo_subsite/templates/application.dryml +1 -0
  111. data/rails_generators/hobo_subsite/templates/controller.rb +5 -0
  112. data/rails_generators/hobo_subsite/templates/site_taglib.dryml +13 -0
  113. data/{hobo_files/plugin/generators → rails_generators}/hobo_user_controller/USAGE +0 -0
  114. data/{hobo_files/plugin/generators → rails_generators}/hobo_user_controller/hobo_user_controller_generator.rb +0 -0
  115. data/{hobo_files/plugin/generators → rails_generators}/hobo_user_controller/templates/controller.rb +0 -0
  116. data/{hobo_files/plugin/generators → rails_generators}/hobo_user_controller/templates/functional_test.rb +0 -0
  117. data/{hobo_files/plugin/generators → rails_generators}/hobo_user_controller/templates/helper.rb +0 -0
  118. data/{hobo_files/plugin/generators → rails_generators}/hobo_user_model/USAGE +0 -0
  119. data/rails_generators/hobo_user_model/hobo_user_model_generator.rb +30 -0
  120. data/{hobo_files/plugin/generators → rails_generators}/hobo_user_model/templates/fixtures.yml +0 -0
  121. data/rails_generators/hobo_user_model/templates/forgot_password.erb +10 -0
  122. data/rails_generators/hobo_user_model/templates/mailer.rb +14 -0
  123. data/rails_generators/hobo_user_model/templates/model.rb +55 -0
  124. data/{hobo_files/plugin/generators → rails_generators}/hobo_user_model/templates/unit_test.rb +0 -0
  125. data/script/destroy +14 -0
  126. data/script/generate +14 -0
  127. data/{hobo_files/plugin/taglibs → taglibs}/core.dryml +6 -7
  128. data/{hobo_files/plugin/taglibs → taglibs}/rapid.dryml +36 -67
  129. data/{hobo_files/plugin/taglibs → taglibs}/rapid_document_tags.dryml +7 -24
  130. data/{hobo_files/plugin/taglibs → taglibs}/rapid_editing.dryml +51 -50
  131. data/{hobo_files/plugin/taglibs → taglibs}/rapid_forms.dryml +62 -56
  132. data/taglibs/rapid_generics.dryml +33 -0
  133. data/taglibs/rapid_lifecycles.dryml +43 -0
  134. data/{hobo_files/plugin/taglibs → taglibs}/rapid_navigation.dryml +23 -23
  135. data/taglibs/rapid_pages.dryml +183 -0
  136. data/{hobo_files/plugin/taglibs → taglibs}/rapid_plus.dryml +30 -5
  137. data/{hobo_files/plugin/taglibs → taglibs}/rapid_support.dryml +3 -4
  138. data/taglibs/rapid_user_pages.dryml +179 -0
  139. data/{hobo_files/plugin/tasks → tasks}/environments.rake +0 -0
  140. data/{hobo_files/plugin/tasks → tasks}/fix_dryml.rake +0 -0
  141. data/{hobo_files/plugin/tasks → tasks}/generate_tag_reference.rb +21 -22
  142. data/tasks/hobo_tasks.rake +32 -0
  143. data/test/test_generator_helper.rb +29 -0
  144. data/test/test_helper.rb +1 -0
  145. data/test/test_hobo_model_controller_generator.rb +56 -0
  146. data/{hobo_files/plugin/uninstall.rb → uninstall.rb} +0 -0
  147. metadata +240 -225
  148. data/README.txt +0 -18
  149. data/hobo_files/plugin/LICENSE.txt +0 -22
  150. data/hobo_files/plugin/Rakefile +0 -96
  151. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +0 -24
  152. data/hobo_files/plugin/generators/hobo_front_controller/templates/search.dryml +0 -19
  153. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/banner.gif +0 -0
  154. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg-bodytop.gif +0 -0
  155. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg-corner-01.gif +0 -0
  156. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg-corner-02.gif +0 -0
  157. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg-corner-03.gif +0 -0
  158. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg-corner-04.gif +0 -0
  159. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg-shadow-bottom.gif +0 -0
  160. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg-shadow-left.gif +0 -0
  161. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg-shadow-right.gif +0 -0
  162. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg-shadow-top.gif +0 -0
  163. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header-blue.gif +0 -0
  164. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header-dblue.gif +0 -0
  165. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header-green.gif +0 -0
  166. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header-purple.gif +0 -0
  167. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header-red.gif +0 -0
  168. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/logo.gif +0 -0
  169. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/plus.png +0 -0
  170. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/spinner.gif +0 -0
  171. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt-list-img-dblue.gif +0 -0
  172. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt-list-img-green.gif +0 -0
  173. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt-list-img-purple.gif +0 -0
  174. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt-list-img-red.gif +0 -0
  175. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window-corner-01.gif +0 -0
  176. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window-corner-02.gif +0 -0
  177. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window-corner-03.gif +0 -0
  178. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window-corner-04.gif +0 -0
  179. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window-shadow-bottom.gif +0 -0
  180. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window-shadow-left.gif +0 -0
  181. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window-shadow-right.gif +0 -0
  182. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window-shadow-top.gif +0 -0
  183. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/stylesheets/application.css +0 -400
  184. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +0 -96
  185. data/hobo_files/plugin/generators/hobo_user_model/hobo_user_model_generator.rb +0 -25
  186. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +0 -56
  187. data/hobo_files/plugin/init.rb +0 -101
  188. data/hobo_files/plugin/lib/action_view_extensions/base.rb +0 -15
  189. data/hobo_files/plugin/lib/active_record/has_many_association.rb +0 -55
  190. data/hobo_files/plugin/lib/active_record/has_many_through_association.rb +0 -20
  191. data/hobo_files/plugin/lib/hobo/dryml.rb +0 -165
  192. data/hobo_files/plugin/lib/hobo/dryml/parser/document.rb +0 -53
  193. data/hobo_files/plugin/lib/hobo/scopes/association_proxy_extensions.rb +0 -33
  194. data/hobo_files/plugin/lib/hobo/scopes/defined_scope_proxy_extender.rb +0 -90
  195. data/hobo_files/plugin/lib/hobo/scopes/scope_reflection.rb +0 -18
  196. data/hobo_files/plugin/lib/hobo/scopes/scoped_proxy.rb +0 -55
  197. data/hobo_files/plugin/taglib-docs/core.markdown +0 -165
  198. data/hobo_files/plugin/taglib-docs/rapid.markdown +0 -677
  199. data/hobo_files/plugin/taglib-docs/rapid_document_tags.markdown +0 -240
  200. data/hobo_files/plugin/taglib-docs/rapid_editing.markdown +0 -418
  201. data/hobo_files/plugin/taglib-docs/rapid_forms.markdown +0 -562
  202. data/hobo_files/plugin/taglib-docs/rapid_generics.markdown +0 -187
  203. data/hobo_files/plugin/taglib-docs/rapid_navigation.markdown +0 -214
  204. data/hobo_files/plugin/taglib-docs/rapid_pages.markdown +0 -530
  205. data/hobo_files/plugin/taglib-docs/rapid_plus.markdown +0 -65
  206. data/hobo_files/plugin/taglib-docs/rapid_support.markdown +0 -50
  207. data/hobo_files/plugin/taglib-docs/rapid_user_pages.markdown +0 -129
  208. data/hobo_files/plugin/taglibs/rapid_generics.dryml +0 -117
  209. data/hobo_files/plugin/taglibs/rapid_pages.dryml +0 -359
  210. data/hobo_files/plugin/taglibs/rapid_user_pages.dryml +0 -104
  211. data/hobo_files/plugin/tasks/dump_fixtures.rake +0 -70
  212. data/hobo_files/plugin/tasks/hobo_tasks.rake +0 -17
@@ -0,0 +1,23 @@
1
+ module Hobo
2
+
3
+ module Lifecycles
4
+
5
+ class State < Struct.new(:name, :on_enter, :transitions_in, :transitions_out)
6
+
7
+ include Actions
8
+
9
+ def initialize(*args)
10
+ super
11
+ self.transitions_in = []
12
+ self.transitions_out = []
13
+ end
14
+
15
+
16
+ def activate!(record)
17
+ fire_event(record, on_enter)
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,66 @@
1
+ module Hobo
2
+
3
+ module Lifecycles
4
+
5
+ class Transition < Struct.new(:lifecycle, :name, :who, :start_states, :end_state, :on_transition, :options)
6
+
7
+ include Actions
8
+
9
+
10
+ def initialize(*args)
11
+ super
12
+ start_states.each do |from|
13
+ state = lifecycle.states[from.to_s]
14
+ raise ArgumentError, "No such state '#{from}' in #{'name'} transition (#{lifecycle.model.name})" unless state
15
+ state.transitions_out << self
16
+ end
17
+ unless end_state.to_s == "destroy"
18
+ state = lifecycle.states[end_state.to_s]
19
+ raise ArgumentError, "No such state '#{end_state}' in '#{name}' transition (#{lifecycle.model.name})" unless state
20
+ state.transitions_in << self
21
+ end
22
+ lifecycle.transitions << self
23
+ end
24
+
25
+
26
+ def allowed?(record, user, attributes=nil)
27
+ prepare_and_check!(record, user, attributes) && true
28
+ end
29
+
30
+
31
+ def extract_attributes(attributes)
32
+ update_attributes = options.fetch(:update, [])
33
+ attributes & update_attributes
34
+ end
35
+
36
+
37
+ def run!(record, user, attributes)
38
+ if prepare_and_check!(record, user, attributes)
39
+ fire_event(record, on_transition)
40
+ record.become end_state
41
+ else
42
+ raise Hobo::Model::PermissionDeniedError
43
+ end
44
+ end
45
+
46
+
47
+ def set_or_check_who_with_key!(record, user)
48
+ if who == :with_key
49
+ record.lifecycle.valid_key? or raise LifecycleKeyError
50
+ else
51
+ set_or_check_who_without_key!(record, user)
52
+ end
53
+ end
54
+ alias_method_chain :set_or_check_who!, :key
55
+
56
+
57
+ def parameters
58
+ options[:update] || []
59
+ end
60
+
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -1,36 +1,44 @@
1
1
  module Hobo
2
2
 
3
3
  module Model
4
-
4
+
5
5
  class PermissionDeniedError < RuntimeError; end
6
6
  class NoNameError < RuntimeError; end
7
-
7
+
8
8
  NAME_FIELD_GUESS = %w(name title)
9
9
  PRIMARY_CONTENT_GUESS = %w(description body content profile)
10
10
  SEARCH_COLUMNS_GUESS = %w(name title body content profile)
11
-
12
-
11
+
12
+
13
13
  def self.included(base)
14
14
  base.extend(ClassMethods)
15
-
15
+
16
16
  included_in_class_callbacks(base)
17
17
 
18
- Hobo.register_model(base)
19
-
18
+ register_model(base)
19
+
20
20
  patch_will_paginate
21
21
 
22
22
  base.class_eval do
23
23
  inheriting_cattr_reader :default_order
24
24
  alias_method_chain :attributes=, :hobo_type_conversion
25
+
26
+ attr_accessor :acting_user, :origin, :origin_attribute
27
+
28
+ bool_attr_accessor :exempt_from_edit_checks
29
+
30
+ include Hobo::Lifecycles::ModelExtensions
31
+ include Hobo::FindFor
32
+ include Hobo::IncludeInSave
25
33
  end
26
-
34
+
27
35
  class << base
28
- alias_method_chain :has_many, :defined_scopes
29
- alias_method_chain :has_many, :join_record_management
30
- alias_method_chain :belongs_to, :creator_metadata
31
-
36
+ alias_method_chain :belongs_to, :creator_metadata
37
+ alias_method_chain :belongs_to, :target_is
38
+ alias_method_chain :attr_accessor, :creator_metadata
39
+
32
40
  alias_method_chain :has_one, :new_method
33
-
41
+
34
42
  def inherited(klass)
35
43
  super
36
44
  fields do
@@ -40,15 +48,16 @@ module Hobo
40
48
  end
41
49
  end
42
50
 
51
+ base.fields # force hobofields to load
43
52
  end
44
-
53
+
45
54
  def self.patch_will_paginate
46
55
  if defined?(WillPaginate) && !WillPaginate::Collection.respond_to?(:member_class)
47
-
56
+
48
57
  WillPaginate::Collection.class_eval do
49
58
  attr_accessor :member_class, :origin, :origin_attribute
50
59
  end
51
-
60
+
52
61
  WillPaginate::Finder::ClassMethods.class_eval do
53
62
  def paginate_with_hobo_metadata(*args, &block)
54
63
  returning paginate_without_hobo_metadata(*args, &block) do |collection|
@@ -58,134 +67,200 @@ module Hobo
58
67
  end
59
68
  end
60
69
  alias_method_chain :paginate, :hobo_metadata
61
-
70
+
62
71
  end
63
-
72
+
64
73
  end
65
74
  end
66
-
67
75
 
76
+
77
+ def self.register_model(model)
78
+ @model_names ||= Set.new
79
+ @model_names << model.name
80
+ end
81
+
82
+
83
+ def self.all_models
84
+ # Load every controller in app/controllers...
85
+ unless @models_loaded
86
+ Dir.entries("#{RAILS_ROOT}/app/models/").each do |f|
87
+ f =~ /^[a-zA-Z_][a-zA-Z0-9_]*\.rb$/ and f.sub(/.rb$/, '').camelize.constantize
88
+ end
89
+ @models_loaded = true
90
+ end
91
+
92
+ # ...but only return the ones that registered themselves
93
+ @model_names.*.constantize
94
+ end
95
+
96
+
97
+ def self.enable
98
+ ActiveRecord::Base.class_eval do
99
+ def self.hobo_model
100
+ include Hobo::Model
101
+ fields # force hobofields to load
102
+ end
103
+
104
+ alias_method :has_hobo_method?, :respond_to_without_attributes?
105
+ end
106
+ end
107
+
108
+
68
109
  module ClassMethods
69
-
110
+
70
111
  # include methods also shared by CompositeModel
71
112
  #include ModelSupport::ClassMethods
72
-
113
+
73
114
  attr_accessor :creator_attribute
74
115
  attr_writer :name_attribute, :primary_content_attribute
75
-
116
+
76
117
  def named(*args)
77
118
  raise NoNameError, "Model #{name} has no name attribute" unless name_attribute
78
119
  send("find_by_#{name_attribute}", *args)
79
120
  end
80
-
81
-
121
+
122
+
82
123
  def field_added(name, type, args, options)
83
124
  self.name_attribute = name.to_sym if options.delete(:name)
84
125
  self.primary_content_attribute = name.to_sym if options.delete(:primary_content)
85
126
  self.creator_attribute = name.to_sym if options.delete(:creator)
86
127
  validate = options.delete(:validate) {true}
87
-
128
+
88
129
  #FIXME - this should be in Hobo::User
89
130
  send(:login_attribute=, name.to_sym, validate) if options.delete(:login) && respond_to?(:login_attribute=)
90
131
  end
91
-
92
-
132
+
133
+
93
134
  def user_find(user, *args)
94
135
  record = find(*args)
95
136
  raise PermissionDeniedError unless Hobo.can_view?(user, record)
96
137
  record
97
138
  end
98
-
99
-
139
+
140
+
100
141
  def user_new(user, attributes={})
101
- record = new(attributes)
102
- record.user_changes(user) and record
142
+ record = new(attributes) {|r| r.acting_user = user; yield if block_given? }
143
+ allowed = record.user_changes(user)
144
+ record.acting_user = nil
145
+ allowed && record
103
146
  end
104
-
105
-
147
+
148
+
106
149
  def user_new!(user, attributes={})
107
150
  user_new(user, attributes) or raise PermissionDeniedError
108
151
  end
109
-
110
-
152
+
153
+
111
154
  def user_create(user, attributes={})
112
155
  record = new(attributes)
113
156
  record.user_save_changes(user)
114
157
  record
115
158
  end
116
-
117
-
159
+
160
+
118
161
  def user_can_create?(user, attributes={})
119
162
  record = new(attributes)
120
163
  record.user_changes(user)
121
164
  end
122
-
123
-
165
+
166
+
124
167
  def user_update(user, id, attributes={})
125
168
  find(id).user_save_changes(user, attributes)
126
169
  end
127
-
128
-
170
+
171
+
129
172
  def name_attribute
130
173
  @name_attribute ||= begin
131
- cols = columns.*.name
132
- NAME_FIELD_GUESS.detect {|f| f.in? columns.*.name }
174
+ names = columns.*.name + public_instance_methods
175
+ NAME_FIELD_GUESS.detect {|f| f.in? names }
133
176
  end
134
177
  end
135
-
178
+
136
179
 
137
180
  def primary_content_attribute
138
181
  @primary_content_attribute ||= begin
139
- cols = columns.*.name
140
- PRIMARY_CONTENT_GUESS.detect {|f| f.in? columns.*.name }
182
+ names = columns.*.name + public_instance_methods
183
+ PRIMARY_CONTENT_GUESS.detect {|f| f.in? names }
141
184
  end
142
185
  end
143
-
186
+
144
187
  def dependent_collections
145
- reflections.values.select do |refl|
188
+ reflections.values.select do |refl|
146
189
  refl.macro == :has_many && refl.options[:dependent]
147
190
  end.*.name
148
191
  end
149
-
150
-
192
+
193
+
151
194
  def dependent_on
152
- reflections.values.select do |refl|
195
+ reflections.values.select do |refl|
153
196
  refl.macro == :belongs_to && (rev = reverse_reflection(refl.name) and rev.options[:dependent])
154
197
  end.*.name
155
198
  end
156
-
157
-
199
+
200
+
158
201
  def default_dependent_on
159
202
  dependent_on.first
160
203
  end
161
-
162
-
204
+
205
+
163
206
  private
164
-
165
-
207
+
208
+
166
209
  def belongs_to_with_creator_metadata(name, options={}, &block)
167
210
  self.creator_attribute = name.to_sym if options.delete(:creator)
168
211
  belongs_to_without_creator_metadata(name, options, &block)
169
212
  end
170
-
171
-
213
+
214
+ def belongs_to_with_target_is(name, options={}, &block)
215
+ belongs_to_without_target_is(name, options, &block)
216
+ refl = reflections[name]
217
+ if options[:polymorphic]
218
+ class_eval %{
219
+ def #{name}_is?(target)
220
+ target.id == self.#{refl.primary_key_name} && target.class.name == self.#{refl.options[:foreign_type]}
221
+ end
222
+ }
223
+ else
224
+ class_eval %{
225
+ def #{name}_is?(target)
226
+ target.id == self.#{refl.primary_key_name}
227
+ end
228
+ }
229
+ end
230
+ end
231
+
232
+
233
+ def attr_accessor_with_creator_metadata(*args)
234
+ options = args.extract_options!
235
+ if options.delete(:creator)
236
+ if args.length == 1
237
+ self.creator_attribute = args.first.to_sym
238
+ else
239
+ raise ArgumentError, "trying to set :creator => true on multiple attributes"
240
+ end
241
+ end
242
+ args << options unless options.empty?
243
+ attr_accessor_without_creator_metadata(*args)
244
+ end
245
+
246
+
172
247
  def has_one_with_new_method(name, options={}, &block)
173
248
  has_one_without_new_method(name, options)
174
249
  class_eval "def new_#{name}(attributes={}); build_#{name}(attributes, false); end"
175
250
  end
176
-
177
-
251
+
252
+
178
253
  def set_default_order(order)
179
254
  @default_order = order
180
255
  end
181
-
256
+
182
257
 
183
258
  def never_show(*fields)
184
259
  @hobo_never_show ||= []
185
260
  @hobo_never_show.concat(fields.*.to_sym)
186
261
  end
187
262
 
188
-
263
+
189
264
  def set_search_columns(*columns)
190
265
  class_eval %{
191
266
  def self.search_columns
@@ -193,15 +268,15 @@ module Hobo
193
268
  end
194
269
  }
195
270
  end
196
-
197
-
271
+
272
+
198
273
  public
199
274
 
200
-
275
+
201
276
  def never_show?(field)
202
277
  (@hobo_never_show && field.to_sym.in?(@hobo_never_show)) || (superclass < Hobo::Model && superclass.never_show?(field))
203
278
  end
204
-
279
+
205
280
 
206
281
  def find(*args, &b)
207
282
  options = args.extract_options!
@@ -216,29 +291,36 @@ module Hobo
216
291
  result.member_class = self if result.is_a?(Array)
217
292
  result
218
293
  end
219
-
220
294
 
295
+
296
+ def find_by_sql(*args)
297
+ result = super
298
+ result.member_class = self # find_by_sql always returns array
299
+ result
300
+ end
301
+
302
+
221
303
  def all(options={})
222
304
  find(:all, options.reverse_merge(:order => :default))
223
305
  end
224
-
225
-
306
+
307
+
226
308
  def creator_type
227
- reflections[creator_attribute]._?.klass
309
+ attr_type(creator_attribute)
228
310
  end
229
311
 
230
-
312
+
231
313
  def search_columns
232
- cols = columns.*.name
233
- SEARCH_COLUMNS_GUESS.select{|c| c.in?(cols) }
314
+ column_names = columns.*.name
315
+ SEARCH_COLUMNS_GUESS.select{|c| c.in?(column_names) }
234
316
  end
235
-
236
-
317
+
318
+
237
319
  # FIXME: This should really be a method on AssociationReflection
238
320
  def reverse_reflection(association_name)
239
- refl = reflections[association_name]
321
+ refl = reflections[association_name.to_sym] or raise "No reverse reflection for #{name}.#{association_name}"
240
322
  return nil if refl.options[:conditions] || refl.options[:polymorphic]
241
-
323
+
242
324
  reverse_macro = if refl.macro == :has_many
243
325
  :belongs_to
244
326
  elsif refl.macro == :belongs_to
@@ -251,12 +333,12 @@ module Hobo
251
333
  r.primary_key_name == refl.primary_key_name
252
334
  end
253
335
  end
254
-
255
-
336
+
337
+
256
338
  def has_inheritance_column?
257
339
  columns_hash.include?(inheritance_column)
258
340
  end
259
-
341
+
260
342
 
261
343
  def method_missing(name, *args, &block)
262
344
  name = name.to_s
@@ -264,148 +346,155 @@ module Hobo
264
346
  # FIXME: Do we need this now?
265
347
  call_method_chain(name, args, &block)
266
348
  elsif create_automatic_scope(name)
267
- send(name, *args, &block)
349
+ send(name.to_sym, *args, &block)
268
350
  else
269
351
  super(name.to_sym, *args, &block)
270
352
  end
271
353
  end
272
354
 
273
-
355
+
356
+ def respond_to?(method)
357
+ super || create_automatic_scope(method)
358
+ end
359
+
360
+
274
361
  def call_method_chain(chain, args, &block)
275
362
  parts = chain.split(".")
276
363
  s = parts[0..-2].inject(self) { |m, scope| m.send(scope) }
277
364
  s.send(parts.last, *args)
278
365
  end
279
-
280
-
366
+
367
+
281
368
  def to_url_path
282
369
  "#{name.underscore.pluralize}"
283
370
  end
284
-
285
-
371
+
372
+
286
373
  def typed_id
287
374
  HoboFields.to_name(self) || name.underscore.gsub("/", "__")
288
375
  end
289
-
290
-
291
- def manage_join_records(association)
292
-
293
- method = "manage_join_records_for_#{association}"
294
- after_save method
295
- class_eval %{
296
- def #{method}
297
- assigned = #{association}.dup
298
- current = #{association}.reload
299
-
300
- through = #{association}.proxy_reflection.through_reflection
301
- source = #{association}.proxy_reflection.source_reflection
302
-
303
- to_delete = current - assigned
304
- to_add = assigned - current
305
- through.klass.delete_all(["\#{through.primary_key_name} = ? and \#{source.primary_key_name} in (?)",
306
- self.id, to_delete.*.id]) if to_delete.any?
307
- to_add.each { |record| #{association} << record }
308
- end
309
- }
310
- end
311
-
312
- def has_many_with_join_record_management(name, options={}, &b)
313
- manage = options.delete(:managed)
314
- returning (has_many_without_join_record_management(name, options, &b)) do
315
- manage_join_records(name) if manage
316
- end
317
- end
376
+
318
377
 
319
378
  end # --- of ClassMethods --- #
320
-
321
-
379
+
380
+
322
381
  include Scopes
323
-
324
-
382
+
325
383
  def to_url_path
326
384
  "#{self.class.to_url_path}/#{to_param}" unless new_record?
327
385
  end
328
386
 
329
387
 
330
- def user_changes(user, changes={})
331
- if new_record?
332
- self.attributes = changes
333
- set_creator(user)
334
- Hobo.can_create?(user, self)
388
+ def to_param
389
+ name_attr = self.class.name_attribute
390
+ if name_attr
391
+ readable = send(name_attr).to_s.downcase.gsub(/[^a-z0-9]+/, '-').gsub(/-+$/, '').gsub(/^-+$/, '').split('-')[0..5].join('-')
392
+ @to_param ||= "#{id}-#{readable}"
335
393
  else
336
- original = duplicate
337
- # 'duplicate' can cause these to be set, but they can conflict
338
- # with the changes so we clear them
339
- clear_aggregation_cache
340
- clear_association_cache
341
-
342
- self.attributes = changes
343
-
344
- Hobo.can_update?(user, original, self)
345
- end
394
+ id
395
+ end
346
396
  end
347
-
348
-
397
+
398
+
399
+ def with_acting_user(user)
400
+ old = acting_user
401
+ self.acting_user = user
402
+ result = yield
403
+ self.acting_user = old
404
+ result
405
+ end
406
+
407
+
408
+ def user_changes(user, changes={})
409
+ with_acting_user user do
410
+ if new_record?
411
+ self.attributes = changes
412
+ set_creator(user)
413
+ Hobo.can_create?(user, self)
414
+ else
415
+ original = duplicate
416
+ # 'duplicate' can cause these to be set, but they can conflict
417
+ # with the changes so we clear them
418
+ clear_aggregation_cache
419
+ clear_association_cache
420
+
421
+ self.attributes = changes
422
+
423
+ Hobo.can_update?(user, original, self)
424
+ end
425
+ end
426
+ end
427
+
428
+
349
429
  def user_changes!(user, changes={})
350
430
  user_changes(user, changes) or raise PermissionDeniedError
351
431
  end
352
-
353
-
432
+
433
+
354
434
  def user_can_create?(user, attributes={})
355
435
  raise ArgumentError, "Called #user_can_create? on existing record" unless new_record?
356
436
  user_changes(user, attributes)
357
437
  end
358
-
438
+
359
439
 
360
440
  def user_save_changes(user, changes={})
361
- user_changes!(user, changes)
362
- save
441
+ with_acting_user user do
442
+ user_changes!(user, changes)
443
+ save
444
+ end
363
445
  end
364
-
446
+
447
+
448
+ def user_save(user)
449
+ user_save_changes(user)
450
+ end
451
+
365
452
 
366
453
  def user_view(user, field=nil)
367
454
  raise PermissionDeniedError, self.inspect unless Hobo.can_view?(user, self, field)
368
455
  end
369
-
370
-
456
+
457
+
371
458
  def user_destroy(user)
372
- raise PermissionDeniedError unless Hobo.can_delete?(user, self)
373
- destroy
459
+ with_acting_user user do
460
+ raise PermissionDeniedError unless Hobo.can_delete?(user, self)
461
+ destroy
462
+ end
374
463
  end
375
-
376
-
464
+
465
+
377
466
  def dependent_on
378
467
  self.class.dependent_on.map { |assoc| send(assoc) }
379
468
  end
380
-
381
-
469
+
470
+
382
471
  def attributes_with_hobo_type_conversion=(attributes, guard_protected_attributes=true)
383
472
  converted = attributes.map_hash { |k, v| convert_type_for_mass_assignment(self.class.attr_type(k), v) }
384
473
  send(:attributes_without_hobo_type_conversion=, converted, guard_protected_attributes)
385
474
  end
386
-
387
475
 
388
-
476
+
389
477
  def set_creator(user)
390
478
  set_creator!(user) unless get_creator
391
479
  end
392
-
393
-
480
+
481
+
394
482
  def set_creator!(user)
395
483
  attr = self.class.creator_attribute
396
484
  return unless attr
397
-
485
+
398
486
  # Is creator a string field or an association?
399
- if self.class.reflections[attr]
400
- # It's an association
401
- self.send("#{attr}=", user) if (t = self.class.creator_type) && user.is_a?(t)
402
- else
403
- # Assume it's a string field -- set it to the name of the current user
487
+ if self.class.attr_type(attr)._? <= String
488
+ # Set it to the name of the current user
404
489
  self.send("#{attr}=", user.to_s) unless user.guest?
490
+ else
491
+ # Assume user is a user object, but don't set if we've got a type mismatch
492
+ t = self.class.creator_type
493
+ self.send("#{attr}=", user) if t.nil? || user.is_a?(t)
405
494
  end
406
495
  end
407
-
408
-
496
+
497
+
409
498
  # We deliberately give this method an unconventional name to avoid
410
499
  # polluting the application namespace too badly
411
500
  def get_creator
@@ -418,7 +507,7 @@ module Hobo
418
507
  copy.copy_instance_variables_from(self, ["@attributes_cache"])
419
508
  copy.instance_variable_set("@attributes", @attributes.dup)
420
509
  copy.instance_variable_set("@new_record", nil) unless new_record?
421
-
510
+
422
511
  # Shallow copy of belongs_to associations
423
512
  for refl in self.class.reflections.values
424
513
  if refl.macro == :belongs_to and (target = self.send(refl.name))
@@ -433,31 +522,31 @@ module Hobo
433
522
 
434
523
  def same_fields?(other, *fields)
435
524
  return true if other.nil?
436
-
525
+
437
526
  fields = fields.flatten
438
527
  fields.all?{|f| self.send(f) == other.send(f)}
439
528
  end
440
-
441
-
529
+
530
+
442
531
  def only_changed_fields?(other, *changed_fields)
443
532
  return true if other.nil?
444
-
533
+
445
534
  changed_fields = changed_fields.flatten.*.to_s
446
535
  all_cols = self.class.columns.*.name - []
447
536
  all_cols.all?{|c| c.in?(changed_fields) || self.send(c) == other.send(c) }
448
537
  end
449
-
450
-
538
+
539
+
451
540
  def compose_with(object, use=nil)
452
541
  CompositeModel.new_for([self, object])
453
542
  end
454
-
543
+
455
544
 
456
545
  def typed_id
457
- "#{self.class.name.underscore}_#{self.id}" if id
546
+ "#{self.class.name.underscore}_#{self.id}" if id
458
547
  end
459
548
 
460
-
549
+
461
550
  def to_s
462
551
  if self.class.name_attribute
463
552
  send self.class.name_attribute
@@ -465,23 +554,26 @@ module Hobo
465
554
  "#{self.class.name.titleize} #{id}"
466
555
  end
467
556
  end
468
-
469
-
557
+
558
+
470
559
  private
471
-
472
-
560
+
561
+
473
562
  def parse_datetime(s)
474
563
  defined?(Chronic) ? Chronic.parse(s) : Time.parse(s)
475
564
  end
476
565
 
477
-
566
+
478
567
  def convert_type_for_mass_assignment(field_type, value)
479
- if field_type.is_a?(ActiveRecord::Reflection::AssociationReflection)
480
- convert_associated_records_for_mass_assignment(field_type, value)
481
-
568
+ if field_type.is_a?(Class) && field_type < ActiveRecord::Base
569
+ convert_record_reference_for_mass_assignment(field_type, value)
570
+
571
+ elsif field_type.is_a?(ActiveRecord::Reflection::AssociationReflection)
572
+ convert_collection_for_mass_assignment(field_type, value)
573
+
482
574
  elsif !field_type.is_a?(Class)
483
575
  value
484
-
576
+
485
577
  elsif field_type <= Date
486
578
  if value.is_a? Hash
487
579
  Date.new(*(%w{year month day}.map{|s| value[s].to_i}))
@@ -491,8 +583,8 @@ module Hobo
491
583
  else
492
584
  value
493
585
  end
494
-
495
- elsif field_type <= Time
586
+
587
+ elsif field_type <= Time || field_type <= ActiveSupport::TimeWithZone
496
588
  if value.is_a? Hash
497
589
  Time.local(*(%w{year month day hour minute}.map{|s| value[s].to_i}))
498
590
  elsif value.is_a? String
@@ -500,52 +592,49 @@ module Hobo
500
592
  else
501
593
  value
502
594
  end
503
-
595
+
504
596
  elsif field_type <= Hobo::Boolean
505
597
  (value.is_a?(String) && value.strip.downcase.in?(['0', 'false']) || value.blank?) ? false : true
506
-
598
+
507
599
  else
508
- # primitive field
600
+ # no conversion
509
601
  value
510
602
  end
511
603
  end
512
-
513
- def convert_associated_records_for_mass_assignment(reflection, value)
514
- if reflection.macro.in?([:belongs_to, :has_one])
515
- if value.is_a?(String)
516
- if value.starts_with?('@')
517
- # TODO: This @foo_1 feature is rarely (never?) used - get rid of it
518
- Hobo.object_from_dom_id(value[1..-1])
519
- else
520
- reflection.klass.named(value)
521
- end
522
- else
523
- value
524
- end
525
- elsif reflection.macro == :has_many
526
- if reflection.klass.try.name_attribute
527
- value.map do |x|
528
- if x.is_a?(String)
529
- reflection.klass.named(x) unless x.blank?
530
- else
531
- x
532
- end
533
- end.compact
604
+
605
+
606
+ def convert_record_reference_for_mass_assignment(klass, value)
607
+ if value.is_a?(String)
608
+ if value.starts_with?('@')
609
+ # TODO: This @foo_1 feature is rarely (never?) used - get rid of it
610
+ Hobo.object_from_dom_id(value[1..-1])
534
611
  else
535
- value
612
+ klass.named(value)
536
613
  end
537
-
538
614
  else
539
- # unknown kind of accociation - no conversion
540
615
  value
541
616
  end
542
617
  end
543
-
618
+
619
+
620
+ def convert_collection_for_mass_assignment(reflection, value)
621
+ klass = reflection.safe_class
622
+ if klass.try.name_attribute && value.is_a?(Array)
623
+ value.map do |x|
624
+ if x.is_a?(String)
625
+ klass.named(x) unless x.blank?
626
+ else
627
+ x
628
+ end
629
+ end.compact
630
+ else
631
+ value
632
+ end
633
+ end
634
+
544
635
  end
545
-
636
+
546
637
  end
547
638
 
548
639
 
549
- class ActiveRecord::Base
550
- alias_method :has_hobo_method?, :respond_to_without_attributes?
551
- end
640
+ Hobo::Model.enable if defined? ActiveRecord