lolita 3.1.10 → 3.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (239) hide show
  1. data/.document +5 -5
  2. data/.rspec +2 -2
  3. data/GUIDELINES.rdoc +24 -24
  4. data/Gemfile +26 -26
  5. data/History.rdoc +162 -154
  6. data/LICENSE.txt +22 -22
  7. data/README.rdoc +98 -98
  8. data/Rakefile +40 -40
  9. data/VERSION +1 -1
  10. data/app/controllers/lolita/info_controller.rb +41 -41
  11. data/app/controllers/lolita/rest_controller.rb +130 -130
  12. data/app/helpers/components/lolita/configuration/list_component.rb +10 -10
  13. data/app/helpers/lolita_helper.rb +11 -11
  14. data/app/views/components/lolita/configuration/column/_display.html.erb +4 -4
  15. data/app/views/components/lolita/configuration/column/_header.html.erb +7 -7
  16. data/app/views/components/lolita/configuration/column/_sort.html.erb +4 -4
  17. data/app/views/components/lolita/configuration/columns/_body.html.erb +4 -4
  18. data/app/views/components/lolita/configuration/columns/_display.html.erb +1 -1
  19. data/app/views/components/lolita/configuration/columns/_first.html.erb +2 -2
  20. data/app/views/components/lolita/configuration/columns/_first_column_header.html.erb +3 -3
  21. data/app/views/components/lolita/configuration/columns/_header.html.erb +8 -8
  22. data/app/views/components/lolita/configuration/columns/_last.html.erb +4 -4
  23. data/app/views/components/lolita/configuration/columns/_last_column_header.html.erb +2 -2
  24. data/app/views/components/lolita/configuration/columns/_row.html.erb +7 -7
  25. data/app/views/components/lolita/configuration/field/_display.html.erb +9 -9
  26. data/app/views/components/lolita/configuration/field/_label.html.erb +1 -1
  27. data/app/views/components/lolita/configuration/field/_object.html.erb +1 -1
  28. data/app/views/components/lolita/configuration/field/array/_display.html.erb +4 -4
  29. data/app/views/components/lolita/configuration/field/array/habtm/_display.html.erb +26 -26
  30. data/app/views/components/lolita/configuration/field/array/select/_display.html.erb +5 -5
  31. data/app/views/components/lolita/configuration/field/boolean/filter/_display.html.erb +1 -1
  32. data/app/views/components/lolita/configuration/field/date/_display.html.erb +1 -1
  33. data/app/views/components/lolita/configuration/field/date_time/date/_display.html.erb +1 -1
  34. data/app/views/components/lolita/configuration/field/integer/_display.html.erb +1 -1
  35. data/app/views/components/lolita/configuration/field/string/_display.html.erb +4 -4
  36. data/app/views/components/lolita/configuration/field/string/disabled/_display.html.erb +1 -1
  37. data/app/views/components/lolita/configuration/field/string/password/_display.html.erb +3 -3
  38. data/app/views/components/lolita/configuration/field/string/text/_display.html.erb +18 -18
  39. data/app/views/components/lolita/configuration/field_set/_display.html.erb +5 -5
  40. data/app/views/components/lolita/configuration/list/_display.html.erb +10 -10
  41. data/app/views/components/lolita/configuration/list/_filter.html.erb +7 -7
  42. data/app/views/components/lolita/configuration/list/_new_resource.html.erb +4 -4
  43. data/app/views/components/lolita/configuration/list/_title.html.erb +4 -4
  44. data/app/views/components/lolita/configuration/tab/_display.html.erb +17 -17
  45. data/app/views/components/lolita/configuration/tab/_fields.html.erb +6 -6
  46. data/app/views/components/lolita/configuration/tab/content/_display.html.erb +1 -1
  47. data/app/views/components/lolita/configuration/tab/default/_display.html.erb +8 -8
  48. data/app/views/components/lolita/configuration/tabs/_display.html.erb +10 -10
  49. data/app/views/components/lolita/navigation/_display.html.erb +2 -2
  50. data/app/views/components/lolita/navigation/_tree.html.erb +38 -38
  51. data/app/views/components/lolita/shared/_flash.html.erb +5 -5
  52. data/app/views/components/lolita/shared/_header.html.erb +8 -8
  53. data/app/views/components/lolita/shared/_right_sidebar.html.erb +13 -13
  54. data/app/views/layouts/lolita/application.html.erb +44 -44
  55. data/app/views/lolita/info/index.html.erb +232 -232
  56. data/app/views/lolita/rest/form.html.erb +1 -1
  57. data/app/views/lolita/rest/index.html.erb +1 -1
  58. data/author +1 -1
  59. data/config/locales/default/lv.yml +180 -180
  60. data/config/locales/en.yml +20 -20
  61. data/config/locales/lv.yml +20 -20
  62. data/config/routes.rb +3 -3
  63. data/db/seed.rb +1 -1
  64. data/features/create_page.feature +19 -19
  65. data/features/step_definitions/lolita_steps.rb +7 -7
  66. data/features/step_definitions/web_steps.rb +219 -219
  67. data/features/support/env.rb +73 -73
  68. data/features/support/paths.rb +35 -35
  69. data/lib/generators/helpers/file_helper.rb +22 -22
  70. data/lib/generators/lolita/assets_generator.rb +18 -18
  71. data/lib/generators/lolita/install_generator.rb +28 -28
  72. data/lib/generators/templates/lolita.rb +19 -19
  73. data/lib/lolita.rb +184 -184
  74. data/lib/lolita/adapter/abstract_adapter.rb +13 -13
  75. data/lib/lolita/adapter/active_record.rb +148 -148
  76. data/lib/lolita/adapter/mongoid.rb +127 -127
  77. data/lib/lolita/base_configuration.rb +165 -165
  78. data/lib/lolita/builder.rb +249 -249
  79. data/lib/lolita/configuration/base.rb +76 -76
  80. data/lib/lolita/configuration/column.rb +93 -94
  81. data/lib/lolita/configuration/columns.rb +66 -66
  82. data/lib/lolita/configuration/factory.rb +55 -55
  83. data/lib/lolita/configuration/field.rb +216 -216
  84. data/lib/lolita/configuration/field/array.rb +75 -75
  85. data/lib/lolita/configuration/field/big_decimal.rb +12 -12
  86. data/lib/lolita/configuration/field/boolean.rb +12 -12
  87. data/lib/lolita/configuration/field/date.rb +12 -12
  88. data/lib/lolita/configuration/field/date_time.rb +13 -13
  89. data/lib/lolita/configuration/field/integer.rb +12 -12
  90. data/lib/lolita/configuration/field/string.rb +13 -13
  91. data/lib/lolita/configuration/field/time.rb +13 -13
  92. data/lib/lolita/configuration/field_set.rb +25 -25
  93. data/lib/lolita/configuration/fields.rb +35 -35
  94. data/lib/lolita/configuration/filter.rb +108 -108
  95. data/lib/lolita/configuration/list.rb +104 -104
  96. data/lib/lolita/configuration/page.rb +125 -124
  97. data/lib/lolita/configuration/tab.rb +187 -187
  98. data/lib/lolita/configuration/tab/content.rb +21 -21
  99. data/lib/lolita/configuration/tab/default.rb +25 -25
  100. data/lib/lolita/configuration/tabs.rb +132 -132
  101. data/lib/lolita/controllers/component_helpers.rb +127 -127
  102. data/lib/lolita/controllers/internal_helpers.rb +109 -109
  103. data/lib/lolita/controllers/url_helpers.rb +111 -111
  104. data/lib/lolita/controllers/user_helpers.rb +32 -32
  105. data/lib/lolita/controllers/view_user_helpers.rb +12 -12
  106. data/lib/lolita/dbi/base.rb +50 -50
  107. data/lib/lolita/errors.rb +12 -12
  108. data/lib/lolita/hooks.rb +355 -355
  109. data/lib/lolita/hooks/named_hook.rb +125 -125
  110. data/lib/lolita/lazy_loader.rb +46 -46
  111. data/lib/lolita/mapping.rb +55 -55
  112. data/lib/lolita/modules.rb +6 -6
  113. data/lib/lolita/modules/rest.rb +10 -10
  114. data/lib/lolita/navigation/branch.rb +132 -132
  115. data/lib/lolita/navigation/tree.rb +116 -116
  116. data/lib/lolita/observed_array.rb +74 -74
  117. data/lib/lolita/rails.rb +20 -20
  118. data/lib/lolita/rails/all.rb +10 -10
  119. data/lib/lolita/rails/routes.rb +133 -133
  120. data/lib/lolita/ruby_ext/accessors.rb +26 -26
  121. data/lib/lolita/support/formatter.rb +62 -62
  122. data/lib/lolita/support/formatter/rails.rb +56 -56
  123. data/lib/lolita/test/matchers.rb +77 -77
  124. data/lolita.gemspec +2 -2
  125. data/public/javascripts/jquery-1.6.min.js +15 -15
  126. data/public/javascripts/jquery-ui-1.8.13.min.js +407 -407
  127. data/public/javascripts/lolita/main.js +39 -39
  128. data/public/javascripts/lolita/tab.js +41 -41
  129. data/public/javascripts/modernizr-1.7.min.js +1 -1
  130. data/public/javascripts/rails.js +137 -137
  131. data/public/javascripts/tinymce/langs/en.js +221 -221
  132. data/public/javascripts/tinymce/license.txt +504 -504
  133. data/public/javascripts/tinymce/themes/advanced/about.htm +52 -52
  134. data/public/javascripts/tinymce/themes/advanced/anchor.htm +26 -26
  135. data/public/javascripts/tinymce/themes/advanced/charmap.htm +51 -51
  136. data/public/javascripts/tinymce/themes/advanced/color_picker.htm +74 -74
  137. data/public/javascripts/tinymce/themes/advanced/editor_template_src.js +1328 -1328
  138. data/public/javascripts/tinymce/themes/advanced/image.htm +80 -80
  139. data/public/javascripts/tinymce/themes/advanced/js/about.js +73 -73
  140. data/public/javascripts/tinymce/themes/advanced/js/anchor.js +42 -42
  141. data/public/javascripts/tinymce/themes/advanced/js/charmap.js +354 -354
  142. data/public/javascripts/tinymce/themes/advanced/js/color_picker.js +329 -329
  143. data/public/javascripts/tinymce/themes/advanced/js/image.js +247 -247
  144. data/public/javascripts/tinymce/themes/advanced/js/link.js +153 -153
  145. data/public/javascripts/tinymce/themes/advanced/js/source_editor.js +56 -56
  146. data/public/javascripts/tinymce/themes/advanced/langs/en.js +68 -68
  147. data/public/javascripts/tinymce/themes/advanced/langs/en_dlg.js +53 -53
  148. data/public/javascripts/tinymce/themes/advanced/link.htm +57 -57
  149. data/public/javascripts/tinymce/themes/advanced/shortcuts.htm +47 -47
  150. data/public/javascripts/tinymce/themes/advanced/skins/cirkuit/content.css +66 -66
  151. data/public/javascripts/tinymce/themes/advanced/skins/cirkuit/dialog.css +117 -117
  152. data/public/javascripts/tinymce/themes/advanced/skins/cirkuit/ui.css +988 -988
  153. data/public/javascripts/tinymce/themes/advanced/source_editor.htm +25 -25
  154. data/public/javascripts/tinymce/tiny_mce_popup.js +4 -4
  155. data/public/javascripts/tinymce_config.js +15 -15
  156. data/public/stylesheets/lolita/default.css +169 -169
  157. data/public/stylesheets/lolita/style.css +253 -253
  158. data/spec/adapter_helper.rb +43 -43
  159. data/spec/builder_spec.rb +120 -120
  160. data/spec/configuration/base_spec.rb +44 -44
  161. data/spec/configuration/column_spec.rb +89 -89
  162. data/spec/configuration/columns_spec.rb +54 -54
  163. data/spec/configuration/field_set_spec.rb +12 -12
  164. data/spec/configuration/field_spec.rb +118 -118
  165. data/spec/configuration/filter_spec.rb +122 -122
  166. data/spec/configuration/list_spec.rb +76 -76
  167. data/spec/configuration/page_spec.rb +19 -19
  168. data/spec/configuration/tab_spec.rb +165 -165
  169. data/spec/configuration/tabs_spec.rb +106 -106
  170. data/spec/controllers/component_helpers_spec.rb +5 -5
  171. data/spec/controllers/internal_helpers_spec.rb +76 -76
  172. data/spec/controllers/lolita_rest_nested_resources_spec.rb +33 -33
  173. data/spec/controllers/lolita_rest_spec.rb +53 -53
  174. data/spec/dbi/base_spec.rb +44 -44
  175. data/spec/hooks_spec.rb +257 -257
  176. data/spec/lolita_spec.rb +13 -13
  177. data/spec/mapping_spec.rb +29 -29
  178. data/spec/navigation/branch_spec.rb +81 -81
  179. data/spec/navigation/tree_spec.rb +73 -73
  180. data/spec/orm/mongoid.rb +11 -11
  181. data/spec/rails_app/app/controllers/application_controller.rb +3 -3
  182. data/spec/rails_app/app/helpers/application_helper.rb +3 -3
  183. data/spec/rails_app/app/mongoid/address.rb +7 -7
  184. data/spec/rails_app/app/mongoid/category.rb +5 -5
  185. data/spec/rails_app/app/mongoid/comment.rb +5 -5
  186. data/spec/rails_app/app/mongoid/post.rb +18 -18
  187. data/spec/rails_app/app/mongoid/preference.rb +5 -5
  188. data/spec/rails_app/app/mongoid/profile.rb +13 -13
  189. data/spec/rails_app/app/mongoid/tag.rb +3 -3
  190. data/spec/rails_app/app/views/components/lolita/configuration/list/_body_cell.html.erb +1 -1
  191. data/spec/rails_app/config/application.rb +26 -26
  192. data/spec/rails_app/config/boot.rb +13 -13
  193. data/spec/rails_app/config/environment.rb +5 -5
  194. data/spec/rails_app/config/environments/development.rb +19 -19
  195. data/spec/rails_app/config/environments/production.rb +33 -33
  196. data/spec/rails_app/config/environments/test.rb +33 -33
  197. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -7
  198. data/spec/rails_app/config/initializers/inflections.rb +2 -2
  199. data/spec/rails_app/config/initializers/secret_token.rb +1 -1
  200. data/spec/rails_app/config/routes.rb +2 -2
  201. data/spec/rails_app/lib/lolita/configuration/field/my_custom_collection.rb +13 -13
  202. data/spec/rails_app/public/javascripts/jquery-1.5.1.min.js +15 -15
  203. data/spec/rails_app/public/javascripts/lolita/main.js +6 -6
  204. data/spec/rails_app/public/javascripts/lolita/tab.js +40 -40
  205. data/spec/rails_app/public/javascripts/modernizr-1.7.min.js +1 -1
  206. data/spec/rails_app/public/javascripts/rails.js +137 -137
  207. data/spec/rails_app/public/javascripts/tinymce/langs/en.js +221 -221
  208. data/spec/rails_app/public/javascripts/tinymce/license.txt +504 -504
  209. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/about.htm +52 -52
  210. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/anchor.htm +26 -26
  211. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/charmap.htm +51 -51
  212. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/color_picker.htm +74 -74
  213. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/editor_template_src.js +1328 -1328
  214. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/image.htm +80 -80
  215. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/js/about.js +73 -73
  216. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/js/anchor.js +42 -42
  217. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/js/charmap.js +354 -354
  218. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/js/color_picker.js +329 -329
  219. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/js/image.js +247 -247
  220. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/js/link.js +153 -153
  221. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/js/source_editor.js +56 -56
  222. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/langs/en.js +68 -68
  223. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/langs/en_dlg.js +53 -53
  224. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/link.htm +57 -57
  225. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/shortcuts.htm +47 -47
  226. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/skins/cirkuit/content.css +66 -66
  227. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/skins/cirkuit/dialog.css +117 -117
  228. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/skins/cirkuit/ui.css +988 -988
  229. data/spec/rails_app/public/javascripts/tinymce/themes/advanced/source_editor.htm +25 -25
  230. data/spec/rails_app/public/javascripts/tinymce/tiny_mce_popup.js +4 -4
  231. data/spec/rails_app/public/stylesheets/lolita/default.css +169 -169
  232. data/spec/rails_app/public/stylesheets/lolita/style.css +214 -214
  233. data/spec/routing/routes_spec.rb +15 -15
  234. data/spec/spec_helper.rb +46 -46
  235. data/spec/support/factories/category.rb +3 -3
  236. data/spec/support/factories/post.rb +4 -4
  237. data/spec/support/factories/tag.rb +2 -2
  238. data/spec/support/formatter_spec.rb +42 -42
  239. metadata +31 -31
