hobo 0.8.4 → 0.8.5

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.
data/CHANGES.txt CHANGED
@@ -1,3 +1,30 @@
1
+ === Hobo 0.8.5 ===
2
+
3
+ New permission system
4
+
5
+ Various fixes
6
+
7
+ Now runs permission checks *before* callbacks, not after
8
+
9
+ In the switch to the new permissions system, we changed to running them after all callbacks. This turned
10
+ out to be wrong. Permissions should only be about what the user tried to change, not other changes
11
+ triggered by application logic
12
+
13
+ API change: Web method permissions should now be defined as foo_permitted? instead of foo_call_permitted?
14
+
15
+ Updated hobo command and hobo generator to use the new config.gem style for apps that use the Hobo gem
16
+
17
+ The --add-gem option to script/generate hobo will add "config.gem 'hobo'" to environment.rb. The hobo command
18
+ does this automatically
19
+
20
+ Lifecycles fix -- state_name would throw a nil error if there was no state (not returns nil)
21
+
22
+ This was causing the :new_key option to fail on a create step
23
+
24
+ Fixes to problems with live-search introduced with the Rails 2.2 upgrade
25
+
26
+
27
+
1
28
  === Hobo 0.8.4 ===
2
29
 
3
30
  Rails 2.2 compatible. Rails 2.1 support dropped.
data/Rakefile CHANGED
@@ -40,11 +40,11 @@ Echoe.new('hobo') do |p|
40
40
  p.project = "hobo"
41
41
 
42
42
  p.changelog = "CHANGES.txt"
43
- p.version = "0.8.4"
43
+ p.version = "0.8.5"
44
44
 
45
45
  p.dependencies = [
46
- 'hobosupport =0.8.4',
47
- 'hobofields =0.8.4',
46
+ 'hobosupport =0.8.5',
47
+ 'hobofields =0.8.5',
48
48
  'rails >=2.2.2',
49
49
  'mislav-will_paginate >=2.2.1']
50
50
 
data/bin/hobo CHANGED
@@ -74,7 +74,7 @@ Dir.chdir(app_path) do
74
74
  FileUtils.touch("public/stylesheets/application.css")
75
75
 
76
76
  puts "\nInitialising Hobo...\n"
77
- command(gen, "hobo --add-routes")
77
+ command(gen, "hobo --add-gem --add-routes")
78
78
 
79
79
  puts "\nInstalling Hobo Rapid and default theme...\n"
80
80
  command("#{gen} hobo_rapid --import-tags")
data/hobo.gemspec CHANGED
@@ -1,18 +1,18 @@
1
1
 
2
- # Gem::Specification for Hobo-0.8.4
2
+ # Gem::Specification for Hobo-0.8.5
3
3
  # Originally generated by Echoe
4
4
 
5
5
  --- !ruby/object:Gem::Specification
6
6
  name: hobo
7
7
  version: !ruby/object:Gem::Version
8
- version: 0.8.4
8
+ version: 0.8.5
9
9
  platform: ruby
10
10
  authors:
11
11
  - Tom Locke
12
12
  autorequire:
13
13
  bindir: bin
14
14
 
15
- date: 2008-12-06 00:00:00 +00:00
15
+ date: 2008-12-09 00:00:00 +00:00
16
16
  default_executable:
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
@@ -23,7 +23,7 @@ dependencies:
23
23
  requirements:
24
24
  - - "="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.8.4
26
+ version: 0.8.5
27
27
  version:
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: hobofields
@@ -33,7 +33,7 @@ dependencies:
33
33
  requirements:
34
34
  - - "="
35
35
  - !ruby/object:Gem::Version
36
- version: 0.8.4
36
+ version: 0.8.5
37
37
  version:
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: rails
data/lib/hobo.rb CHANGED
@@ -16,7 +16,7 @@ class HoboError < RuntimeError; end
16
16
 
17
17
  module Hobo
18
18
 
19
- VERSION = "0.8.4"
19
+ VERSION = "0.8.5"
20
20
 
21
21
  class PermissionDeniedError < RuntimeError; end
