hobo 0.8.5 → 0.8.6

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 (68) hide show
  1. data/CHANGES.txt +41 -0
  2. data/Manifest +1 -5
  3. data/Rakefile +10 -3
  4. data/bin/hobo +38 -15
  5. data/dryml_generators/rapid/cards.dryml.erb +7 -7
  6. data/dryml_generators/rapid/pages.dryml.erb +52 -24
  7. data/hobo.gemspec +42 -322
  8. data/init.rb +0 -7
  9. data/lib/active_record/association_collection.rb +9 -0
  10. data/lib/hobo.rb +13 -14
  11. data/lib/hobo/accessible_associations.rb +32 -7
  12. data/lib/hobo/authentication_support.rb +1 -1
  13. data/lib/hobo/controller.rb +5 -7
  14. data/lib/hobo/dryml.rb +9 -2
  15. data/lib/hobo/dryml/dryml_builder.rb +11 -12
  16. data/lib/hobo/dryml/dryml_doc.rb +22 -24
  17. data/lib/hobo/dryml/dryml_generator.rb +41 -4
  18. data/lib/hobo/dryml/part_context.rb +5 -3
  19. data/lib/hobo/dryml/template.rb +7 -7
  20. data/lib/hobo/dryml/template_environment.rb +11 -22
  21. data/lib/hobo/dryml/template_handler.rb +94 -25
  22. data/lib/hobo/find_for.rb +2 -2
  23. data/lib/hobo/hobo_helper.rb +21 -21
  24. data/lib/hobo/include_in_save.rb +9 -5
  25. data/lib/hobo/lifecycles/transition.rb +2 -2
  26. data/lib/hobo/model.rb +11 -61
  27. data/lib/hobo/model_controller.rb +28 -29
  28. data/lib/hobo/model_router.rb +12 -13
  29. data/lib/hobo/permissions.rb +47 -37
  30. data/lib/hobo/permissions/associations.rb +1 -1
  31. data/lib/hobo/scopes/association_proxy_extensions.rb +5 -6
  32. data/lib/hobo/scopes/automatic_scopes.rb +7 -4
  33. data/lib/hobo/tasks/rails.rb +4 -0
  34. data/lib/hobo/user.rb +0 -1
  35. data/lib/hobo/user_controller.rb +3 -1
  36. data/lib/hobo/view_hints.rb +17 -3
  37. data/rails_generators/hobo/hobo_generator.rb +1 -0
  38. data/rails_generators/hobo_front_controller/templates/functional_test.rb +1 -11
  39. data/rails_generators/hobo_front_controller/templates/index.dryml +1 -6
  40. data/rails_generators/hobo_rapid/hobo_rapid_generator.rb +1 -0
  41. data/rails_generators/hobo_rapid/templates/hobo-rapid.css +3 -2
  42. data/rails_generators/hobo_rapid/templates/hobo-rapid.js +24 -15
  43. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +17 -12
  44. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +6 -2
  45. data/rails_generators/hobo_rapid/templates/themes/clean/views/clean.dryml +2 -2
  46. data/rails_generators/hobo_user_model/templates/forgot_password.erb +2 -2
  47. data/rails_generators/hobo_user_model/templates/model.rb +2 -2
  48. data/taglibs/rapid.dryml +3 -2
  49. data/taglibs/rapid_core.dryml +21 -16
  50. data/taglibs/rapid_document_tags.dryml +1 -1
  51. data/taglibs/rapid_editing.dryml +7 -10
  52. data/taglibs/rapid_forms.dryml +115 -26
  53. data/taglibs/rapid_generics.dryml +13 -3
  54. data/taglibs/rapid_lifecycles.dryml +18 -1
  55. data/taglibs/rapid_navigation.dryml +50 -61
  56. data/taglibs/rapid_pages.dryml +103 -19
  57. data/taglibs/rapid_plus.dryml +54 -6
  58. data/taglibs/rapid_support.dryml +38 -1
  59. data/taglibs/rapid_user_pages.dryml +17 -5
  60. data/test/permissions/models/models.rb +24 -12
  61. data/test/permissions/models/test.sqlite3 +0 -0
  62. metadata +6 -15
  63. data/lib/extensions/test_case.rb +0 -129
  64. data/lib/hobo/composite_model.rb +0 -73
  65. data/lib/hobo/model_support.rb +0 -44
  66. data/tasks/fix_dryml.rake +0 -143
  67. data/tasks/generate_tag_reference.rake +0 -192
  68. data/test/dryml/complilation_test.rb +0 -261
