hobo 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. data/hobo_files/plugin/CHANGES.txt +220 -23
  2. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +18 -25
  3. data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +20 -15
  4. data/hobo_files/plugin/generators/hobo_model/templates/model.rb +3 -3
  5. data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +3 -3
  6. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.css +1 -2
  7. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.js +21 -4
  8. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/images/fieldbg.gif +0 -0
  9. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/images/spinner.gif +0 -0
  10. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css +154 -26
  11. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +144 -0
  12. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +1 -1
  13. data/hobo_files/plugin/generators/hobo_user_controller/templates/controller.rb +1 -1
  14. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +8 -11
  15. data/hobo_files/plugin/init.rb +0 -2
  16. data/hobo_files/plugin/lib/active_record/has_many_association.rb +0 -9
  17. data/hobo_files/plugin/lib/active_record/has_many_through_association.rb +0 -10
  18. data/hobo_files/plugin/lib/hobo.rb +57 -44
  19. data/hobo_files/plugin/lib/hobo/bundle.rb +222 -0
  20. data/hobo_files/plugin/lib/hobo/controller.rb +2 -5
  21. data/hobo_files/plugin/lib/hobo/dryml.rb +8 -7
  22. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +10 -21
  23. data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +107 -80
  24. data/hobo_files/plugin/lib/hobo/dryml/template.rb +27 -20
  25. data/hobo_files/plugin/lib/hobo/enum_string.rb +1 -1
  26. data/hobo_files/plugin/lib/hobo/field_declaration_dsl.rb +7 -0
  27. data/hobo_files/plugin/lib/hobo/guest.rb +4 -0
  28. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +37 -9
  29. data/hobo_files/plugin/lib/hobo/model.rb +79 -17
  30. data/hobo_files/plugin/lib/hobo/model_controller.rb +59 -60
  31. data/hobo_files/plugin/lib/hobo/model_router.rb +16 -4
  32. data/hobo_files/plugin/lib/hobo/rapid_helper.rb +2 -1
  33. data/hobo_files/plugin/lib/hobo/user.rb +10 -7
  34. data/hobo_files/plugin/lib/hobo/user_controller.rb +6 -6
  35. data/hobo_files/plugin/{tags → taglibs}/core.dryml +5 -4
  36. data/hobo_files/plugin/{tags → taglibs}/rapid.dryml +54 -7
  37. data/hobo_files/plugin/{tags → taglibs}/rapid_document_tags.dryml +0 -0
  38. data/hobo_files/plugin/{tags → taglibs}/rapid_editing.dryml +4 -2
  39. data/hobo_files/plugin/{tags → taglibs}/rapid_forms.dryml +1 -4
  40. data/hobo_files/plugin/{tags → taglibs}/rapid_navigation.dryml +1 -2
  41. data/hobo_files/plugin/taglibs/rapid_pages.dryml +411 -0
  42. data/hobo_files/plugin/{tags → taglibs}/rapid_plus.dryml +0 -0
  43. data/hobo_files/plugin/{tags → taglibs}/rapid_support.dryml +9 -6
  44. data/hobo_files/plugin/tasks/fix_dryml.rake +0 -1
  45. metadata +16 -14
  46. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid_ui.css +0 -167
  47. data/hobo_files/plugin/lib/hobo/plugins.rb +0 -75
  48. data/hobo_files/plugin/tags/rapid_pages.dryml +0 -341
@@ -17,8 +17,6 @@ require 'hobo/dryml/taglib'
17
17
  require 'hobo/dryml/template_environment'
18
18
  require 'hobo/dryml/template_handler'
19
19
 
20
- require 'hobo/plugins'
21
-
22
20
  require 'extensions/test_case' if RAILS_ENV == "test"
23
21
 
24
22
  # Rich data types
@@ -24,15 +24,6 @@ module ActiveRecord::Associations
24
24
  end
25
25
 
26
26
 
