attribute_ext 1.1.0 → 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -58,6 +58,44 @@ or
58
58
  class User < ActiveRecord::Base
59
59
  safe_attributes :login, :if => Proc.new { |user,role| role == :admin }
60
60
  end
61
+
62
+ Default role and role mapper:
63
+
64
+ SafeAttributes provides helper for handling roles including a method to set
65
+ a new default role as well as a method to map roles to other values. Changes to
66
+ role will only affect SafeAttributes and will not be given to Rails 3.1 mass
67
+ assignment authorizer.
68
+
69
+ Set default role that will be used if given role is nil or :default.
70
+
71
+ AttributeExt::SafeAttributes.default_role = :new_default
72
+
73
+ Role values can be restricted to specific values using the role mapper.
74
+
75
+ AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
76
+ [:guest, :user, :admin].include?(role) ? role : :guest
77
+ end
78
+
79
+ or
80
+
81
+ AttributeExt::SafeAttributes.role_mapper do |role|
82
+ [:guest, :user, :admin].include?(role) ? role : :guest
83
+ end
84
+
85
+ The role mapper is especially usefull if you want the current user model be the
86
+ default role.
87
+
88
+ AttributeExt::SafeAttributes.role_mapper do |role|
89
+ role.is_a?(User) ? role : User.current
90
+ end
91
+
92
+ You can perform checks like this now:
93
+
94
+ class User < ActiveRecord::Base
95
+ safe_attribute :email, :if => Proc.new { |user,role| user == role or role.admin? }
96
+ end
97
+
98
+ Now the user can edit there own emails or everyons email if it is an admin.
61
99
 
62
100
 
63
101
  AttributeExt::HiddenAttributes
@@ -115,6 +153,12 @@ By default rules *do not* apply when serializing to hash.
115
153
  Changelog
116
154
  ---------
117
155
 
156
+ Sep 24, 2011
157
+
158
+ SafeAttributes provides methods to change default role and to map roles to
159
+ specific values before processing rules. Also added full documentation to
160
+ all public methods and methods that are usefull for testing own rules.
161
+
118
162
  Sep 22, 2011
119
163
 
120
164
  Nearly all features are successfully tested using a fake environment now.
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "attribute_ext"
6
- s.version = "1.1.0"
6
+ s.version = "1.2.4"
7
7
  s.authors = ["Jan Graichen"]
8
8
  s.email = ["jan.graichen@altimos.de"]
9
9
  s.homepage = "https://github.com/jgraichen/attribute_ext"
data/init.rb CHANGED
@@ -1,5 +1,4 @@
1
1
 
2
2
  require 'attribute_ext'
3
3
 
4
- ActiveRecord::Base.send :include, AttributeExt::HiddenAttributes
5
- ActiveRecord::Base.send :include, AttributeExt::SafeAttributes
4
+ AttributeExt.setup
@@ -2,3 +2,10 @@
2
2
  require 'attribute_ext/hidden_attributes'
3
3
  require 'attribute_ext/safe_attributes'
4
4
  require 'attribute_ext/railtie' if defined?(Rails)
5
+
6
+ module AttributeExt
7
+ def AttributeExt.setup # :nodoc:
8
+ ActiveRecord::Base.send :include, AttributeExt::HiddenAttributes
9
+ ActiveRecord::Base.send :include, AttributeExt::SafeAttributes
10
+ end
11
+ end
@@ -1,6 +1,6 @@
1
1
  module AttributeExt
2
2
  module HiddenAttributes
3
- def self.included(base)
3
+ def self.included(base) # :nodoc:
4
4
  base.extend(ClassMethods)
5
5
  base.alias_method_chain :to_xml, :hidden_attrs
6
6
  base.alias_method_chain :as_json, :hidden_attrs
@@ -8,6 +8,39 @@ module AttributeExt
8
8
  end
9
9
 
10
10
  module ClassMethods