22
22
 
@@ -9,31 +9,36 @@ module Hobo
9
9
 
10
10
  array = params_hash_to_array(array_or_hash)
11
11
  array.map! do |record_hash_or_string|
12
- find_or_create_and_update(owner, association, association_name, record_hash_or_string) { association.build }
12
+ finder = association.member_class.scoped :conditions => association.conditions
13
+ find_or_create_and_update(owner, association_name, finder, record_hash_or_string) do |id|
14
+ # The block is required to either locate find an existing record in the collection, or build a new one
15
+ if id
16
+ # TODO: We don't really want to find these one by one
17
+ association.find(id)
18
+ else
19
+ association.build
20
+ end
21
+ end
13
22
  end
14
23
  array.compact
15
24
  end
16
25
 
17
26
 
18
- def find_or_create_and_update(owner, association, association_name, record_hash_or_string)
27
+ def find_or_create_and_update(owner, association_name, finder, record_hash_or_string)
19
28
  if record_hash_or_string.is_a?(String)
20
- # An ID (if it starts '@') or else a name
21
- record = find_record(association, record_hash_or_string)
29
+ # An ID or a name - the passed block will find the record
30
+ record = find_by_name_or_id(finder, record_hash_or_string)
22
31
 
23
32
  elsif record_hash_or_string.is_a?(Hash)
24
33
  # A hash of attributes
25
34
  hash = record_hash_or_string
26
35
 
27
36
  # Remove completely blank hashes
28
- return nil if hash.values.join.blank?
37
+ return nil if hash.values.all?(&:blank?)
29
38
 
30
39
  id = hash.delete(:id)
31
40
 
32
- record = if id
33
- association.find(id) # TODO: We don't really want to find these one by one
34
- else
35
- record = yield
36
- end
41
+ record = yield id
37
42
  record.attributes = hash
38
43
  owner.include_in_save(association_name, record) unless owner.new_record? && record.new_record?
39
44
 
@@ -43,7 +48,7 @@ module Hobo
43
48
  end
44
49
  record
45
50
  end
46
-
51
+
47
52
 
48
53
  def params_hash_to_array(array_or_hash)
49
54
  if array_or_hash.is_a?(Hash)
@@ -54,17 +59,12 @@ module Hobo
54
59
  end
55
60
 
56
61
 
57
- def find_record(association, id_or_name)
58
- klass = association.member_class
62
+ def find_by_name_or_id(finder, id_or_name)
59
63
  if id_or_name =~ /^@(.*)/
60
64
  id = $1
61
- if id =~ /:/
62
- Hobo::Model.find_by_typed_id(id)
63
- else
64
- klass.find(id)
65
- end
65
+ finder.find(id)
66
66
  else
67
- klass.named(id_or_name, :conditions => association.conditions)
67
+ finder.named(id_or_name)
68
68
  end
69
69
  end
70
70
 
@@ -103,7 +103,18 @@ module Hobo
103
103
  if options[:accessible]