27
- def include?(record)
28
- if loaded?
29
- target.include?(record)
30
- else
31
- (r = find(record.id)) && r == record rescue false
32
- end
33
- end
34
-
35
-
36
27
  def member_class
37
28
  proxy_reflection.klass
38
29
  end
@@ -2,16 +2,6 @@ module ActiveRecord::Associations
2
2
 
3
3
  class HasManyThroughAssociation
4
4
 
5
- def include?(record)
6
- return false unless record.is_a? ActiveRecord::Base
7
-
8
- if loaded?
9
- target.include?(record)
10
- else
11
- !!find_by_id(record.id)
12
- end
13
- end
14
-
15
5
  def member_class
16
6
  proxy_reflection.klass
17
7
  end
@@ -47,7 +47,7 @@ module Hobo
47
47
 
48
48
  def models
49
49
  unless @models_loaded
50
- Dir.entries("#{RAILS_ROOT}/app/models/").map do |f|
50
+ Dir.entries("#{RAILS_ROOT}/app/models/").each do |f|
51
51
  f =~ /^[a-zA-Z_][a-zA-Z0-9_]*\.rb$/ and f.sub(/.rb$/, '').camelize.constantize
52
52
  end
53
53
  @models_loaded = true
@@ -111,7 +111,8 @@ module Hobo
111
111
 
112
112
  def find_by_search(query)
113
113
  sql = Hobo.models.map do |model|
114
- if model.superclass == ActiveRecord::Base # filter out STI subclasses
114
+ if model.superclass == ActiveRecord::Base && # filter out STI subclasses
115
+ ModelRouter.linkable?(nil, model, :show) # filter out non-linkables
115
116
  cols = model.search_columns
116
117
  if cols.blank?
117
118
  nil
@@ -208,54 +209,66 @@ module Hobo
208
209
 
209
210
 
210
211
  def can_edit?(person, object, field)
211
- setter = "#{field.to_s.sub /\?$/, ''}="
212
- return false unless can_view?(person, object, field) and object.respond_to?(setter)
212
+ # Can't view implies can't edit
213
+ return false if !can_view?(person, object, field)
213
214
 
214
- refl = object.class.reflections[field.to_sym] if object.is_a?(ActiveRecord::Base)
215
-
216
- # has_one and polymorphic associations are not editable (for now)
217
- return false if refl and (refl.options[:polymorphic] or refl.macro == :has_one)
218
-
219
- if object.has_hobo_method?(:editable_by?)
220
- check_permission(:edit, person, object, field.to_sym)
221
- elsif object.has_hobo_method?("#{field}_editable_by?")
222
- object.send("#{field}_editable_by?", person)
215
+ if field.nil?
216
+ if respond_to?(:editable_by?)
217
+ object.editable_by?(person)
218
+ else
219
+ object.updatable_by?(person, nil)
220
+ end
221
+
223
222
  else
224
- # Fake an edit test by setting the field in question to
225
- # Hobo::Undefined and then testing for update permission
223
+ setter = "#{field.to_s.sub /\?$/, ''}="
224
+ return false if !object.respond_to?(setter)
226
225
 
227
- # This technique is not suitable for has_many associations
228
- return false if refl._?.macro == :has_many
226
+ refl = object.class.reflections[field.to_sym] if object.is_a?(ActiveRecord::Base)
229
227
 
230
- current = object.send(field)
231
- new = object.duplicate
232
-
233
- begin
234
- # Setting the undefined value can sometimes result in an
235
- # UndefinedAccessError. In that case we have no choice but
236
- # return false.
237
- new.send(setter, if current == true
238
- false
239
- elsif current == false
240
- true
241
- elsif refl and refl.macro == :belongs_to
242
- Hobo::Undefined.new(refl.klass)
243
- else
244
- Hobo::Undefined.new
245
- end)
246
- rescue Hobo::UndefinedAccessError
247
- raise HoboError, ("#{object.class.name}##{field} does not support undefined assignements, " +
248
- "define editable_by?(user, field)")
249
- end
228
+ # has_one and polymorphic associations are not editable (for now)
229
+ return false if refl and (refl.options[:polymorphic] or refl.macro == :has_one)
250
230
 
