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 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
- p = Person.new
28
- p.first_name = "Chris"
29
- p.last_name = "Griego"
30
- p.attributes #=> {"first_name"=>"Chris", "last_name"=>"Griego"}
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
- p = Person.new
43
- p.valid? #=> true
44
- p.errors.full_messages #=> []
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
- p = Person.new(:first_name => "Chris")
60
- p.attributes = { :last_name => "Griego" }
61
- p.first_name #=> "Chris"
62
- p.last_name #=> "Griego"
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", "~> 1.0"
25
- s.add_development_dependency "rake", "~> 0.9.0"
26
- s.add_development_dependency "rspec", "~> 2.6"
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 the raw attributes Hash
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.class.attributes.sort.map do |attribute|
74
- "#{attribute.name.to_s}: #{read_attribute(attribute.name).inspect}"
75
- end.join(", ")
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. If the value does not exist
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
- @attributes[name.to_s]
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
- @attributes[name.to_s] = value
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
- alias_method :attribute=, :write_attribute
114
- private :attribute=
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
- attributes << attribute_definition unless attributes.include? attribute_definition
131
- define_attribute_method attribute_definition.name
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
- BASE_OBJECTS = begin
24
- base_objects = []
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?(base.superclass)
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