11
+ # Adds attribute to a blacklist that will be hidden when serializing if optional conditions
12
+ # are true.
13
+ #
14
+ # class User < ActiveRecord::Base
15
+ # hide_attributes :password # always hide
16
+ # hide_attributes :email, :if => Proc.new { |user| user.hide_email? }
17
+ # hide_attributes :not_in_json, :only => :json
18
+ # hide_attributes :except_xml_hash, :except => [:xml, :hash]
19
+ # end
20
+ #
21
+ # All given conditions to a rule must be true if attributes should be hidden. Attributes can
22
+ # appear in more than one rule.
23
+ #
24
+ # Options:
25
+ # [:+if+]
26
+ # Requires a Proc block to be true.
27
+ #
28
+ # [:+unless+]
29
+ # Requires a Proc block to be false.
30
+ #
31
+ # [:+only+]
32
+ # Requires export format to be in given array. A non array object will be converted in to
33
+ # an array only containing given object.
34
+ #
35
+ # [:+except+]
36
+ # Requires export format to not be in given array. A non array object will be converted in
37
+ # to an array only containing given object.
38
+ #
39
+ # [:+on_hash+]
40
+ # By default rules will not be applied when serializing to hash when no :only or :except
41
+ # rule is specified. If :on_hash is true rule will also apply to hash serialization. If an
42
+ # :only or :except option is given :on_hash does nothing.
43
+ #
11
44
  def hide_attributes(*attrs)
12
45
  @hidden_attributes ||= []
13
46
  if attrs.empty?
@@ -34,14 +67,14 @@ module AttributeExt
34
67
  end
35
68
  end
36
69
 
37
- def to_xml_with_hidden_attrs(options = nil, &block)
70
+ def to_xml_with_hidden_attrs(options = nil, &block) # :nodoc:
38
71
  options ||= {}
39
72
  options[:except] = hidden_attribute_names(:xml, options)
40
73
 
41
74
  to_xml_without_hidden_attrs(options)
42
75
  end
43
76
 
44
- def as_json_with_hidden_attrs(options = nil, &block)
77
+ def as_json_with_hidden_attrs(options = nil, &block) # :nodoc:
45
78
  options ||= {}
46
79
  options[:except] = hidden_attribute_names(:json, options)
47
80
  options[:hidden_attributes_format] = :json
@@ -49,13 +82,20 @@ module AttributeExt
49
82
  as_json_without_hidden_attrs(options)
50
83
  end
51
84
 
52
- def serializable_hash_with_hidden_attrs(options = nil)
85
+ def serializable_hash_with_hidden_attrs(options = nil) # :nodoc:
53
86
  options ||= {}
54
87
  options[:except] = hidden_attribute_names((options[:hidden_attributes_format] || :hash), options)
55
88
 
56
89
  serializable_hash_without_hidden_attrs(options)
57
90
  end
58
91
 
92
+ # Returns an array with attributes to hide from serialization.
93
+ #
94
+ # This method should only be used to test own rules without need to run a formatter and
95
+ # validate the generated output. See AttributeExt specs for details.
96
+ #
97
+ # hidden_attribute_names :format, :options => :hash
98
+ #
59
99
  def hidden_attribute_names(format, options = {})
60
100
  if options[:except].is_a?(Array)
61
101
  names = options[:except]
@@ -1,10 +1,9 @@
1
1
 
2
2
  module AttributeExt
3
- class Railtie < Rails::Railtie
3
+ class Railtie < Rails::Railtie # :nodoc:
4
4
  initializer 'attribute_ext' do |app|
5
5
  ActiveSupport.on_load :active_record do
6
- ActiveRecord::Base.send :include, AttributeExt::HiddenAttributes
7
- ActiveRecord::Base.send :include, AttributeExt::SafeAttributes
6
+ AttributeExt.setup
8
7
  end
9
8
  end
10
9
  end
@@ -1,11 +1,81 @@
1
1
  module AttributeExt
2
2
  module SafeAttributes