251
- begin
252
- if object.new_record?
253
- check_permission(:create, person, new)
254
- else
255
- check_permission(:update, person, object, new)
231
+ if object.has_hobo_method?("#{field}_editable_by?")
232
+ object.send("#{field}_editable_by?", person)
233
+ elsif object.has_hobo_method?(:editable_by?)
234
+ check_permission(:edit, person, object)
235
+ else
236
+ # Fake an edit test by setting the field in question to
237
+ # Hobo::Undefined and then testing for update permission
238
+
239
+ # This technique is not suitable for has_many associations
240
+ return false if refl._?.macro == :has_many
241
+
242
+ current = object.send(field)
243
+ new = object.duplicate
244
+
245
+ begin
246
+ # Setting the undefined value can sometimes result in an
247
+ # UndefinedAccessError. In that case we have no choice but
248
+ # return false.
249
+ new.send(setter, if current == true
250
+ false
251
+ elsif current == false
252
+ true
253
+ elsif refl and refl.macro == :belongs_to
254
+ Hobo::Undefined.new(refl.klass)
255
+ else
256
+ Hobo::Undefined.new
257
+ end)
258
+ rescue Hobo::UndefinedAccessError
259
+ raise HoboError, ("#{object.class.name}##{field} does not support undefined assignements, " +
260
+ "define editable_by?(user, field)")
261
+ end
262
+
263
+ begin
264
+ if object.new_record?
265
+ check_permission(:create, person, new)
266
+ else
267
+ check_permission(:update, person, object, new)
268
+ end
269
+ rescue Hobo::UndefinedAccessError
270
+ false
256
271
  end
257
- rescue Hobo::UndefinedAccessError
258
- false
259
272
  end
260
273
  end
261
274
  end