104
104
  class_eval %{
105
105
  def #{name}_with_accessible=(record_hash_or_string)
106
- record = Hobo::AccessibleAssociations.find_or_create_and_update(self, #{name}, :#{name}, record_hash_or_string) { self.class.reflections[:#{name}].klass.new }
106
+ refl = self.class.reflections[:#{name}]
107
+ conditions = ActiveRecord::Associations::BelongsToAssociation.new(self, refl).conditions
108
+ finder = refl.klass.scoped(:conditions => conditions)
109
+ record = Hobo::AccessibleAssociations.find_or_create_and_update(self, :#{name}, finder, record_hash_or_string) do |id|
110
+ if id
111
+ raise ArgumentError, "attempted to update the wrong record in belongs_to association #{self}##{name}" unless
112
+ #{name} && id == self.#{name}.id
113
+ #{name}
114
+ else
115
+ refl.klass.new
116
+ end
117
+ end
107
118
  self.#{name}_without_accessible = record
108
119
  end
109
120
  }, __FILE__, __LINE__ - 5
@@ -122,7 +122,7 @@ module Hobo
122
122
 
123
123
  def tag_renderer
124
124
  @tag_renderer ||= begin
125
- add_variables_to_assigns
125
+ @template.send(:_evaluate_assigns_and_ivars)
126
126
  Hobo::Dryml.empty_page_renderer(@template)
127
127
  end
128
128
  end
@@ -135,7 +135,7 @@ module Hobo
135
135
  NO_SEARCH_RESULTS_HTML = "<p>Your search returned no matches.</p>"
136
136
  def site_search(query)
137
137
  results_hash = Hobo.find_by_search(query)
138
- all_results = results_hash.values.flatten.select { |r| Hobo.can_view?(current_user, r, nil) }
138
+ all_results = results_hash.values.flatten.select { |r| r.viewable_by?(current_user) }
139
139
  if all_results.empty?
140
140
  render :text => NO_SEARCH_RESULTS_HTML
141
141
  else
@@ -141,7 +141,8 @@ module Hobo
141
141
 
142
142
 
143
143
  def state_name
144
- record.read_attribute(self.class.state_field).to_sym
144
+ name = record.read_attribute(self.class.state_field)
145
+ name.to_sym if name
145
146
  end
146
147
 
147
148
 
@@ -111,7 +111,7 @@ module Hobo
111
111
  # Make sure we have a copy of the options - it is being mutated somewhere
112
112
  opts = {}.merge(options)
113
113
  self.this = find_instance(opts) unless opts[:no_find]
114
- raise Hobo::PermissionDeniedError unless Hobo.can_call?(current_user, @this, method)
114
+ raise Hobo::PermissionDeniedError unless @this.method_callable_by?(current_user, method)
115
115
  if got_block
116
116
  instance_eval(&block)
117
117
  else
@@ -7,16 +7,12 @@ module Hobo
7
7
  end
8
8
 
9
9
  def self.included(klass)
10
- klass.extend ClassMethods
11
-
12
- create_with_callbacks = find_aliased_name klass, :create_with_callbacks
13
- update_with_callbacks = find_aliased_name klass, :update_with_callbacks
14
- destroy_with_callbacks = find_aliased_name klass, :destroy_with_callbacks
15
-
16
10
  klass.class_eval do
17
- alias_method create_with_callbacks, :create_with_callbacks_with_hobo_permission_check
18
- alias_method update_with_callbacks, :update_with_callbacks_with_hobo_permission_check
19
- alias_method destroy_with_callbacks, :destroy_with_callbacks_with_hobo_permission_check
11
+ extend ClassMethods
12
+
13
+ alias_method_chain :create, :hobo_permission_check
14
+ alias_method_chain :update, :hobo_permission_check
15
+ alias_method_chain :destroy, :hobo_permission_check
20
16
 
21
17
  attr_accessor :acting_user, :origin, :origin_attribute
22
18
 
@@ -91,40 +87,26 @@ module Hobo
91
87
  acting_user && !(self.class.has_lifecycle? && lifecycle.active_step)
92
88
  end
93
89
 
94
- def create_with_callbacks_with_hobo_permission_check(*args)
95
- return false if callback(:before_create) == false
96
-
90
+ def create_with_hobo_permission_check(*args, &b)
97
91
  if permission_check_required?
98
92
  create_permitted? or raise PermissionDeniedError, "#{self.class.name}#create"
99
93
  end
100
-
101
- result = create_without_callbacks
102
- callback(:after_create)
103
- result
94
+ create_without_hobo_permission_check(*args, &b)
104
95
  end
105
96
 
106
- def update_with_callbacks_with_hobo_permission_check(*args)
107
- return false if callback(:before_update) == false
108
-
97
+ def update_with_hobo_permission_check(*args)
109
98
  if permission_check_required?
110
99
  update_permitted? or raise PermissionDeniedError, "#{self.class.name}#update"
111
100
  end
112
-
113
- result = update_without_callbacks(*args)
114
- callback(:after_update)
115
- result
101
+ update_without_hobo_permission_check(*args)
116
102
  end
117
103
 
118
- def destroy_with_callbacks_with_hobo_permission_check
119
- return false if callback(:before_destroy) == false
120
-
104
+ def destroy_with_hobo_permission_check
121
105
  if permission_check_required?
122
106
  destroy_permitted? or raise PermissionDeniedError, "#{self.class.name}#.destroy"
123
107
  end
124
108
 
125
- result = destroy_without_callbacks
126
- callback(:after_destroy)
127
- result
109
+ destroy_without_hobo_permission_check
128
110
  end
129
111
 
130
112
  # -------------------------------------- #
@@ -184,8 +166,8 @@ module Hobo
184
166
  end
185
167
 
186
168
  def method_callable_by?(user, method)
187
- permission_method = "#{method}_call_permitted?"
188
- respond_to?(permission_method) && with_acting_user(current_user) { send(permission_method) }
169
+ permission_method = "#{method}_permitted?"
170
+ respond_to?(permission_method) && with_acting_user(user) { send(permission_method) }
189
171
  end
190
172
 
191
173
  def viewable_by?(user, attribute=nil)
@@ -337,20 +319,30 @@ module Hobo
337
319
 
338
320
  # By default, attempt to derive edit permission from create/update permission
339
321
  def edit_permitted?(attribute)
340
- Hobo::Permissions.unknownify_attribute(self, attribute) if attribute
322
+ if attribute
323
+ with_attribute_or_belongs_to_keys(attribute) do |attr, ftype|
324
+ unknownify_attribute(self, attr)
325
+ unknownify_attribute(self, ftype) if ftype
326
+ end
327
+ end
341
328
  new_record? ? create_permitted? : update_permitted?
342
329
  rescue Hobo::UndefinedAccessError
343
330
  # The permission is dependent on the unknown value
344
331
  # so this attribute is not editable
345
332
  false
346
333
  ensure
347
- Hobo::Permissions.deunknownify_attribute(self, attribute) if attribute
334
+ if attribute
335
+ with_attribute_or_belongs_to_keys(attribute) do |attr, ftype|
336
+ deunknownify_attribute(self, attr)
337
+ deunknownify_attribute(self, ftype) if ftype
338
+ end
339
+ end
348
340
  end
349
341
 
350
342
 
351
343
  # Add some singleton methods to +record+ so give the effect that +attribute+ is unknown. That is,
352
344
  # attempts to access the attribute will result in a Hobo::UndefinedAccessError
353
- def self.unknownify_attribute(record, attr)
345
+ def unknownify_attribute(record, attr)
354
346
  record.metaclass.class_eval do
355
347
 
356
348
  define_method attr do
@@ -386,7 +378,7 @@ module Hobo
386
378
  end
387
379
 
388
380
  # Best. Name. Ever
389
- def self.deunknownify_attribute(record, attr)
381
+ def deunknownify_attribute(record, attr)
390
382
  [attr, "#{attr}_change", "#{attr}_was", "#{attr}_changed?", :changed?, :changed, :changes].each do |m|
391
383
  record.metaclass.send :remove_method, m.to_sym
392
384
  end
@@ -1,16 +1,12 @@
1
1
  class HoboGenerator < Rails::Generator::Base
2
2
 
3
3
  def manifest
4
+ if options[:add_gem]
5
+ add_to_file "config/environment.rb", "Rails::Initializer.run do |config|", " config.gem 'hobo'\n"
6
+ end
7
+
4
8
  if options[:add_routes]
5
- routes_path = File.join(RAILS_ROOT, "config/routes.rb")
6
-
7
- route = " Hobo.add_routes(map)\n"
8
- route_src = File.read(routes_path)
9
- unless route_src.include?(route)
10
- head = "ActionController::Routing::Routes.draw do |map|"
11
- route_src.sub!(head, head + "\n\n" + route)
12
- File.open(routes_path, 'w') {|f| f.write(route_src) }
13
- end
9
+ add_to_file "config/routes.rb", "ActionController::Routing::Routes.draw do |map|", "\n Hobo.add_routes(map)\n"
14
10
  end
15
11
 
16
12
  record do |m|
@@ -32,7 +28,7 @@ class HoboGenerator < Rails::Generator::Base
32
28
 
33
29
  protected
34
30
  def banner
35
- "Usage: #{$0} #{spec.name} [--add-routes]"
31
+ "Usage: #{$0} #{spec.name} [--add-routes] [--add-gem]"
36
32
  end
37
33
 
38
34
  def add_options!(opt)
@@ -40,5 +36,17 @@ class HoboGenerator < Rails::Generator::Base
40
36
  opt.separator 'Options:'
41
37
  opt.on("--add-routes",
42
38
  "Add Hobo routes to config/routes.rb") { |v| options[:add_routes] = v }
39
+ opt.on("--add-gem",
40
+ "Edit environment.rb to require the hobo gem") { |v| options[:add_gem] = v }
43
41
  end
42
+
43
+ def add_to_file(filename, after_line, new_line)
44
+ filename = File.join(RAILS_ROOT, filename)
45
+ src = File.read filename
46
+ unless src.include? new_line
47
+ src.sub!(after_line, after_line + "\n" + new_line)
48
+ File.open(filename, 'w') {|f| f.write(src) }
49
+ end
50
+ end
51
+
44
52
  end
@@ -1,9 +1 @@
1
- # Load Hobo from the gem if is not already loaded
2
- # (i.e. if the plugin is not present)
3
-
4
- unless defined? Hobo
5
- gem 'hobo'
6
- require 'hobo'
7
- end
8
-
9
1
  Hobo::ModelRouter.reload_routes_on_every_request = true
@@ -773,8 +773,8 @@ Use the `uri` option to specify a redirect location:
773
773
 
774
774
 
775
775
  <def tag="or-cancel">
776
- <if test="&linkable?">or <a>Cancel</a></if>
776
+ <if test="&linkable?">or <a merge-attrs>Cancel</a></if>
777
777
  <else>
778
- <if test="&linkable?(this.class)">or <a to="&this.class">Cancel</a></if>
778
+ <if test="&linkable?(this.class)">or <a to="&this.class" merge-attrs>Cancel</a></if>
779
779
  </else>
780
780
  </def>
Binary file
@@ -254,6 +254,18 @@ class PermissionsTest < Test::Unit::TestCase
254
254
  assert_equal("code.zip", @r.code_example.filename)
255
255
  end
256
256
 
257
+ should "allow the code example related to a recipe to be changed" do
258
+ r = existing_recipe @user
259
+ c1 = CodeExample.create! :filename => "exmaple1.zip"
260
+ c2 = CodeExample.create! :filename => "exmaple2.zip"
261
+ r.code_example = c1
262
+ r.save
263
+
264
+ r.user_update_attributes! @user, :code_example => "@#{c2.id}"
265
+
266
+ assert_equal(c2, r.code_example)
267
+ end
268
+
257
269
  context "on an existing recipe with a code example" do
258
270
  setup do
259
271
  @ce = CodeExample.create! :filename => "exmaple.zip"
@@ -261,8 +273,11 @@ class PermissionsTest < Test::Unit::TestCase
261
273
  end
262
274
 
263
275
  should "allow update of the code-example" do
264
- @r.user_update_attributes! @user, :code_example => { :filename => "changed.zip" }
276
+ # To update the exsting target of a belongs_to association, the id must be included in the hash
277
+ # It is an error to provide any id other than that of the existing target
278
+ @r.user_update_attributes! @user, :code_example => { :id => @ce.id, :filename => "changed.zip" }
265
279
  assert_equal("changed.zip", @r.code_example.filename)
280
+ assert_equal(@ce, @r.code_example)
266
281
  end
267
282
 
268
283
  should "allow removal of the code-example" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hobo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.4
4
+ version: 0.8.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Locke
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-06 00:00:00 +00:00
12
+ date: 2008-12-09 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - "="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.8.4
23
+ version: 0.8.5
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: hobofields
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - "="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.8.4
33
+ version: 0.8.5
34
34
  version:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: rails