@@ -1,13 +1,13 @@
1
- module Lolita
2
- module Controllers
3
- module ViewUserHelpers
4
- def lolita_current_user
5
- if self.respond_to?(:"current_#{Lolita.user_classes.first.to_s.downcase}")
6
- send(:"current_#{Lolita.user_classes.first.to_s.downcase}")
7
- else
8
- false
9
- end
10
- end
11
- end
12
- end
1
+ module Lolita
2
+ module Controllers
3
+ module ViewUserHelpers
4
+ def lolita_current_user
5
+ if self.respond_to?(:"current_#{Lolita.user_classes.first.to_s.downcase}")
6
+ send(:"current_#{Lolita.user_classes.first.to_s.downcase}")
7
+ else
8
+ false
9
+ end
10
+ end
11
+ end
12
+ end
13
13
  end
@@ -1,50 +1,50 @@
1
- module Lolita
2
- module DBI
3
- # Lolita::DBI::Base is DataBase Interface class, that handle the request to ORM classes.
4
- # Depending on given class DBI::Base detect which ORM class is used and include right adapter
5
- # for that class. Other Lolita classes that need to manipulate with data need to have dbi object
6
- # or it can be created in that class.
7
- # Lolita::DBI::Base support Mongoid and ActiveRecord::Base, or details see Lolita::Adapter.
8
- class Base
9
-
10
- attr_reader :adapter_name #return connected adapter name
11
- attr_reader :klass # return related orm class object
12
- attr_reader :adapter # connected Adaptee for adapter
13
- # Expect ORM class that is supported by Lolita. See Adapter for available adapters.
14
- def initialize(class_object)
15
- @klass=class_object
16
- detect_adapter
17
- connect_adapter
18
- end
19
-
20
- # Detect which ORM class is given and based on it connect Adapter.
21
- def detect_adapter
22
- if defined?(Mongoid) && defined?(Mongoid::Document) && self.klass.ancestors.include?(Mongoid::Document)
23
- @adapter_name=:mongoid
24
- elsif defined?(ActiveRecord) && defined?(ActiveRecord::Base) && self.klass.ancestors.include?(ActiveRecord::Base)
25
- @adapter_name=:active_record
26
- else
27
- raise NotORMClassError.new("Lolita::DBI::Base can not find appropriate #{self.klass} class adapter.")
28
- end
29
- end
30
-
31
- # Connect Adapter by including adapter module into DBI::Base class.
32
- def connect_adapter()
33
- @adapter="Lolita::Adapter::#{self.adapter_name.to_s.camelize}".constantize.new(self)
34
- end
35
-
36
- def method_missing(metod,*args,&block)
37
- @adapter.send(metod,*args,&block)
38
- end
39
-
40
- class << self
41
- # Return Array of available adapters.
42
- def adapters
43
- Dir[File.expand_path(File.join(File.dirname(__FILE__),'..','adapter','**','*.rb'))].map {|f|
44
- File.basename(f,".rb").to_sym
45
- }.reject{|el| el==:abstract_adapter}
46
- end
47
- end
48
- end
49
- end
50
- end
1
+ module Lolita
2
+ module DBI
3
+ # Lolita::DBI::Base is DataBase Interface class, that handle the request to ORM classes.
4
+ # Depending on given class DBI::Base detect which ORM class is used and include right adapter
5
+ # for that class. Other Lolita classes that need to manipulate with data need to have dbi object
6
+ # or it can be created in that class.
7
+ # Lolita::DBI::Base support Mongoid and ActiveRecord::Base, or details see Lolita::Adapter.
8
+ class Base
9
+
10
+ attr_reader :adapter_name #return connected adapter name
11
+ attr_reader :klass # return related orm class object
12
+ attr_reader :adapter # connected Adaptee for adapter
13
+ # Expect ORM class that is supported by Lolita. See Adapter for available adapters.
14
+ def initialize(class_object)
15
+ @klass=class_object
16
+ detect_adapter
17
+ connect_adapter
18
+ end
19
+
20
+ # Detect which ORM class is given and based on it connect Adapter.
21
+ def detect_adapter
22
+ if defined?(Mongoid) && defined?(Mongoid::Document) && self.klass.ancestors.include?(Mongoid::Document)
23
+ @adapter_name=:mongoid
24
+ elsif defined?(ActiveRecord) && defined?(ActiveRecord::Base) && self.klass.ancestors.include?(ActiveRecord::Base)
25
+ @adapter_name=:active_record
26
+ else
27
+ raise NotORMClassError.new("Lolita::DBI::Base can not find appropriate #{self.klass} class adapter.")
28
+ end
29
+ end
30
+
31
+ # Connect Adapter by including adapter module into DBI::Base class.
32
+ def connect_adapter()
33
+ @adapter="Lolita::Adapter::#{self.adapter_name.to_s.camelize}".constantize.new(self)
34
+ end
35
+
36
+ def method_missing(metod,*args,&block)
37
+ @adapter.send(metod,*args,&block)
38
+ end
39
+
40
+ class << self
41
+ # Return Array of available adapters.
42
+ def adapters
43
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'..','adapter','**','*.rb'))].map {|f|
44
+ File.basename(f,".rb").to_sym
45
+ }.reject{|el| el==:abstract_adapter}
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
data/lib/lolita/errors.rb CHANGED
@@ -1,13 +1,13 @@
1
- module Lolita
2
- class NotORMClassError < ArgumentError; end
3
- class NoFieldsGivenError < ArgumentError; end
4
- class SameTabTypeError < ArgumentError; end
5
- class ReferenceError < ArgumentError; end
6
- class FieldNameError < NoMethodError; end
7
- class FieldTypeError < ArgumentError; end
8
- class ModuleNotFound < ArgumentError; end
9
- class NotFound < ArgumentError; end
10
- class ConfigurationClassNotFound < NameError; end
11
- class AssociationError < ArgumentError; end
12
- class HookNotFound < ArgumentError;end
1
+ module Lolita
2
+ class NotORMClassError < ArgumentError; end
3
+ class NoFieldsGivenError < ArgumentError; end
4
+ class SameTabTypeError < ArgumentError; end
5
+ class ReferenceError < ArgumentError; end
6
+ class FieldNameError < NoMethodError; end
7
+ class FieldTypeError < ArgumentError; end
8
+ class ModuleNotFound < ArgumentError; end
9
+ class NotFound < ArgumentError; end
10
+ class ConfigurationClassNotFound < NameError; end
11
+ class AssociationError < ArgumentError; end
12
+ class HookNotFound < ArgumentError;end
13
13
  end