@@ -0,0 +1,222 @@
1
+ require 'extensions'
2
+
3
+ module ::Hobo
4
+
5
+ class Bundle
6
+
7
+ @bundles = HashWithIndifferentAccess.new
8
+
9
+ class << self
10
+
11
+ attr_accessor :bundles, :plugin
12
+
13
+ def inherited(base)
14
+ filename = caller[0].match(/^(.*):\d+/)[1]
15
+ dirname = filename.match(%r(^.*/plugins/[^/]+))[0]
16
+ base.plugin = File.basename(dirname)
17
+
18
+ base.meta_eval do
19
+ attr_accessor :models, :controllers
20
+ end
21
+
22
+ base.models = []
23
+ base.controllers = []
24
+
25
+ eval_ruby_files(base, "#{dirname}/models")
26
+ eval_ruby_files(base, "#{dirname}/controllers")
27
+ end
28
+
29
+
30
+ def bundle_model(name, &block)
31
+ models << [name, block]
32
+ end
33
+
34
+
35
+ def bundle_model_controller(model_name, &block)
36
+ controllers << [model_name, block]
37
+ end
38
+
39
+
40
+ private
41
+
42
+ def eval_ruby_files(base, dir)
43
+ Dir["#{dir}/*.rb"].each do |f|
44
+ base.instance_eval(File.read(f), f, 1)
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ def initialize(*args)
51
+ options = defaults.with_indifferent_access
52
+ options.update(args.extract_options!)
53
+
54
+ self.name = args.first || self.class.name.match(/[^:]+$/)[0].underscore
55
+ Bundle.bundles[name] = self
56
+
57
+ @renames, @options = separate_renames(options)
58
+
59
+ includes
60
+
61
+ create_models
62
+ create_controllers
63
+
64
+ init
65
+ end
66
+
67
+ attr_accessor :renames, :options, :name
68
+
69
+ # optionally overridden by the bundle subclass
70
+ def includes; end
71
+ def init; end
72
+ def defaults; {}; end
73
+
74
+
75
+ def plugin
76
+ self.class.plugin
77
+ end
78
+
79
+
80
+ def create_models
81
+ self.class.models.each do |name, block|
82
+ klass = make_class(new_name_for(name), ActiveRecord::Base) do
83
+ hobo_model
84
+ end
85
+ klass.class_eval(&block)
86
+ end
87
+ end
88
+
89
+
90
+ def create_controllers
91
+ bundle = self
92
+ self.class.controllers.each do |model_name, block|
93
+ klass = make_class("#{new_name_for(model_name).to_s.pluralize}Controller", ApplicationController) do
94
+ hobo_model_controller
95
+ end
96
+ klass.class_eval(&block)
97
+ end
98
+ end
99
+
100
+
101
+ def make_class(name, base_class, &b)
102
+ bundle = self
103
+ klass = Class.new(base_class) do
104
+ # Nasty hack because blocks can't take blocks
105
+ # Roll on Ruby 1.9
106
+ def self.feature(name, &block)
107
+ _feature(name, block)
108
+ end
109
+ end
110
+
111
+ klass.meta_def(:bundle) do
112
+ bundle
113
+ end
114
+
115
+ klass.meta_def(:_feature) do |feature, block|
116
+ has_feature = bundle.options[feature]
117
+ if has_feature
118
+ define_method("features_#{feature}?") { true }
119
+ meta_def("features_#{feature}?") { true }
120
+ block.call if block
121
+ else
122
+ define_method("features_#{feature}?") { false }
123
+ meta_def("features_#{feature}?") { false }
124
+ end
125
+ end
126
+ silence_warnings { Object.const_set(name, klass) }
127
+
128
+ klass.meta_def :method_missing do |name, *args|
129
+ if name.to_s =~ /^_.*_$/
130
+ bundle.magic_option(name)
131
+ else
132
+ super
133
+ end
134
+ end
135
+
136
+ klass.class_eval(&b) if b
137
+ klass
138
+ end
139
+
140
+
141
+ def new_name_for(name)
142
+ while renames.has_key?(name)
143
+ name = renames[name]
144
+ name2 = name.to_s.gsub(/_.*?_/) { |s| new_name_for(s[1..-2]) }
145
+
146
+ # Make sure symbols stay symbols
147
+ name = name.is_a?(Symbol) ? name2.to_sym : name2
148
+ end
149
+ name
150
+ end
151
+
152
+
153
+ def separate_renames(options)
154
+ simple_options, renames = HashWithIndifferentAccess.new, HashWithIndifferentAccess.new
155
+ options.each do |k, v|
156
+ if k.to_s =~ /^[A-Z]/
157
+ renames[k] = v.to_s
158
+ renames[k.to_s.underscore] = v.to_s.underscore.to_sym
159
+ renames[k.to_s.underscore.pluralize] = v.to_s.underscore.pluralize.to_sym
160
+ else
161
+ simple_options[k] = v
162
+ end
163
+ end
164
+ [renames, simple_options]
165
+ end
166
+
167
+
168
+ def customize(name, &block)
169
+ new_name_for(name).to_s.constantize.class_eval(&block)
170
+ end
171
+
172
+
173
+ def method_missing(name, *args)
174
+ if name.to_s =~ /^_.*_$/
175
+ magic_option(name)
176
+ else
177
+ super
178
+ end
179
+ end
180
+
181
+
182
+ # Returns the option value or renamed class name from a 'magic'
183
+ # name like _foo_ or _MyFoo_
184
+ def magic_option(name)
185
+ option_name = name.to_s[1..-2]
186
+ if option_name == "bundle"
187
+ self.name
188
+ elsif options.has_key?(option_name)
189
+ options[option_name]
190
+ else
191
+ new_name_for(option_name)
192
+ end
193
+ end
194
+
195
+
196
+ def optional_bundle(*args)
197
+ local_options = args.extract_options!
198
+ class_name, option_name = args
199
+ option_name ||= class_name.to_s.underscore
200
+ _include_bundle(class_name, option_name, local_options) if self.options[option_name]
201
+ end
202
+
203
+
204
+ def include_bundle(*args)
205
+ local_options = args.extract_options!
206
+ class_name, option_name = args
207
+ option_name ||= class_name.to_s.underscore
208
+ _include_bundle(class_name, option_name, local_options)
209
+ end
210
+
211
+
212
+ def _include_bundle(class_name, option_name, local_options)
213
+ external_options = self.options[option_name]
214
+ external_options = {} if external_options.nil? || external_options == true
215
+ name = "#{self.name}_#{option_name}"
216
+ class_name.to_s.constantize.new(name, external_options.merge(local_options).merge(renames))
217
+ self.options["#{option_name}_bundle"] = name
218
+ end
219
+
220
+ end
221
+
222
+ end
@@ -32,12 +32,9 @@ module Hobo
32
32
  attr_reader :included_taglibs