data/init.rb CHANGED
@@ -1,9 +1,2 @@
1
1
  require File.dirname(__FILE__) + "/lib/hobo"
2
2
  require 'rails_generator'
3
-
4
- # 'orrible but 'reative 'ack to allow generators to be in symlinked plugins
5
- Rails::Generator::PathSource.class_eval do
6
- def path
7
- @path.gsub('**', '*/**')
8
- end
9
- end
@@ -13,6 +13,15 @@ module ActiveRecord
13
13
  def new_candidate(attributes = {})
14
14
  record = new
15
15
  @target.delete record
16
+ set_reverse_association(record) if hobo_association_collection?
17
+ record
18
+ end
19
+
20
+
21
+ def user_new_candidate(user, attributes = {})
22
+ record = user_new(user, attributes)
23
+ @target.delete record
24
+ set_reverse_association(record) if hobo_association_collection?
16
25
  record
17
26
  end
18
27
 
@@ -16,7 +16,7 @@ class HoboError < RuntimeError; end
16
16
 
17
17
  module Hobo
18
18
 
19
- VERSION = "0.8.5"
19
+ VERSION = "0.8.6"
20
20
 
21
21
  class PermissionDeniedError < RuntimeError; end
22
22
 
@@ -41,10 +41,6 @@ module Hobo
41
41
  end
42
42
 
43
43
 
44
- def typed_id(obj, attr=nil)
45
- attr ? "#{obj.typed_id}:#{attr}" : obj.typed_id
46
- end
47
-
48
44
  def find_by_search(query, search_targets=nil)
49
45
  search_targets ||=
50
46
  begin
@@ -90,10 +86,14 @@ module Hobo
90
86
  def get_field(object, field)
91
87
  return nil if object.nil?
92
88
  field_str = field.to_s
93
- if field_str =~ /^\d+$/
94
- object[field.to_i]
95
- else
96
- object.send(field)
89
+ begin
90
+ return object.send(field_str)
91
+ rescue NoMethodError => ex
92
+ if field_str =~ /^\d+$/
93
+ return object[field.to_i]
94
+ else
95
+ return object[field]
96
+ end
97
97
  end
98
98
  end
99
99
 
@@ -159,9 +159,7 @@ module Hobo
159
159
 
160
160
  ActionController::Base.send(:include, Hobo::ControllerExtensions)
161
161
 
162
- if defined? HoboFields
163
- HoboFields.never_wrap(Hobo::Undefined)
164
- end
162
+ HoboFields.never_wrap(Hobo::Undefined) if defined? HoboFields
165
163
 
166
164
  ActiveSupport::Dependencies.load_paths |= [ "#{RAILS_ROOT}/app/viewhints" ]
167
165
  end
@@ -189,7 +187,6 @@ module Hobo
189
187
  # Empty class to represent the boolean type.
190
188
  class Boolean; end
191
189
 
192
-
193
190
  end
194
191
 
195
192
 
@@ -212,11 +209,13 @@ end
212
209
 
213
210
  module ::Enumerable
214
211
  def group_by_with_metadata(&block)
215
- group_by_without_metadata(&block).each do |k,v|
212
+ r=group_by_without_metadata(&block)
213
+ r.each do |k,v|
216
214
  v.origin = origin
217
215
  v.origin_attribute = origin_attribute
218
216
  v.member_class = member_class
219
217
  end
218
+ r
220
219
  end
