hobo 0.8.4 → 0.8.5

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