33
33
 
34
34
  def include_taglib(src, options={})
35
- @included_taglibs << if options[:from_plugin]
36
- 'plugins/' + options[:from_plugin] + '/taglibs/' + src
37
- else
38
- src.to_s
39
- end
35
+ @included_taglibs << options.merge(:src => src)
40
36
  end
37
+
41
38
  end
42
39
 
43
40
 
@@ -24,8 +24,8 @@ module Hobo
24
24
 
25
25
  EMPTY_PAGE = "[tag-page]"
26
26
 
27
- APPLICATION_TAGLIB = "taglibs/application"
28
- CORE_TAGLIB = "plugins/hobo/tags/core"
27
+ APPLICATION_TAGLIB = { :src => "taglibs/application" }
28
+ CORE_TAGLIB = { :src => "core", :plugin => "hobo" }
29
29
 
30
30
  DEFAULT_IMPORTS = [Hobo::HoboHelper, ApplicationHelper]
31
31
 
@@ -61,10 +61,11 @@ module Hobo
61
61
  page ||= view.instance_variable_get('@hobo_template_path')
62
62
 
63
63
  prepare_view!(view)
64
- included_taglibs = ([subsite_taglib(page)] + controller_taglibs(page)).compact
64
+ included_taglibs = ([subsite_taglib(page)] + controller_taglibs(view.controller.class)).compact
65
65
 
66
66
  if page.ends_with?(EMPTY_PAGE)
67
- controller_class = controller_class_for(page)
67
+ # DELETE ME: controller_class = controller_class_for(page)
68
+ controller_class = view.controller.class
68
69
  @tag_page_renderer_classes[controller_class.name] ||=
69
70
  make_renderer_class("", page, local_names, DEFAULT_IMPORTS, included_taglibs)
70
71
  @tag_page_renderer_classes[controller_class.name].new(page, view)
@@ -87,14 +88,14 @@ module Hobo
87
88
  end
88
89
 
89
90
 
91
+ # TODO: Delete this - not needed (use view.controller.class)
90
92
  def controller_class_for(page)
91
93
  controller, view = Controller.controller_and_view_for(page)
92
94
  "#{controller.camelize}Controller".constantize
93
95
  end
94
96
 
95
97
 
96
- def controller_taglibs(page)
97
- controller_class = controller_class_for(page)
98
+ def controller_taglibs(controller_class)
98
99
  (controller_class.respond_to?(:included_taglibs) && controller_class.included_taglibs) || []
99
100
  end
100
101
 
@@ -103,7 +104,7 @@ module Hobo
103
104
  parts = page.split("/")
104
105
  if parts.length == 3
105
106
  subsite = parts.first
106
- "taglibs/#{subsite}" if File.exists?("#{RAILS_ROOT}/app/views/taglibs/#{subsite}.dryml")
107
+ { :src => "taglibs/#{subsite}" } if File.exists?("#{RAILS_ROOT}/app/views/taglibs/#{subsite}.dryml")
107
108
  end
108
109
  end
109
110