active_attr 0.2.2 → 0.3.0
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.
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
|