hobo 0.8.5 → 0.8.6

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