3
- def self.included(base)
3
+ # Returns default role used by SafeAttributes.
4
+ # See SafeAttributes#default_role= for how to specify a default role.
5
+ def SafeAttributes.default_role
6
+ @default_role || :default
7
+ end
8
+
9
+ # Sets SafeAttributes default role that will be used when given role
10
+ # is a nil value or the :default role. The SafeAttributes default role will
11
+ # only affect this extension and will not be given to Rails 3.1 mass
12
+ # assignment authorizer.
13
+ def SafeAttributes.default_role=(role)
14
+ @default_role = role
15
+ end
16
+
17
+ # Returns current role mapper block or sets role mapper if an block is
18
+ # given. By default no role mapper is active.
19
+ #
20
+ # AttributeExt::SafeAttributes.role_mapper do |role|
21
+ # [:guest, :user, :admin].include?(role) ? role : :guest
22
+ # end
23
+ #
24
+ def SafeAttributes.role_mapper(&block)
25
+ self.role_mapper = block if block
26
+ @role_mapper
27
+ end
28
+
29
+ # Sets current role mapper to given Proc or removes role mapper if
30
+ # a nil value is given. Any other value will do nothing.
31
+ #
32
+ # AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
33
+ # [:guest, :user, :admin].include?(role) ? role : :guest
34
+ # end
35
+ #
36
+ # See SafeAttributes#role_mapper for an short way to set a role mapper.
37
+ def SafeAttributes.role_mapper=(role_mapper)
38
+ @role_mapper = role_mapper if role_mapper.is_a?(Proc)
39
+ @role_mapper = nil if role_mapper.nil?
40
+ end
41
+
42
+ def self.included(base) # :nodoc:
4
43
  base.extend(ClassMethods)
5
44
  base.alias_method_chain :mass_assignment_authorizer, :safe_attrs
6
45
  end
7
46
 
47
+
8
48
  module ClassMethods
49
+ # Adds a whitelist rule that allows mass assignment for given attributes
50
+ # based on given optional conditions.
51
+ #
52
+ # class User < ActiveRecord::Base
53
+ # # always mass assignable
54
+ # safe_attributes :name, :email
55
+ # # only when new record
56
+ # safe_attributes :login, :if => Proc.new { |user| user.new_record? }
57
+ # # only own password or as admin
58
+ # safe_attributes :password, :if => Proc.new { |user,role| user == role }
59
+ # safe_attributes :password, :as => :admin
60
+ # end
61
+ #
62
+ # All given conditions for one rule must be true to allow mass
63
+ # assignment for given attributes. Attributes can be added in more than
64
+ # one rule to allow alternatives (like password above).
65
+ #
66
+ # Available Options:
67
+ # [:+as+]
68
+ # Attributes will be assignable if mass assignment role is equal (==) given object.
69
+ #
70
+ # [:+if+]
71
+ # Makes attributes assignable if given Proc block returns true.
72
+ #
73
+ # [:+unless+]
74
+ # Attributes cannot be mass assigned if Proc block evaluates to true.
75
+ #
76
+ # The :if and :unless options must be Proc block that will be executed each time the
77
+ # mass assignment authorizer is called and they are called with current
78
+ # model and role as parameters.
9
79
  def safe_attributes(*attrs)
10
80
  @safe_attributes ||= []
11
81
  if attrs.empty?
@@ -26,12 +96,34 @@ module AttributeExt
26
96
  end
27
97
  end
28
98
 
29
- def mass_assignment_authorizer_with_safe_attrs(role = nil)
30
- attrs = role.nil? ? mass_assignment_authorizer_without_safe_attrs : mass_assignment_authorizer_without_safe_attrs(role)
31
- attrs += safe_attribute_names(role ? role : :default)
99
+ def mass_assignment_authorizer_with_safe_attrs(role = nil) # :nodoc:
100
+ if role.nil?
101
+ attrs = mass_assignment_authorizer_without_safe_attrs +
102
+ safe_attribute_names
103
+ else
104
+ attrs = mass_assignment_authorizer_without_safe_attrs(role) +
105
+ safe_attribute_names(role)
106
+ end
107
+ end
108
+
109
+ # Returns new mapped role for given role used by SafeAttributes.
110
+ # This method should only be used to test own role mapper implementations without need for a
111
+ # full application. See AttributeExt specs for details.
112
+ #
113
+ # See +role_mapper+ method in SafeAttributes module for how to set a role mapper.
114
+ def safe_attributes_role(role = nil)
115
+ return AttributeExt::SafeAttributes.role_mapper.call(role) unless AttributeExt::SafeAttributes.role_mapper.nil?
116
+ return AttributeExt::SafeAttributes.default_role if role.nil? or role == :default
117
+ role
32
118
  end
33
119
 
34
- def safe_attribute_names(role = :default)
120
+ # Returns an array with attributes allowed to be mass assigned by given role. Role will be
121
+ # mapped before given to rules.
122
+ # This method should only be used to test own rules without need to create lots of records
123
+ # to test different situations. See AttributeExt specs for details.
124
+ def safe_attribute_names(role = nil)
125
+ role = safe_attributes_role(role)
126
+
35
127
  names = []