data/lib/lolita/hooks.rb CHANGED
@@ -1,356 +1,356 @@
1
- module Lolita
2
- # Provide hook mechanism for Lolita. To use hooks for class start with including this in your own class.
3
- # Next step is hook definition. This may be done using Lolita::Hooks#add_hook method.
4
- # Hooks are stored in class <i>@hooks</i> variable, that is Hash and each key is hook name
5
- # and each hook also is Hash that have <em>:methods</em> and <em>:blocks</em>
6
- # keys. Both of those are Array, and each time you call callback method, like <i>before_save</i> and so on, block
7
- # and/or methods is stored. <b>Each time</b> #run is called all blocks and methods will be executed.
8
- # It may look like this.
9
- # class MyClass
10
- # include Lolita::Hooks
11
- # add_hook :before_save, :after_save
12
- # end
13
- # # This will define two hooks for MyClass.
14
- # To add hook callback just call hook name on class and pass method(-s) or block.
15
- # MyClass.after_save :write_log
16
- # MyClass.before_save do
17
- # validate(self)
18
- # end
19
- # ==Scopes
20
- # Most times hook callbacks are defined for class like in previous example, but also it's possible to do it
21
- # on class instances. Difference between calling it on class or on instance is that instance callbacks will
22
- # be called only when event is runned on instance. Class callbacks will be called on class and also on instance
23
- # callbacks.
24
- # my_object=MyClass.new
25
- # MyClass.before_save do
26
- # puts "class callback"
27
- # end
28
- # my_object.before_save do
29
- # puts "instance callback"
30
- # end
31
- #
32
- # MyClass.run(:before_save) #=>
33
- # class_callback
34
- #
35
- # my_object.run(:before_save) #=>
36
- # class_callback
37
- # instance_callback
38
- # # As you can see, first class callbacks is called and after that instance callbacks.
39
- #
40
- # ==Firing events
41
- # To execute callbacks, events should be called on object. Event names is same hooks names. #run can be called
42
- # on class or on instance. Also it is possible to pass block to run event, that will replace callback block
43
- # or if #let_content is called than it will work like wrapper, like this
44
- # # this is continuation of previous code
45
- # MyClass.run(:before_save) do
46
- # puts "replaced text"
47
- # end
48
- # # will produce #=> replaced text
49
- #
50
- # MyClass.run(:before_save) do
51
- # puts "before callback"
52
- # let_content
53
- # puts "after callback"
54
- # end
55
- # # this will produce #=>
56
- # # before callback
57
- # # class callback
58
- # # after callback
59
- # ==Named hooks
60
- # See Lolita::Hooks::NamedHook for details.
61
- module Hooks
62
- def self.included(base)
63
- base.extend(ClassMethods)
64
- base.extend(CommonMethods)
65
- base.class_eval{
66
- include CommonMethods
67
- include InstanceMethods
68
- }
69
- end
70
-
71
- # Look for named hook with singular or plural name of method.
72
- def self.method_missing method_name,*args, &block
73
- if named_hook=(Lolita::Hooks::NamedHook.by_name(method_name))
74
- named_hook[:_class]
75
- else
76
- super
77
- end
78
- end
79
-
80
- # Shared methods between class and instance.
81
- module CommonMethods
82
-
83
- # All callbacks for class or instance.
84
- def callbacks
85
- var=self.instance_variable_get(:@callbacks)
86
- unless var
87
- var={}
88
- self.instance_variable_set(:@callbacks,var)
89
- end
90
- instance_variable_get(:@callbacks)
91
- end
92
- end
93
-
94
- module ClassMethods
95
- attr_accessor :hooks_run_scope
96
-
97
- # Setter for #hook_scope.
98
- def hooks_scope=(object)
99
- @hooks_scope=object
100
- end
101
-
102
- # Hooks scope is used to execute callbacks. By default it is class itself.
103
- def hooks_scope
104
- @hooks_scope||self
105
- end
106
-
107
- # Setter for #callback_content
108
- def given_callback_content=(content)
109
- @given_callback_content=content
110
- end
111
-
112
- # Callback content is used to let callback content executed insede of run block.
113
- def given_callback_content
114
- @given_callback_content
115
- end
116
-
117
- # All hooks for class. This is Array of hook names.
118
- def hooks
119
- @hooks||=[]
120
- @hooks
121
- end
122
-
123
- def all_hooks
124
- @all_hooks||=self.ancestors.inject([]) do |result,const_name|
125
- if const_name.respond_to?(:hooks)
126
- result+=const_name.send(:hooks)
127
- else
128
- result
129
- end
130
- end
131
- @all_hooks
132
- end
133
-
134
- # Reset all hooks and callbacks to defaults.
135
- def clear_hooks
136
- @hooks=[]
137
- @callbacks={}
138
- end
139
-
140
- def add_hooks *names
141
- add_hook *names
142
- end
143
-
144
- # This method is used to add hooks for class. It accept one or more hook names.
145
- # ====Example
146
- # add_hook :before_save
147
- # MyClass.add_hooks :after_save, :around_save
148
- def add_hook(*names)
149
- (names||[]).each{|hook_name|
150
- self.class_eval <<-HOOK,__FILE__,__LINE__+1
151
- def self.#{hook_name}(*methods,&block)
152
- options=methods.extract_options!
153
- in_hooks_scope(options[:scope]) do
154
- register_callback(:"#{hook_name}",*methods,&block)
155
- end
156
- end
157
-
158
- def #{hook_name}(*method,&block)
159
- self.class.#{hook_name}(*method,:scope=>self,&block)
160
- end
161
- HOOK
162
- register_hook(hook_name)
163
- }
164
- end
165
-
166
- # run is used to execute callback. Method accept one or more <i>hook_names</i> and optional block.
167
- # It will raise error if hook don't exist for this class. Also it accept <em>:scope</em> options, that
168
- # is used to #get_callbacks and #run_callbacks.
169
- # ====Example
170
- # MyClass.run(:before_save,:after_save,:scope=>MyClass.new)
171
- # # this will call callbacks in MyClass instance scope, that means that self will be MyClass instance.
172
- def run(hook_name,*args,&block)
173
-
174
- result=nil
175
- options=args ? args.extract_options! : {}
176
-
177
- raise Lolita::HookNotFound, "Hook #{hook_name} is not defined for #{self}." unless self.has_hook?(hook_name)
178
- in_hooks_scope(options[:scope],options[:run_scope]) do
179
- callback=get_callback(hook_name)
180
- result=run_callback(callback,&block)
181
- end
182
-
183
- result
184
- end
185
-
186
- # Is hook with <em>name</em> is defined for class.
187
- def has_hook?(name)
188
- self.all_hooks.include?(name.to_sym)
189
- end
190
-
191
- # Try to recognize named run methods like
192
- # MyClass.run_after_save # will call MyClass.run(:after_save)
193
- def method_missing(*args, &block)
194
- unless self.recognize_hook_methods(*args,&block)
195
- super
196
- end
197
- end
198
-
199
- # Call callback block inside of run block.
200
- # ====Example
201
- # MyClass.run(:before_save) do
202
- # do_stuff
203
- # let_content # execute callback block(-s) in same scope as run is executed.
204
- # end
205
- def let_content
206
- if self.given_callback_content.respond_to?(:call)
207
- run_block(self.given_callback_content)
208
- elsif self.given_callback_content
209
- self.given_callback_content
210
- end
211
- end
212
-
213
- # Set #method_missing
214
- def recognize_hook_methods method_name, *args, &block
215
- if method_name.to_s.match(/^run_(\w+)/)
216
- self.run($1,*args,&block)
217
- true
218
- end
219
- end
220
-
221
- protected
222
-
223
- # Switch between self and given <em>scope</em>. Block will be executed with <em>scope</em>.
224
- # And after that it will switch back to self.
225
- def in_hooks_scope(scope,run_scope=nil)
226
- begin
227
- self.hooks_scope=scope||self
228
- if run_scope
229
- run_scope.define_singleton_method(:let_content) do
230
- scope.let_content
231
- end
232
- end
233
- self.hooks_run_scope=run_scope || self.hooks_scope
234
- yield
235
- ensure
236
- self.hooks_scope=self
237
- self.hooks_run_scope=self.hooks_scope
238
- end
239
- end
240
-
241
- # Run callback. Each callback is Hash with <i>:methods</i> Array and </i>:blocks</i> Array
242
- def run_callback(callback,&block)
243
- method_results=run_methods(callback[:methods],&block)
244
- block_results=run_blocks(callback[:blocks],&block)
245
- method_results+block_results
246
- end
247
-
248
- # Run methods from <em>methods</em> Array
249
- def run_methods methods, &block
250
- result=""
251
- (methods||[]).each do |method_name|
252
- result << (hooks_run_scope.__send__(method_name,&block)).to_s
253
- end
254
- result
255
- end
256
-
257
- # Run blocks from <em>blocks</em> Array. Also it set #given_callback_content if block is given, this
258
- # will allow to call #let_content. Each block is runned with #run_block.
259
- # After first run result of first block become #given_callback_content, and when next block
260
- # call #let_content, this string will be returned for that block
261
- def run_blocks blocks,&given_block
262
- result=""
263
-
264
- self.given_callback_content=block_given? ? given_block : nil
265
-
266
- if blocks && !blocks.empty?
267
- blocks.each do |block|
268
- result << (run_block(block,&given_block)).to_s
269
- self.given_callback_content=result
270
- end
271
- elsif block_given?
272
- self.given_callback_content=nil
273
- result << run_block(given_block).to_s
274
- end
275
- result
276
- end
277
-
278
- # Run block in scope.
279
- def run_block block, &given_block
280
- hooks_run_scope.instance_eval(&block)
281
- end
282
-
283
- # Return all callbacks
284
- # If scope is not class then it merge class callbacks with scope callbacks. That means that
285
- # class callbacks always will be called before scope callbacks.
286
- def get_callback(name)
287
- scope_callbacks=hooks_scope.callbacks[name.to_sym] || {}
288
-
289
- superclasses.each do |const_name|
290
- scope_callbacks=collect_callbacks_from(name,const_name,scope_callbacks)
291
- end
292
- scope_callbacks
293
- end
294
-
295
-
296
- def collect_callbacks_from(name,const_name,scope_callbacks)
297
- class_callbacks=const_name.callbacks[name.to_sym] || {}
298
- [:methods,:blocks].each do |attr|
299
- scope_callbacks[attr]=((class_callbacks[attr] || [])+(scope_callbacks[attr] || [])).uniq
300
- end
301
- scope_callbacks
302
- end
303
-
304
- # Register callback with given scope.
305
- def register_callback(name,*methods,&block)
306
- temp_callback=hooks_scope.callbacks[name]||{}
307
- temp_callback[:methods]||=[]
308
- temp_callback[:methods]+=(methods||[]).compact
309
- temp_callback[:blocks]||=[]
310
- temp_callback[:blocks]<< block if block_given?
311
- hooks_scope.callbacks[name]=temp_callback
312
- end
313
-
314
- # Register hook for scope.
315
- def register_hook(name)
316
- self.hooks<<name
317
- end
318
-
319
- def superclasses
320
- unless @klasses
321
- @klasses=[]
322
- self.ancestors.each do |const_name|
323
- if const_name.respond_to?(:hooks)
324
- @klasses<<const_name
325
- end
326
- end
327
- end
328
- @klasses
329
- end
330
- end
331
-
332
- # Methods for instance.
333
- module InstanceMethods
334
-
335
- # See Lolita::Hooks::ClassMethods#run
336
- def run(*hook_names,&block)
337
- options=hook_names ? hook_names.extract_options! : {}
338
- options[:scope]=self
339
- self.class.run(*hook_names,options,&block)
340
- end
341
-
342
- # See Lolita::Hooks::ClassMethods#let_content
343
- def let_content
344
- self.class.let_content
345
- end
346
-
347
- # See Lolita::Hooks::ClassMethods#method_missing
348
- def method_missing(*args,&block)
349
- unless self.class.recognize_hook_methods(*args,:scope=>self,&block)
350
- super
351
- end
352
- end
353
- end
354
-
355
- end
1
+ module Lolita
2
+ # Provide hook mechanism for Lolita. To use hooks for class start with including this in your own class.
3
+ # Next step is hook definition. This may be done using Lolita::Hooks#add_hook method.
4
+ # Hooks are stored in class <i>@hooks</i> variable, that is Hash and each key is hook name
5
+ # and each hook also is Hash that have <em>:methods</em> and <em>:blocks</em>
6
+ # keys. Both of those are Array, and each time you call callback method, like <i>before_save</i> and so on, block
7
+ # and/or methods is stored. <b>Each time</b> #run is called all blocks and methods will be executed.
8
+ # It may look like this.
9
+ # class MyClass
10
+ # include Lolita::Hooks
11
+ # add_hook :before_save, :after_save
12
+ # end
13
+ # # This will define two hooks for MyClass.
14
+ # To add hook callback just call hook name on class and pass method(-s) or block.
15
+ # MyClass.after_save :write_log
16
+ # MyClass.before_save do
17
+ # validate(self)
18
+ # end
19
+ # ==Scopes
20
+ # Most times hook callbacks are defined for class like in previous example, but also it's possible to do it
21
+ # on class instances. Difference between calling it on class or on instance is that instance callbacks will
22
+ # be called only when event is runned on instance. Class callbacks will be called on class and also on instance
23
+ # callbacks.
24
+ # my_object=MyClass.new
25
+ # MyClass.before_save do
26
+ # puts "class callback"
27
+ # end
28
+ # my_object.before_save do
29
+ # puts "instance callback"
30
+ # end
31
+ #
32
+ # MyClass.run(:before_save) #=>
33
+ # class_callback
34
+ #
35
+ # my_object.run(:before_save) #=>
36
+ # class_callback
37
+ # instance_callback
38
+ # # As you can see, first class callbacks is called and after that instance callbacks.
39
+ #
40
+ # ==Firing events
41
+ # To execute callbacks, events should be called on object. Event names is same hooks names. #run can be called
42
+ # on class or on instance. Also it is possible to pass block to run event, that will replace callback block
43
+ # or if #let_content is called than it will work like wrapper, like this
44
+ # # this is continuation of previous code
45
+ # MyClass.run(:before_save) do
46
+ # puts "replaced text"
47
+ # end
48
+ # # will produce #=> replaced text
49
+ #
50
+ # MyClass.run(:before_save) do
51
+ # puts "before callback"
52
+ # let_content
53
+ # puts "after callback"
54
+ # end
55
+ # # this will produce #=>
56
+ # # before callback
57
+ # # class callback
58
+ # # after callback
59
+ # ==Named hooks
60
+ # See Lolita::Hooks::NamedHook for details.
61
+ module Hooks
62
+ def self.included(base)
63
+ base.extend(ClassMethods)
64
+ base.extend(CommonMethods)
65
+ base.class_eval{
66
+ include CommonMethods
67
+ include InstanceMethods
68
+ }
69
+ end
70
+
71
+ # Look for named hook with singular or plural name of method.
72
+ def self.method_missing method_name,*args, &block
73
+ if named_hook=(Lolita::Hooks::NamedHook.by_name(method_name))
74
+ named_hook[:_class]
75
+ else
76
+ super
77
+ end
78
+ end
79
+
80
+ # Shared methods between class and instance.
81
+ module CommonMethods
82
+
83
+ # All callbacks for class or instance.
84
+ def callbacks
85
+ var=self.instance_variable_get(:@callbacks)
86
+ unless var
87
+ var={}
88
+ self.instance_variable_set(:@callbacks,var)
89
+ end
90
+ instance_variable_get(:@callbacks)
91
+ end
92
+ end
93
+
94
+ module ClassMethods
95
+ attr_accessor :hooks_run_scope
96
+
97
+ # Setter for #hook_scope.
98
+ def hooks_scope=(object)
99
+ @hooks_scope=object
100
+ end
101
+
102
+ # Hooks scope is used to execute callbacks. By default it is class itself.
103
+ def hooks_scope
104
+ @hooks_scope||self
105
+ end
106
+
107
+ # Setter for #callback_content
108
+ def given_callback_content=(content)
109
+ @given_callback_content=content
110
+ end
111
+
112
+ # Callback content is used to let callback content executed insede of run block.
113
+ def given_callback_content
114
+ @given_callback_content
115
+ end
116
+
117
+ # All hooks for class. This is Array of hook names.
118
+ def hooks
119
+ @hooks||=[]
120
+ @hooks
121
+ end
122
+
123
+ def all_hooks
124
+ @all_hooks||=self.ancestors.inject([]) do |result,const_name|
125
+ if const_name.respond_to?(:hooks)
126
+ result+=const_name.send(:hooks)
127
+ else
128
+ result
129
+ end
130
+ end
131
+ @all_hooks
132
+ end
133
+
134
+ # Reset all hooks and callbacks to defaults.
135
+ def clear_hooks
136
+ @hooks=[]
137
+ @callbacks={}
138
+ end
139
+
140
+ def add_hooks *names
141
+ add_hook *names
142
+ end
143
+
144
+ # This method is used to add hooks for class. It accept one or more hook names.
145
+ # ====Example
146
+ # add_hook :before_save
147
+ # MyClass.add_hooks :after_save, :around_save
148
+ def add_hook(*names)
149
+ (names||[]).each{|hook_name|
150
+ self.class_eval <<-HOOK,__FILE__,__LINE__+1
151
+ def self.#{hook_name}(*methods,&block)
152
+ options=methods.extract_options!
153
+ in_hooks_scope(options[:scope]) do
154
+ register_callback(:"#{hook_name}",*methods,&block)
155
+ end
156
+ end
157
+
158
+ def #{hook_name}(*method,&block)
159
+ self.class.#{hook_name}(*method,:scope=>self,&block)
160
+ end
161
+ HOOK
162
+ register_hook(hook_name)
163
+ }
164
+ end
165
+
166
+ # run is used to execute callback. Method accept one or more <i>hook_names</i> and optional block.
167
+ # It will raise error if hook don't exist for this class. Also it accept <em>:scope</em> options, that
168
+ # is used to #get_callbacks and #run_callbacks.
169
+ # ====Example
170
+ # MyClass.run(:before_save,:after_save,:scope=>MyClass.new)
171
+ # # this will call callbacks in MyClass instance scope, that means that self will be MyClass instance.
172
+ def run(hook_name,*args,&block)
173
+
174
+ result=nil
175
+ options=args ? args.extract_options! : {}
176
+
177
+ raise Lolita::HookNotFound, "Hook #{hook_name} is not defined for #{self}." unless self.has_hook?(hook_name)
178
+ in_hooks_scope(options[:scope],options[:run_scope]) do
179
+ callback=get_callback(hook_name)
180
+ result=run_callback(callback,&block)
181
+ end
182
+
183
+ result
184
+ end
185
+
186
+ # Is hook with <em>name</em> is defined for class.
187
+ def has_hook?(name)
188
+ self.all_hooks.include?(name.to_sym)
189
+ end
190
+
191
+ # Try to recognize named run methods like
192
+ # MyClass.run_after_save # will call MyClass.run(:after_save)
193
+ def method_missing(*args, &block)
194
+ unless self.recognize_hook_methods(*args,&block)
195
+ super
196
+ end
197
+ end
198
+
199
+ # Call callback block inside of run block.
200
+ # ====Example
201
+ # MyClass.run(:before_save) do
202
+ # do_stuff
203
+ # let_content # execute callback block(-s) in same scope as run is executed.
204
+ # end
205
+ def let_content
206
+ if self.given_callback_content.respond_to?(:call)
207
+ run_block(self.given_callback_content)
208
+ elsif self.given_callback_content
209
+ self.given_callback_content
210
+ end
211
+ end
212
+
213
+ # Set #method_missing
214
+ def recognize_hook_methods method_name, *args, &block
215
+ if method_name.to_s.match(/^run_(\w+)/)
216
+ self.run($1,*args,&block)
217
+ true
218
+ end
219
+ end
220
+
221
+ protected
222
+
223
+ # Switch between self and given <em>scope</em>. Block will be executed with <em>scope</em>.
224
+ # And after that it will switch back to self.
225
+ def in_hooks_scope(scope,run_scope=nil)
226
+ begin
227
+ self.hooks_scope=scope||self
228
+ if run_scope
229
+ run_scope.define_singleton_method(:let_content) do
230
+ scope.let_content
231
+ end
232
+ end
233
+ self.hooks_run_scope=run_scope || self.hooks_scope
234
+ yield
235
+ ensure
236
+ self.hooks_scope=self
237
+ self.hooks_run_scope=self.hooks_scope
238
+ end
239
+ end
240
+
241
+ # Run callback. Each callback is Hash with <i>:methods</i> Array and </i>:blocks</i> Array
242
+ def run_callback(callback,&block)
243
+ method_results=run_methods(callback[:methods],&block)
244
+ block_results=run_blocks(callback[:blocks],&block)
245
+ method_results+block_results
246
+ end
247
+
248
+ # Run methods from <em>methods</em> Array
249
+ def run_methods methods, &block
250
+ result=""
251
+ (methods||[]).each do |method_name|
252
+ result << (hooks_run_scope.__send__(method_name,&block)).to_s
253
+ end
254
+ result
255
+ end
256
+
257
+ # Run blocks from <em>blocks</em> Array. Also it set #given_callback_content if block is given, this
258
+ # will allow to call #let_content. Each block is runned with #run_block.
259
+ # After first run result of first block become #given_callback_content, and when next block
260
+ # call #let_content, this string will be returned for that block
261
+ def run_blocks blocks,&given_block
262
+ result=""
263
+
264
+ self.given_callback_content=block_given? ? given_block : nil
265
+
266
+ if blocks && !blocks.empty?
267
+ blocks.each do |block|
268
+ result << (run_block(block,&given_block)).to_s
269
+ self.given_callback_content=result
270
+ end
271
+ elsif block_given?
272
+ self.given_callback_content=nil
273
+ result << run_block(given_block).to_s
274
+ end
275
+ result
276
+ end
277
+
278
+ # Run block in scope.
279
+ def run_block block, &given_block
280
+ hooks_run_scope.instance_eval(&block)
281
+ end
282
+
283
+ # Return all callbacks
284
+ # If scope is not class then it merge class callbacks with scope callbacks. That means that
285
+ # class callbacks always will be called before scope callbacks.
286
+ def get_callback(name)
287
+ scope_callbacks=hooks_scope.callbacks[name.to_sym] || {}
288
+
289
+ superclasses.each do |const_name|
290
+ scope_callbacks=collect_callbacks_from(name,const_name,scope_callbacks)
291
+ end
292
+ scope_callbacks
293
+ end
294
+
295
+
296
+ def collect_callbacks_from(name,const_name,scope_callbacks)
297
+ class_callbacks=const_name.callbacks[name.to_sym] || {}
298
+ [:methods,:blocks].each do |attr|
299
+ scope_callbacks[attr]=((class_callbacks[attr] || [])+(scope_callbacks[attr] || [])).uniq
300
+ end
301
+ scope_callbacks
302
+ end
303
+
304
+ # Register callback with given scope.
305
+ def register_callback(name,*methods,&block)
306
+ temp_callback=hooks_scope.callbacks[name]||{}
307
+ temp_callback[:methods]||=[]
308
+ temp_callback[:methods]+=(methods||[]).compact
309
+ temp_callback[:blocks]||=[]
310
+ temp_callback[:blocks]<< block if block_given?
311
+ hooks_scope.callbacks[name]=temp_callback
312
+ end
313
+
314
+ # Register hook for scope.
315
+ def register_hook(name)
316
+ self.hooks<<name
317
+ end
318
+
319
+ def superclasses
320
+ unless @klasses
321
+ @klasses=[]
322
+ self.ancestors.each do |const_name|
323
+ if const_name.respond_to?(:hooks)
324
+ @klasses<<const_name
325
+ end
326
+ end
327
+ end
328
+ @klasses
329
+ end
330
+ end
331
+
332
+ # Methods for instance.
333
+ module InstanceMethods
334
+
335
+ # See Lolita::Hooks::ClassMethods#run
336
+ def run(*hook_names,&block)
337
+ options=hook_names ? hook_names.extract_options! : {}
338
+ options[:scope]=self
339
+ self.class.run(*hook_names,options,&block)
340
+ end
341
+
342
+ # See Lolita::Hooks::ClassMethods#let_content
343
+ def let_content
344
+ self.class.let_content
345
+ end
346
+
347
+ # See Lolita::Hooks::ClassMethods#method_missing
348
+ def method_missing(*args,&block)
349
+ unless self.class.recognize_hook_methods(*args,:scope=>self,&block)
350
+ super
351
+ end
352
+ end
353
+ end
354
+
355
+ end
356
356
  end