221
220
  alias_method_chain :group_by, :metadata
222
221
  end
@@ -53,6 +53,13 @@ module Hobo
53
53
  def params_hash_to_array(array_or_hash)
54
54
  if array_or_hash.is_a?(Hash)
55
55
  array = array_or_hash.get(*array_or_hash.keys.sort_by(&:to_i))
56
+ elsif array_or_hash.is_a?(String)
57
+ # Due to the way that rails works, there's no good way to tell
58
+ # the difference between an empty array and a params hash that
59
+ # just isn't making any updates to the array. So we're
60
+ # hacking this in: if you pash an empty string where an array
61
+ # is expected, we assume you wanted an empty array.
62
+ []
56
63
  else
57
64
  array_or_hash
58
65
  end
@@ -67,7 +74,13 @@ module Hobo
67
74
  finder.named(id_or_name)
68
75
  end
69
76
  end
70
-
77
+
78
+ def finder_for_belongs_to(record, name)
79
+ refl = record.class.reflections[name]
80
+ conditions = ActiveRecord::Associations::BelongsToAssociation.new(record, refl).conditions
81
+ finder = refl.klass.scoped(:conditions => conditions)
82
+ end
83
+
71
84
  end
72
85
 
73
86
  classy_module(AccessibleAssociations) do
@@ -103,22 +116,34 @@ module Hobo
103
116
  if options[:accessible]