36
128
  self.class.safe_attributes.collect do |attrs, options|
37
129
  next unless options[:as].empty? or options[:as].include?(role)
@@ -62,4 +62,61 @@ describe AttributeExt::HiddenAttributes do
62
62
  user = User.new
63
63
  user.mass_assignment_authorizer(:admin).should_not include('attribute_unless_admin')
64
64
  end
65
+
66
+ it 'can provide a global default role' do
67
+ AttributeExt::SafeAttributes.default_role = :new_default
68
+ AttributeExt::SafeAttributes.default_role.should == :new_default
69
+ User.new.mass_assignment_authorizer.should include("new_default")
70
+ end
71
+
72
+ context '#role_mapper' do
73
+ it 'is nil by default' do
74
+ AttributeExt::SafeAttributes.role_mapper.should be_nil
75
+ end
76
+
77
+ it 'accept Procs' do
78
+ proc = Proc.new { |role| role }
79
+ AttributeExt::SafeAttributes.role_mapper = proc
80
+ AttributeExt::SafeAttributes.role_mapper.should equal(proc)
81
+ end
82
+
83
+ it 'accept nil' do
84
+ AttributeExt::SafeAttributes.role_mapper = nil
85
+ AttributeExt::SafeAttributes.role_mapper.should be_nil
86
+ end
87
+
88
+ it 'accept blocks' do
89
+ AttributeExt::SafeAttributes.role_mapper { |role| role }
90
+ AttributeExt::SafeAttributes.role_mapper.should be_an Proc
91
+ end
92
+
93
+ it 'maps role according to given Proc' do
94
+ AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
95
+ [:guest, :user, :admin].include?(role) ? role : :guest
96
+ end
97
+
98
+ User.new.safe_attributes_role.should == :guest
99
+ User.new.safe_attributes_role(:user).should == :user
100
+ User.new.safe_attributes_role(:admin).should == :admin
101
+ User.new.safe_attributes_role(:heinz).should == :guest
102
+ end
103
+
104
+ it 'gives role to rules' do
105
+ AttributeExt::SafeAttributes.role_mapper = Proc.new do |role|
106
+ [:guest, :user, :admin].include?(role) ? role : :guest
107
+ end
108
+
109
+ User.new.mass_assignment_authorizer.should include('role_mapper_guest')
110
+ User.new.mass_assignment_authorizer.should_not include('role_mapper_user')
111
+ User.new.mass_assignment_authorizer.should_not include('role_mapper_admin')
112
+
113
+ User.new.mass_assignment_authorizer(:user).should_not include('role_mapper_guest')
114
+ User.new.mass_assignment_authorizer(:user).should include('role_mapper_user')
115
+ User.new.mass_assignment_authorizer(:user).should_not include('role_mapper_admin')
116
+
117
+ User.new.mass_assignment_authorizer(:admin).should_not include('role_mapper_guest')
118
+ User.new.mass_assignment_authorizer(:admin).should_not include('role_mapper_user')
119
+ User.new.mass_assignment_authorizer(:admin).should include('role_mapper_admin')
120
+ end
121
+ end
65
122
  end
@@ -54,6 +54,10 @@ class User < ActiveRecord::Base
54
54
  :if => Proc.new { |user,role| role == :admin}
55
55
  safe_attributes :attribute_unless_admin,
56
56
  :unless => Proc.new { |user,role| role == :admin}
57
+ safe_attributes :new_default, :as => :new_default
58
+ safe_attributes :role_mapper_guest, :if => Proc.new { |user,role| role == :guest }
59
+ safe_attributes :role_mapper_user, :as => :user
60
+ safe_attributes :role_mapper_admin, :if => Proc.new { |user,role| role == :admin }
57
61
 
58
62
  def initialize(opts = {})
59
63
  @opts = {
@@ -61,4 +65,4 @@ class User < ActiveRecord::Base
61
65
  :unless => true
62
66
  }.merge(opts.is_a?(Hash) ? opts : {})
63
67
  end
64
- end
68
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attribute_ext
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 1
9
- - 0
10
- version: 1.1.0
8
+ - 2
9
+ - 4
10
+ version: 1.2.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jan Graichen
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-09-22 00:00:00 Z
18
+ date: 2011-09-24 00:00:00 Z
19
19
  dependencies: []
20
20
 
21
21
  description: AttributeExt provides additional access control for rails model attributes.