active_attr 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of active_attr might be problematic. Click here for more details.
- data/CHANGELOG.md +15 -0
- data/README.md +83 -11
- data/active_attr.gemspec +4 -3
- data/lib/active_attr.rb +5 -0
- data/lib/active_attr/attributes.rb +72 -24
- data/lib/active_attr/block_initialization.rb +37 -0
- data/lib/active_attr/chainable_initialization.rb +6 -4
- data/lib/active_attr/dangerous_attribute_error.rb +11 -0
- data/lib/active_attr/logger.rb +46 -0
- data/lib/active_attr/mass_assignment_security.rb +42 -0
- data/lib/active_attr/query_attributes.rb +62 -0
- data/lib/active_attr/railtie.rb +10 -0
- data/lib/active_attr/unknown_attribute_error.rb +19 -0
- data/lib/active_attr/version.rb +1 -1
- data/spec/functional/active_attr/attributes_spec.rb +104 -0
- data/spec/functional/active_attr/query_attributes_spec.rb +32 -0
- data/spec/support/mass_assignment_shared_examples.rb +9 -0
- data/spec/unit/active_attr/attributes_spec.rb +73 -39
- data/spec/unit/active_attr/block_initialization_spec.rb +39 -0
- data/spec/unit/active_attr/dangerous_attribute_error_spec.rb +9 -0
- data/spec/unit/active_attr/logger_spec.rb +186 -0
- data/spec/unit/active_attr/mass_assignment_security_spec.rb +62 -0
- data/spec/unit/active_attr/query_attributes_spec.rb +225 -0
- data/spec/unit/active_attr/unknown_attribute_error_spec.rb +9 -0
- metadata +48 -17
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
# ActiveAttr 0.3.0 (November 26, 2011) #
|
2
|
+
|
3
|
+
* Added BlockInitialization
|
4
|
+
* Added DangerousAttributeError
|
5
|
+
* Added Logger
|
6
|
+
* Added MassAssignmentSecurity
|
7
|
+
* Added QueryAttributes
|
8
|
+
* Added UnknownAttributeError
|
9
|
+
* Attributes now honors getters/setters when calling #read_attribute,
|
10
|
+
#write_attribute, #[], and #[]=
|
11
|
+
* Attributes now raises DangerousAttributeError when defining an attribute
|
12
|
+
whose methods would conflict with an existing method
|
13
|
+
* Attributes now raises UnknownAttributeError when getting/setting any
|
14
|
+
undefined attributes
|
15
|
+
|
1
16
|
# ActiveAttr 0.2.2 (November 2, 2011) #
|
2
17
|
|
3
18
|
* Fixed all instances of modules' #initialize not invoking its superclass
|
data/README.md
CHANGED
@@ -24,10 +24,10 @@ the attributes of your model.
|
|
24
24
|
attribute :last_name
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
person = Person.new
|
28
|
+
person.first_name = "Chris"
|
29
|
+
person.last_name = "Griego"
|
30
|
+
person.attributes #=> {"first_name"=>"Chris", "last_name"=>"Griego"}
|
31
31
|
|
32
32
|
### BasicModel ###
|
33
33
|
|
@@ -39,9 +39,48 @@ required for your model to meet the ActiveModel API requirements.
|
|
39
39
|
end
|
40
40
|
|
41
41
|
Person.model_name.plural #=> "people"
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
person = Person.new
|
43
|
+
person.valid? #=> true
|
44
|
+
person.errors.full_messages #=> []
|
45
|
+
|
46
|
+
### BlockInitialization ###
|
47
|
+
|
48
|
+
Including the BlockInitialization module into your class will yield the model
|
49
|
+
instance to a block passed to when creating a new instance.
|
50
|
+
|
51
|
+
class Person
|
52
|
+
include ActiveAttr::BlockInitialization
|
53
|
+
attr_accessor :first_name, :last_name
|
54
|
+
end
|
55
|
+
|
56
|
+
person = Person.new do |p|
|
57
|
+
p.first_name = "Chris"
|
58
|
+
p.last_name = "Griego"
|
59
|
+
end
|
60
|
+
|
61
|
+
person.first_name #=> "Chris"
|
62
|
+
person.last_name #=> "Griego"
|
63
|
+
|
64
|
+
### Logger ###
|
65
|
+
|
66
|
+
Including the Logger module into your class will give you access to a
|
67
|
+
configurable logger in model classes and instances. Your preferred logger can
|
68
|
+
be configured on an instance, subclass, class, parent class, and globally by
|
69
|
+
setting ActiveAttr::Logger.logger. When using Rails, the Rails framework
|
70
|
+
logger will be configured by default.
|
71
|
+
|
72
|
+
class Person
|
73
|
+
include ActiveAttr::Logger
|
74
|
+
end
|
75
|
+
|
76
|
+
Person.logger = Logger.new(STDOUT)
|
77
|
+
Person.logger? #=> true
|
78
|
+
Person.logger.info "Logging an informational message"
|
79
|
+
|
80
|
+
person = Person.new
|
81
|
+
person.logger? #=> true
|
82
|
+
person.logger = Logger.new(STDERR)
|
83
|
+
person.logger.warn "Logging a warning message"
|
45
84
|
|
46
85
|
### MassAssignment ###
|
47
86
|
|
@@ -56,10 +95,43 @@ attribute.
|
|
56
95
|
attr_accessor :first_name, :last_name
|
57
96
|
end
|
58
97
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
98
|
+
person = Person.new(:first_name => "Chris")
|
99
|
+
person.attributes = { :last_name => "Griego" }
|
100
|
+
person.first_name #=> "Chris"
|
101
|
+
person.last_name #=> "Griego"
|
102
|
+
|
103
|
+
### MassAssignmentSecurity ###
|
104
|
+
|
105
|
+
Including the MassAssignmentSecurity module into your class extends the
|
106
|
+
MassAssignment methods to honor any declared mass assignment permission
|
107
|
+
blacklists or whitelists including support for mass assignment roles.
|
108
|
+
|
109
|
+
class Person
|
110
|
+
include ActiveAttr::MassAssignment
|
111
|
+
attr_accessor :first_name, :last_name
|
112
|
+
attr_protected :last_name
|
113
|
+
end
|
114
|
+
|
115
|
+
person = Person.new(:first_name => "Chris", :last_name => "Griego")
|
116
|
+
person.first_name #=> "Chris"
|
117
|
+
person.last_name #=> nil
|
118
|
+
|
119
|
+
### QueryAttributes ###
|
120
|
+
|
121
|
+
Including the QueryAttributes module into your class builds on Attributes by
|
122
|
+
providing instance methods for querying your attributes
|
123
|
+
|
124
|
+
class Person
|
125
|
+
include ActiveAttr::QueryAttributes
|
126
|
+
|
127
|
+
attribute :first_name
|
128
|
+
attribute :last_name
|
129
|
+
end
|
130
|
+
|
131
|
+
person = Person.new
|
132
|
+
person.first_name = "Chris"
|
133
|
+
person.first_name? #=> true
|
134
|
+
person.last_name? #=> false
|
63
135
|
|
64
136
|
## RSpec Integration ##
|
65
137
|
|
data/active_attr.gemspec
CHANGED
@@ -21,7 +21,8 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_runtime_dependency "activemodel", "~> 3.1"
|
22
22
|
s.add_runtime_dependency "activesupport", "~> 3.1"
|
23
23
|
|
24
|
-
s.add_development_dependency "bundler",
|
25
|
-
s.add_development_dependency "
|
26
|
-
s.add_development_dependency "
|
24
|
+
s.add_development_dependency "bundler", "~> 1.0"
|
25
|
+
s.add_development_dependency "factory_girl", "~> 2.2"
|
26
|
+
s.add_development_dependency "rake", "~> 0.9.0"
|
27
|
+
s.add_development_dependency "rspec", "~> 2.6"
|
27
28
|
end
|
data/lib/active_attr.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "active_attr/railtie" if defined?(Rails)
|
1
2
|
require "active_support/dependencies/autoload"
|
2
3
|
|
3
4
|
# ActiveAttr is a set of modules to enhance Plain Old Ruby Objects (POROs)
|
@@ -13,8 +14,12 @@ module ActiveAttr
|
|
13
14
|
autoload :BasicModel
|
14
15
|
autoload :ChainableInitialization
|
15
16
|
autoload :Error
|
17
|
+
autoload :Logger
|
16
18
|
autoload :MassAssignment
|
19
|
+
autoload :MassAssignmentSecurity
|
20
|
+
autoload :QueryAttributes
|
17
21
|
autoload :StrictMassAssignment
|
22
|
+
autoload :UnknownAttributeError
|
18
23
|
autoload :UnknownAttributesError
|
19
24
|
autoload :VERSION
|
20
25
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require "active_attr/attribute_definition"
|
2
2
|
require "active_attr/chainable_initialization"
|
3
|
+
require "active_attr/dangerous_attribute_error"
|
4
|
+
require "active_attr/unknown_attribute_error"
|
3
5
|
require "active_model"
|
4
6
|
require "active_support/concern"
|
5
7
|
|
@@ -22,6 +24,10 @@ module ActiveAttr
|
|
22
24
|
include ActiveAttr::ChainableInitialization
|
23
25
|
include ActiveModel::AttributeMethods
|
24
26
|
|
27
|
+
# Methods deprecated on the Object class which can be safely overridden
|
28
|
+
# @since 0.3.0
|
29
|
+
DEPRECATED_OBJECT_METHODS = %w(id type)
|
30
|
+
|
25
31
|
included do
|
26
32
|
attribute_method_suffix ""
|
27
33
|
attribute_method_suffix "="
|
@@ -42,7 +48,7 @@ module ActiveAttr
|
|
42
48
|
attributes == other.attributes
|
43
49
|
end
|
44
50
|
|
45
|
-
# Returns
|
51
|
+
# Returns a Hash of all attributes
|
46
52
|
#
|
47
53
|
# @example Get attributes
|
48
54
|
# person.attributes # => {"name"=>"Ben Poweski"}
|
@@ -70,48 +76,79 @@ module ActiveAttr
|
|
70
76
|
#
|
71
77
|
# @since 0.2.0
|
72
78
|
def inspect
|
73
|
-
attribute_descriptions = self.
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
attribute_descriptions = " " + attribute_descriptions unless attribute_descriptions.empty?
|
78
|
-
|
79
|
-
"#<#{self.class.name}#{attribute_descriptions}>"
|
79
|
+
attribute_descriptions = self.attributes.sort.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
|
80
|
+
separator = " " unless attribute_descriptions.empty?
|
81
|
+
"#<#{self.class.name}#{separator}#{attribute_descriptions}>"
|
80
82
|
end
|
81
83
|
|
82
|
-
# Read a value from the model's attributes.
|
83
|
-
# it will return nil.
|
84
|
+
# Read a value from the model's attributes.
|
84
85
|
#
|
85
|
-
# @example Read an attribute
|
86
|
+
# @example Read an attribute with read_attribute
|
86
87
|
# person.read_attribute(:name)
|
88
|
+
# @example Rean an attribute with bracket syntax
|
89
|
+
# person[:name]
|
87
90
|
#
|
88
|
-
# @param [String, Symbol] name The name of the attribute to get.
|
91
|
+
# @param [String, Symbol, #to_s] name The name of the attribute to get.
|
89
92
|
#
|
90
93
|
# @return [Object] The value of the attribute.
|
91
94
|
#
|
95
|
+
# @raise [UnknownAttributeError] if the attribute is unknown
|
96
|
+
#
|
92
97
|
# @since 0.2.0
|
93
98
|
def read_attribute(name)
|
94
|
-
|
99
|
+
if respond_to? name
|
100
|
+
send name.to_s
|
101
|
+
else
|
102
|
+
raise UnknownAttributeError, "unknown attribute: #{name}"
|
103
|
+
end
|
95
104
|
end
|
96
105
|
alias_method :[], :read_attribute
|
97
|
-
alias_method :attribute, :read_attribute
|
98
|
-
private :attribute
|
99
106
|
|
100
107
|
# Write a single attribute to the model's attribute hash.
|
101
108
|
#
|
102
|
-
# @example Write the attribute
|
109
|
+
# @example Write the attribute with write_attribute
|
103
110
|
# person.write_attribute(:name, "Benjamin")
|
111
|
+
# @example Write an attribute with bracket syntax
|
112
|
+
# person[:name] = "Benjamin"
|
104
113
|
#
|
105
|
-
# @param [String, Symbol] name The name of the attribute to update.
|
114
|
+
# @param [String, Symbol, #to_s] name The name of the attribute to update.
|
106
115
|
# @param [Object] value The value to set for the attribute.
|
107
116
|
#
|
117
|
+
# @raise [UnknownAttributeError] if the attribute is unknown
|
118
|
+
#
|
108
119
|
# @since 0.2.0
|
109
120
|
def write_attribute(name, value)
|
110
|
-
|
121
|
+
if respond_to? "#{name}="
|
122
|
+
send "#{name}=", value
|
123
|
+
else
|
124
|
+
raise UnknownAttributeError, "unknown attribute: #{name}"
|
125
|
+
end
|
111
126
|
end
|
112
127
|
alias_method :[]=, :write_attribute
|
113
|
-
|
114
|
-
|
128
|
+
|
129
|
+
protected
|
130
|
+
|
131
|
+
# Overrides ActiveModel::AttributeMethods
|
132
|
+
# @private
|
133
|
+
def attribute_method?(attr_name)
|
134
|
+
self.class.attributes.map { |definition| definition.name.to_s }.include? attr_name.to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
# Read an attribute from the attributes hash
|
140
|
+
#
|
141
|
+
# @since 0.2.1
|
142
|
+
def attribute(name)
|
143
|
+
@attributes[name.to_s]
|
144
|
+
end
|
145
|
+
|
146
|
+
# Write an attribute to the attributes hash
|
147
|
+
#
|
148
|
+
# @since 0.2.1
|
149
|
+
def attribute=(name, value)
|
150
|
+
@attributes[name.to_s] = value
|
151
|
+
end
|
115
152
|
|
116
153
|
module ClassMethods
|
117
154
|
# Defines all the attributes that are to be returned from the attributes instance method.
|
@@ -124,11 +161,15 @@ module ActiveAttr
|
|
124
161
|
#
|
125
162
|
# @param (see AttributeDefinition#initialize)
|
126
163
|
#
|
164
|
+
# @raise [DangerousAttributeError] if the attribute name conflicts with existing methods
|
165
|
+
#
|
127
166
|
# @since 0.2.0
|
128
167
|
def attribute(name, options={})
|
129
168
|
AttributeDefinition.new(name, options).tap do |attribute_definition|
|
130
|
-
|
131
|
-
|
169
|
+
unless attributes.include? attribute_definition
|
170
|
+
define_attribute_method attribute_definition.name
|
171
|
+
attributes << attribute_definition
|
172
|
+
end
|
132
173
|
end
|
133
174
|
end
|
134
175
|
|
@@ -169,12 +210,19 @@ module ActiveAttr
|
|
169
210
|
@attributes = attributes
|
170
211
|
end
|
171
212
|
|
213
|
+
# Overrides ActiveModel::AttributeMethods
|
214
|
+
# @private
|
215
|
+
def instance_method_already_implemented?(method_name)
|
216
|
+
deprecated_object_method = DEPRECATED_OBJECT_METHODS.include?(method_name.to_s)
|
217
|
+
already_implemented = !deprecated_object_method && self.allocate.respond_to?(method_name, true)
|
218
|
+
raise DangerousAttributeError, %{an attribute method named "#{method_name}" would conflict with an existing method} if already_implemented
|
219
|
+
false
|
220
|
+
end
|
221
|
+
|
172
222
|
private
|
173
223
|
|
174
224
|
# Ruby inherited hook to assign superclass attributes to subclasses
|
175
225
|
#
|
176
|
-
# @param [Class] subclass
|
177
|
-
#
|
178
226
|
# @since 0.2.2
|
179
227
|
def inherited(subclass)
|
180
228
|
super
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "active_attr/chainable_initialization"
|
2
|
+
require "active_support/concern"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
# BlockInitialization allows you to build an instance in a block
|
6
|
+
#
|
7
|
+
# Including the BlockInitialization module into your class will yield the
|
8
|
+
# model instance to a block passed to when creating a new instance.
|
9
|
+
#
|
10
|
+
# @example Usage
|
11
|
+
# class Person
|
12
|
+
# include ActiveAttr::BlockInitialization
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @since 0.3.0
|
16
|
+
module BlockInitialization
|
17
|
+
extend ActiveSupport::Concern
|
18
|
+
include ChainableInitialization
|
19
|
+
|
20
|
+
# Initialize a model and build via a block
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# person = Person.new do |p|
|
24
|
+
# p.first_name = "Chris"
|
25
|
+
# p.last_name = "Griego"
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# person.first_name #=> "Chris"
|
29
|
+
# person.last_name #=> "Griego"
|
30
|
+
#
|
31
|
+
# @since 0.3.0
|
32
|
+
def initialize(*)
|
33
|
+
super
|
34
|
+
yield self if block_given?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -20,18 +20,20 @@ module ActiveAttr
|
|
20
20
|
# A collection of Ruby base objects
|
21
21
|
# [Object] on Ruby 1.8
|
22
22
|
# [Object, BasicObject] on Ruby 1.9
|
23
|
-
|
24
|
-
|
23
|
+
#
|
24
|
+
# @private
|
25
|
+
BASE_OBJECTS = [].tap do |base_objects|
|
25
26
|
superclass = Class.new
|
26
27
|
base_objects << superclass while superclass = superclass.superclass
|
27
|
-
base_objects
|
28
28
|
end
|
29
29
|
|
30
30
|
# Only append the features of this module to the class that inherits
|
31
31
|
# directly from one of the BASE_OBJECTS
|
32
|
+
#
|
33
|
+
# @private
|
32
34
|
def append_features(base)
|
33
35
|
if base.respond_to? :superclass
|
34
|
-
base = base.superclass while !BASE_OBJECTS.include?
|
36
|
+
base = base.superclass while !BASE_OBJECTS.include? base.superclass
|
35
37
|
end
|
36
38
|
|
37
39
|
super
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "active_attr/error"
|
2
|
+
|
3
|
+
module ActiveAttr
|
4
|
+
# This exception is raised if attempting to define an attribute whose name
|
5
|
+
# conflicts with methods that are already defined
|
6
|
+
#
|
7
|
+
# @since 0.3.0
|
8
|
+
class DangerousAttributeError < ScriptError
|
9
|
+
include Error
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "active_support/core_ext/class/attribute"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
# Provides access to a configurable logger in model classes and instances
|
6
|
+
#
|
7
|
+
# @example Usage
|
8
|
+
# class Person
|
9
|
+
# include ActiveAttr::Logger
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# @since 0.3.0
|
13
|
+
module Logger
|
14
|
+
extend ActiveSupport::Concern
|
15
|
+
|
16
|
+
# The global default logger
|
17
|
+
#
|
18
|
+
# @return [nil, Object] logger Configured global default logger
|
19
|
+
#
|
20
|
+
# @since 0.3.0
|
21
|
+
def self.logger
|
22
|
+
@logger
|
23
|
+
end
|
24
|
+
|
25
|
+
# Determin if a global default logger is configured
|
26
|
+
#
|
27
|
+
# @since 0.3.0
|
28
|
+
def self.logger?
|
29
|
+
!!logger
|
30
|
+
end
|
31
|
+
|
32
|
+
# Configure the global default logger
|
33
|
+
#
|
34
|
+
# @param [Logger, #debug] logger The new global default logger
|
35
|
+
#
|
36
|
+
# @since 0.3.0
|
37
|
+
def self.logger=(new_logger)
|
38
|
+
@logger = new_logger
|
39
|
+
end
|
40
|
+
|
41
|
+
included do
|
42
|
+
class_attribute :logger
|
43
|
+
self.logger = ActiveAttr::Logger.logger
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|