104
117
  class_eval %{
105
118
  def #{name}_with_accessible=(record_hash_or_string)
106
- refl = self.class.reflections[:#{name}]
107
- conditions = ActiveRecord::Associations::BelongsToAssociation.new(self, refl).conditions
108
- finder = refl.klass.scoped(:conditions => conditions)
119
+ finder = Hobo::AccessibleAssociations.finder_for_belongs_to(self, :#{name})
109
120
  record = Hobo::AccessibleAssociations.find_or_create_and_update(self, :#{name}, finder, record_hash_or_string) do |id|
110
121
  if id
111
122
  raise ArgumentError, "attempted to update the wrong record in belongs_to association #{self}##{name}" unless
112
- #{name} && id == self.#{name}.id
123
+ #{name} && id.to_s == self.#{name}.id.to_s
113
124
  #{name}
114
125
  else
115
- refl.klass.new
126
+ finder.new
116
127
  end
117
128
  end
118
129
  self.#{name}_without_accessible = record
119
130
  end
120
- }, __FILE__, __LINE__ - 5
131
+ }, __FILE__, __LINE__ - 15
121
132
  alias_method_chain :"#{name}=", :accessible
133
+ else
134
+ # Not accessible - but finding by name and ID is still supported
135
+ class_eval %{
136
+ def #{name}_with_finder=(record_or_string)
137
+ record = if record_or_string.is_a?(String)
138
+ finder = Hobo::AccessibleAssociations.finder_for_belongs_to(self, :#{name})
139
+ Hobo::AccessibleAssociations.find_by_name_or_id(finder, record_or_string)
140
+ else # it is a record
141
+ record_or_string
142
+ end
143
+ self.#{name}_without_finder = record
144
+ end
145
+ }, __FILE__, __LINE__ - 12
146
+ alias_method_chain :"#{name}=", :finder
122
147
  end
123
148
  end
124
149
  metaclass.alias_method_chain :belongs_to, :accessible
@@ -102,7 +102,7 @@ module Hobo
102
102
  !logged_in? and
103
103
  cookie = cookies[:auth_token] and
104
104
  (token, model_name = cookie.split) and
105
- user_model = model_name.constantize rescue nil and
105
+ user_model = model_name._?.safe_constantize and
106
106
  user = user_model.find_by_remember_token(token) and
107
107
  user.remember_token? and
108
108
  user
@@ -18,27 +18,25 @@ module Hobo
18
18
  before_filter :login_from_cookie
19
19
  alias_method_chain :redirect_to, :object_url
20
20
  around_filter do |controller, action|
21
- Thread.current['Hobo::current_controller'] = controller
21
+ Thread.current['Hobo.current_controller'] = controller
22
22
  action.call
23
- Thread.current['Hobo::current_controller'] = nil # should avoid memory-leakage
23
+ Thread.current['Hobo.current_controller'] = nil # should avoid memory-leakage
24
24
  end
25
25
  @included_taglibs = []
26
26
  end
27
27
  Hobo::HoboHelper.add_to_controller(klass)
28
28
  end
29
29
 
30
- attr_writer :request_host, :app_name # DEPRECIATED AFTER 0.9 - setting them should not be needed
31
-
32
30
  def controller_and_view_for(page_path)
33
31
  page_path.match(/(.*)\/([^\/]+)/)[1..2]
34
32
  end
35
33
 
36
34
  def request_host
37
- @request_host || Thread.current['Hobo::current_controller'].request.host_with_port
35
+ Thread.current['Hobo.current_controller'].request.host_with_port
38
36
  end
39
37
 
40
38
  def app_name
41
- @app_name || Thread.current['Hobo::current_controller'].send(:call_tag, :app_name)
39
+ Thread.current['Hobo.current_controller'].send(:call_tag, :app_name)
42
40
  end
43
41
 
44
42
  end
@@ -57,7 +55,7 @@ module Hobo
57
55
  protected
58
56
 
59
57
  def redirect_to_with_object_url(destination, *args)
60
- if destination.is_a?(String, Hash, Symbol)
58
+ if destination.is_one_of?(String, Hash, Symbol)
61
59
  redirect_to_without_object_url(destination)
62
60
  else
63
61
  redirect_to_without_object_url(object_url(destination, *args))
@@ -38,6 +38,7 @@ module Hobo
38
38
 
39
39
  def enable
40
40
  ActionView::Template.register_template_handler("dryml", Hobo::Dryml::TemplateHandler)
41
+ DrymlGenerator.enable
41
42
  end
42
43
 
43
44
 
@@ -78,8 +79,14 @@ module Hobo
78
79
  @tag_page_renderer_classes[controller_class.name] ||=
79
80
  make_renderer_class("", page, local_names, DEFAULT_IMPORTS, included_taglibs)
80
81
  @tag_page_renderer_classes[controller_class.name].new(page, view)
81
- else
82
- filename ||= view._pick_template(page + ".dryml").filename
82
+ else
83
+ filename ||= if view.view_paths.respond_to? :find_template
84
+ # Rails 2.3
85
+ view.view_paths.find_template(page + ".dryml").filename
86
+ else
87
+ # Rails 2.2
88
+ view._pick_template(page + ".dryml").filename
89
+ end
83
90
  mtime = File.mtime(filename)
84
91
  renderer_class = @renderer_classes[page]
85
92
 
@@ -54,21 +54,20 @@ module Hobo::Dryml
54
54
  ("def render_page(__page_this__, __local_assigns__); " +
55
55
  "#{locals} new_object_context(__page_this__) do " +
56
56
  src +
57
- "; _erbout; end; end")
57
+ "; output_buffer; end; end")
58
58
  end
59
59
 
60
60
 
61
- def erb_process(erb_src)
62
- # Strip off "_erbout = ''" from the beginning and "; _erbout"
63
- # from the end, because we do things differently around
64
- # here. (_erbout is defined as a method)
65
- trim_mode = if defined?(ActionView::TemplateHandlers::ERB.erb_trim_mode)
66
- ActionView::TemplateHandlers::ERB.erb_trim_mode
67
- else
68
- ActionView::Base.erb_trim_mode
69
- end
61
+ def erb_process(erb_src, method_def=false)
62
+ trim_mode = ActionView::TemplateHandlers::ERB.erb_trim_mode
63
+ erb = ERB.new(erb_src, nil, trim_mode, "output_buffer")
64
+ src = erb.src[("output_buffer = '';").length..-("; output_buffer".length)]
70
65
 
71
- ERB.new(erb_src, nil, trim_mode).src[("_erbout = '';").length..-("; _erbout".length)]
66
+ if method_def
67
+ src.sub /^\s*def.*?\(.*?\)/, '\0 __in_erb_template=true; '
68
+ else
69
+ "__in_erb_template=true; " + src
70
+ end
72
71
  end
73
72
 
74
73
 
@@ -83,7 +82,7 @@ module Hobo::Dryml
83
82
  @environment.class_eval(instruction[:src], template_path, instruction[:line_num])
84
83
 
85
84
  when :def
86
- src = erb_process(instruction[:src])
85
+ src = erb_process(instruction[:src], true)
87
86
  @environment.class_eval(src, template_path, instruction[:line_num])
88
87
 
89
88
  when :render_page
@@ -13,10 +13,27 @@ module Hobo
13
13
  dryml_files.map { |f| taglib_class.new(directory, f) }
14
14
  end
15
15
 
16
+ CommentMethods = classy_module do
17
+
18
+ def comment_intro
19
+ comment && comment =~ /(.*?)^#/m ? $1 : comment
20
+ end
21
+
22
+
23
+ def comment_rest
24
+ comment && comment[comment_intro.length..-1]
25
+ end
26
+
27
+ %w(comment comment_intro comment_rest).each do |m|
28
+ class_eval "def #{m}_html; Maruku.new(#{m}).to_html.gsub(/&amp;/, '&'); end"
29
+ end
30
+
31
+ end
32
+
16
33
  class Taglib
17
34
 
18
- def initialize(home, filename)
19
- @name = filename.sub(/.dryml$/, '')[home.length+1..-1]
35
+ def initialize(home, filename, name=nil)
36
+ @name = name || filename.sub(/.dryml$/, '')[home.length+1..-1]
20
37
  @doc = Hobo::Dryml::Parser::Document.new(File.read(filename), filename)
21
38
  parse_tag_defs
22
39
  end
@@ -28,10 +45,7 @@ module Hobo
28
45
  doc.restore_erb_scriptlets(first_node.to_s.strip) if first_node.is_a?(REXML::Comment)
29
46
  end
30
47
 
31
- def comment_html
32
- Maruku.new(comment).to_html
33
- end
34
-
48
+ include CommentMethods
35
49
 
36
50
  private
37
51
 
@@ -78,23 +92,7 @@ module Hobo
78
92
  end
79
93
  end
80
94
 
81
-
82
- def comment_intro
83
- comment && comment =~ /(.*?)^#/m ? $1 : comment
84
- end
85
-
86
-
87
- def comment_rest
88
- comment && comment[comment_intro.length..-1]
89
- end
90
-
91
- %w(comment comment_intro comment_rest).each do |m|
92
- class_eval "def #{m}_html; Maruku.new(#{m}).to_html.gsub(/&amp;/, '&'); end"
93
- end
94
-
95
- def comment_html
96
-
97
- end
95
+ include CommentMethods
98
96
 
99
97
  def no_doc?
100
98
  comment =~ /^nodoc\b/
@@ -158,4 +156,4 @@ module Hobo
158
156
 
159
157
  end
160
158
 
161
- end
159
+ end
@@ -1,6 +1,9 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'set'
2
3
  require 'fileutils'
3
4
 
5
+ require 'action_controller/dispatcher'
6
+
4
7
  module Hobo
5
8
 
6
9
  module Dryml
@@ -12,8 +15,37 @@ module Hobo
12
15
 
13
16
  HEADER = "<!-- AUTOMATICALLY GENERATED FILE - DO NOT EDIT -->\n\n"
14
17
 
18
+ class << self
19
+ attr_accessor :run_on_every_request
20
+ end
21
+
22
+ def self.enable
23
+
24
+ # Unfortunately the dispatcher callbacks don't give us the hook we need (after routes are reloaded)
25
+ # so we have to alias_method_chain
26
+ ActionController::Dispatcher.class_eval do
27
+
28
+ if respond_to? :reload_application
29
+ #Rails 2.3
30
+ class << self
31
+ def reload_application_with_dryml_generators
32
+ reload_application_without_dryml_generators
33
+ DrymlGenerator.run unless Hobo::Dryml::DrymlGenerator.run_on_every_request == false || Rails.env.production?
34
+ end
35
+ alias_method_chain :reload_application, :dryml_generators
36
+ end
37
+ else
38
+ #Rails <= 2.2
39
+ def reload_application_with_dryml_generators
40
+ reload_application_without_dryml_generators
41
+ DrymlGenerator.run unless Hobo::Dryml::DrymlGenerator.run_on_every_request == false || Rails.env.production?
42
+ end
43
+ alias_method_chain :reload_application, :dryml_generators
44
+ end
45
+ end
46
+ end
47
+
15
48
  def self.run
16
- return if RAILS_ENV == "production"
17
49
  @generator ||= DrymlGenerator.new
18
50
  @generator.run
19
51
  end
@@ -124,7 +156,7 @@ module Hobo
124
156
 
125
157
 
126
158
  def model_name(*options)
127
- name = model.name
159
+ name = model.view_hints.model_name
128
160
  name = name.pluralize if :plural.in?(options)
129
161
  name = name.titleize if :title.in?(options)
130
162
  name = name.titleize.downcase if :lowercase.in?(options)
@@ -136,6 +168,11 @@ module Hobo
136
168
  def model_class
137
169
  model_name(:dashed)
138
170
  end
171
+
172
+
173
+ def view_hints
174
+ model.view_hints
175
+ end
139
176
 
140
177
 
141
178
  def through_collection_names(klass=model)
@@ -170,7 +207,7 @@ module Hobo
170
207
  end
171
208
  end
172
209
 
173
-
210
+
174
211
  def standard_fields(*args)
175
212
  klass = args.first.is_a?(Class) ? args.shift : model
176
213
  extras = args
@@ -183,7 +220,7 @@ module Hobo
183
220
  hm = extras.include?(:has_many)
184
221
  klass.reflections.values.sort_by { |refl| refl.name.to_s }.map do |refl|
185
222
  fields << refl.name.to_s if bt && refl.macro == :belongs_to
186
- fields << refl.name.to_s if hm && refl.macro == :has_many
223
+ fields << refl.name.to_s if hm && refl.macro == :has_many && refl.options[:accessible]
187
224
  end
188
225
 
189
226
  fields.reject! { |f| model.never_show? f }
@@ -77,8 +77,10 @@ module Hobo
77
77
 
78
78
  part_name, this_id, locals, form_field_path = context
79
79
 
80
- RAILS_DEFAULT_LOGGER.info "Call part: #{part_name}. this-id = #{this_id}, locals = #{locals.inspect}"
81
- RAILS_DEFAULT_LOGGER.info " : form_field_path = #{form_field_path.inspect}" if form_field_path
80
+ if RAILS_DEFAULT_LOGGER
81
+ RAILS_DEFAULT_LOGGER.info "Call part: #{part_name}. this-id = #{this_id}, locals = #{locals.inspect}"
82
+ RAILS_DEFAULT_LOGGER.info " : form_field_path = #{form_field_path.inspect}" if form_field_path
83
+ end
82
84
 
83
85
  self.part_name = part_name
84
86
  self.this_id = this_id
@@ -91,7 +93,7 @@ module Hobo
91
93
 
92
94
  # Generate the HMAC keyed message digest. Uses SHA1 by default.
93
95
  def generate_digest(data, session)
94
- secret = self.class.secret || ActionController::Base.cached_session_options.first[:secret]
96
+ secret = self.class.secret || ActionController::Base.session_options[:secret] || ActionController::Base.cached_session_options.first[:secret]
95
97
  key = secret.respond_to?(:call) ? secret.call(session) : secret
96
98
  OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(self.class.digest), key, data)
97
99
  end