active_attr 0.13.1 → 0.14.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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +0 -17
- data/CHANGELOG.md +14 -0
- data/Gemfile +4 -5
- data/README.md +140 -112
- data/active_attr.gemspec +8 -1
- data/gemfiles/rails_3_0.gemfile +0 -4
- data/gemfiles/rails_3_1.gemfile +0 -4
- data/gemfiles/rails_3_2.gemfile +0 -4
- data/gemfiles/rails_4_0.gemfile +1 -1
- data/gemfiles/rails_4_1.gemfile +1 -1
- data/gemfiles/rails_4_2.gemfile +1 -1
- data/gemfiles/rails_5_0.gemfile +1 -1
- data/gemfiles/rails_5_1.gemfile +1 -1
- data/gemfiles/rails_5_2.gemfile +1 -1
- data/gemfiles/rails_head.gemfile +1 -1
- data/lib/active_attr.rb +1 -1
- data/lib/active_attr/attributes.rb +62 -9
- data/lib/active_attr/chainable_initialization.rb +2 -6
- data/lib/active_attr/logger.rb +1 -1
- data/lib/active_attr/mass_assignment.rb +2 -2
- data/lib/active_attr/matchers/have_attribute_matcher.rb +1 -1
- data/lib/active_attr/model.rb +1 -0
- data/lib/active_attr/query_attributes.rb +1 -1
- data/lib/active_attr/railtie.rb +5 -0
- data/lib/active_attr/typecasting/boolean_typecaster.rb +8 -0
- data/lib/active_attr/version.rb +1 -1
- data/spec/functional/active_attr/attributes_spec.rb +8 -8
- data/spec/functional/active_attr/matchers/have_attribute_matcher_spec.rb +48 -0
- data/spec/functional/active_attr/model_spec.rb +47 -0
- data/spec/unit/active_attr/attribute_defaults_spec.rb +8 -0
- data/spec/unit/active_attr/attributes_spec.rb +91 -0
- data/spec/unit/active_attr/typecasting/big_decimal_typecaster_spec.rb +4 -0
- data/spec/unit/active_attr/typecasting/boolean_typecaster_spec.rb +3 -3
- metadata +27 -13
- data/.ruby-version +0 -1
data/active_attr.gemspec
CHANGED
@@ -9,6 +9,10 @@ Gem::Specification.new do |gem|
|
|
9
9
|
gem.homepage = "https://github.com/cgriego/active_attr"
|
10
10
|
gem.license = "MIT"
|
11
11
|
|
12
|
+
gem.metadata = {
|
13
|
+
"changelog_uri" => "https://github.com/cgriego/active_attr/blob/master/CHANGELOG.md",
|
14
|
+
}
|
15
|
+
|
12
16
|
gem.files = `git ls-files`.split($\)
|
13
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
@@ -16,11 +20,14 @@ Gem::Specification.new do |gem|
|
|
16
20
|
gem.require_paths = ["lib"]
|
17
21
|
gem.version = ActiveAttr::VERSION
|
18
22
|
|
23
|
+
gem.required_ruby_version = ">= 1.9.2"
|
24
|
+
|
25
|
+
gem.add_runtime_dependency "actionpack", ">= 3.0.2", "< 6.1"
|
19
26
|
gem.add_runtime_dependency "activemodel", ">= 3.0.2", "< 6.1"
|
20
27
|
gem.add_runtime_dependency "activesupport", ">= 3.0.2", "< 6.1"
|
21
28
|
|
22
29
|
gem.add_development_dependency "bundler", "~> 1.0"
|
23
|
-
gem.add_development_dependency "
|
30
|
+
gem.add_development_dependency "factory_bot", "< 5.0"
|
24
31
|
gem.add_development_dependency "minitest"
|
25
32
|
gem.add_development_dependency "rake", ">= 0.9.0", "< 10.6"
|
26
33
|
gem.add_development_dependency "rspec", "~> 3.0"
|
data/gemfiles/rails_3_0.gemfile
CHANGED
data/gemfiles/rails_3_1.gemfile
CHANGED
data/gemfiles/rails_3_2.gemfile
CHANGED
data/gemfiles/rails_4_0.gemfile
CHANGED
data/gemfiles/rails_4_1.gemfile
CHANGED
data/gemfiles/rails_4_2.gemfile
CHANGED
data/gemfiles/rails_5_0.gemfile
CHANGED
@@ -4,6 +4,6 @@ gemspec :development_group => :test, :path => ".."
|
|
4
4
|
|
5
5
|
gem "activemodel", "~> 5.0.0"
|
6
6
|
gem "activesupport", "~> 5.0.0"
|
7
|
-
gem "actionpack", "~> 5.0.0"
|
7
|
+
gem "actionpack", "~> 5.0.0"
|
8
8
|
gem "activemodel-serializers-xml", :group => :test
|
9
9
|
gem "protected_attributes_continued", :group => :test
|
data/gemfiles/rails_5_1.gemfile
CHANGED
@@ -4,6 +4,6 @@ gemspec :development_group => :test, :path => ".."
|
|
4
4
|
|
5
5
|
gem "activemodel", "~> 5.1.0"
|
6
6
|
gem "activesupport", "~> 5.1.0"
|
7
|
-
gem "actionpack", "~> 5.1.0"
|
7
|
+
gem "actionpack", "~> 5.1.0"
|
8
8
|
gem "activemodel-serializers-xml", :group => :test
|
9
9
|
gem "protected_attributes_continued", :group => :test
|
data/gemfiles/rails_5_2.gemfile
CHANGED
@@ -4,6 +4,6 @@ gemspec :development_group => :test, :path => ".."
|
|
4
4
|
|
5
5
|
gem "activemodel", "~> 5.2.0"
|
6
6
|
gem "activesupport", "~> 5.2.0"
|
7
|
-
gem "actionpack", "~> 5.2.0"
|
7
|
+
gem "actionpack", "~> 5.2.0"
|
8
8
|
gem "activemodel-serializers-xml", :group => :test
|
9
9
|
gem "protected_attributes_continued", :group => :test
|
data/gemfiles/rails_head.gemfile
CHANGED
@@ -5,7 +5,7 @@ gemspec :development_group => :test, :path => ".."
|
|
5
5
|
git "git://github.com/rails/rails.git" do
|
6
6
|
gem "activemodel"
|
7
7
|
gem "activesupport"
|
8
|
-
gem "actionpack"
|
8
|
+
gem "actionpack"
|
9
9
|
end
|
10
10
|
|
11
11
|
gem "activemodel-serializers-xml", :group => :test, :git => "git://github.com/rails/activemodel-serializers-xml.git"
|
data/lib/active_attr.rb
CHANGED
@@ -4,7 +4,7 @@ require "active_support/dependencies/autoload"
|
|
4
4
|
# ActiveAttr is a set of modules to enhance Plain Old Ruby Objects (POROs)
|
5
5
|
#
|
6
6
|
# These modules give your objects the type of features that are normally found
|
7
|
-
# in popular Object Relation Mappers (ORMs) like ActiveRecord,
|
7
|
+
# in popular Object Relation Mappers (ORMs) like ActiveRecord, Sequel, and
|
8
8
|
# Mongoid. The goal is to lower the bar for creating easy-to-use Ruby models.
|
9
9
|
module ActiveAttr
|
10
10
|
extend ActiveSupport::Autoload
|
@@ -5,6 +5,12 @@ require "active_model"
|
|
5
5
|
require "active_support/concern"
|
6
6
|
require "active_support/hash_with_indifferent_access"
|
7
7
|
|
8
|
+
begin
|
9
|
+
require "active_support/parameter_filter"
|
10
|
+
rescue LoadError
|
11
|
+
require "action_dispatch/http/parameter_filter"
|
12
|
+
end
|
13
|
+
|
8
14
|
module ActiveAttr
|
9
15
|
# Attributes provides a set of class methods for defining an attributes
|
10
16
|
# schema and instance methods for reading and writing attributes.
|
@@ -23,11 +29,42 @@ module ActiveAttr
|
|
23
29
|
extend ActiveSupport::Concern
|
24
30
|
include ActiveModel::AttributeMethods
|
25
31
|
|
26
|
-
#
|
27
|
-
# @since 0.
|
28
|
-
|
32
|
+
# @private
|
33
|
+
# @since 0.14.0
|
34
|
+
FILTERED = '[FILTERED]'.freeze
|
35
|
+
|
36
|
+
# @private
|
37
|
+
# @since 0.14.0
|
38
|
+
PARAMETER_FILTER = if defined?(ActiveSupport::ParameterFilter)
|
39
|
+
ActiveSupport::ParameterFilter
|
40
|
+
else
|
41
|
+
ActionDispatch::Http::ParameterFilter
|
42
|
+
end
|
43
|
+
|
44
|
+
# Specifies attributes which won't be exposed while calling #inspect
|
45
|
+
#
|
46
|
+
# @return [Array<#to_s, Regexp, Proc>] filter_attributes Configured
|
47
|
+
# global default filtered attributes
|
48
|
+
#
|
49
|
+
# @since 0.14.0
|
50
|
+
def self.filter_attributes
|
51
|
+
@filter_attributes ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
# Configure the global default filtered attributes
|
55
|
+
#
|
56
|
+
# @param [Array<#to_s, Regexp, Proc>] new_filter_attributes The new
|
57
|
+
# global default filtered attributes
|
58
|
+
#
|
59
|
+
# @since 0.14.0
|
60
|
+
def self.filter_attributes=(new_filter_attributes)
|
61
|
+
@filter_attributes = new_filter_attributes
|
62
|
+
end
|
29
63
|
|
30
64
|
included do
|
65
|
+
class_attribute :filter_attributes, :instance_writer => false
|
66
|
+
self.filter_attributes = Attributes.filter_attributes
|
67
|
+
|
31
68
|
attribute_method_suffix "" if attribute_method_matchers.none? { |matcher| matcher.prefix == "" && matcher.suffix == "" }
|
32
69
|
attribute_method_suffix "="
|
33
70
|
end
|
@@ -70,7 +107,20 @@ module ActiveAttr
|
|
70
107
|
#
|
71
108
|
# @since 0.2.0
|
72
109
|
def inspect
|
73
|
-
|
110
|
+
inspection_filter = PARAMETER_FILTER.new(filter_attributes)
|
111
|
+
original_attributes = attributes
|
112
|
+
filtered_attributes = inspection_filter.filter(original_attributes)
|
113
|
+
|
114
|
+
attribute_descriptions = filtered_attributes.sort.map { |key, value|
|
115
|
+
inspect_value = case
|
116
|
+
when original_attributes[key].nil? then nil.inspect
|
117
|
+
when value == FILTERED then FILTERED
|
118
|
+
else value.inspect
|
119
|
+
end
|
120
|
+
|
121
|
+
"#{key}: #{inspect_value}"
|
122
|
+
}.join(", ")
|
123
|
+
|
74
124
|
separator = " " unless attribute_descriptions.empty?
|
75
125
|
"#<#{self.class.name}#{separator}#{attribute_descriptions}>"
|
76
126
|
end
|
@@ -150,7 +200,9 @@ module ActiveAttr
|
|
150
200
|
#
|
151
201
|
# @since 0.7.0
|
152
202
|
def attributes_map
|
153
|
-
|
203
|
+
self.class.attribute_names.each_with_object({}) do |name, hash|
|
204
|
+
hash[name] = yield(name)
|
205
|
+
end
|
154
206
|
end
|
155
207
|
|
156
208
|
module ClassMethods
|
@@ -202,6 +254,7 @@ module ActiveAttr
|
|
202
254
|
# Force active model to generate attribute methods
|
203
255
|
remove_instance_variable("@attribute_methods_generated") if instance_variable_defined?("@attribute_methods_generated")
|
204
256
|
define_attribute_methods([attribute_definition.name]) unless attribute_names.include? attribute_name
|
257
|
+
remove_instance_variable("@attribute_names") if instance_variable_defined?("@attribute_names")
|
205
258
|
attributes[attribute_name] = attribute_definition
|
206
259
|
end
|
207
260
|
end
|
@@ -215,7 +268,7 @@ module ActiveAttr
|
|
215
268
|
#
|
216
269
|
# @since 0.5.0
|
217
270
|
def attribute_names
|
218
|
-
attributes.keys
|
271
|
+
@attribute_names ||= attributes.keys
|
219
272
|
end
|
220
273
|
|
221
274
|
# Returns a Hash of AttributeDefinition instances
|
@@ -251,7 +304,7 @@ module ActiveAttr
|
|
251
304
|
# @since 0.6.0
|
252
305
|
def dangerous_attribute?(name)
|
253
306
|
attribute_methods(name).detect do |method_name|
|
254
|
-
|
307
|
+
allocate.respond_to?(method_name, true)
|
255
308
|
end unless attribute_names.include? name.to_s
|
256
309
|
end
|
257
310
|
|
@@ -273,8 +326,8 @@ module ActiveAttr
|
|
273
326
|
|
274
327
|
# Assign a set of attribute definitions, used when subclassing models
|
275
328
|
#
|
276
|
-
# @param [Array<ActiveAttr::AttributeDefinition>] The
|
277
|
-
# AttributeDefinition instances
|
329
|
+
# @param [Array<ActiveAttr::AttributeDefinition>] attributes The
|
330
|
+
# Array of AttributeDefinition instances
|
278
331
|
#
|
279
332
|
# @since 0.2.2
|
280
333
|
def attributes=(attributes)
|
@@ -18,14 +18,10 @@ module ActiveAttr
|
|
18
18
|
module ChainableInitialization
|
19
19
|
class << self
|
20
20
|
# A collection of Ruby base objects
|
21
|
-
# [Object]
|
22
|
-
# [Object, BasicObject] on Ruby 1.9
|
21
|
+
# [Object, BasicObject]
|
23
22
|
#
|
24
23
|
# @private
|
25
|
-
BASE_OBJECTS = []
|
26
|
-
superclass = Class.new
|
27
|
-
base_objects << superclass while superclass = superclass.superclass
|
28
|
-
end
|
24
|
+
BASE_OBJECTS = [Object, BasicObject]
|
29
25
|
|
30
26
|
# Only append the features of this module to the class that inherits
|
31
27
|
# directly from one of the BASE_OBJECTS
|
data/lib/active_attr/logger.rb
CHANGED
@@ -24,8 +24,8 @@ module ActiveAttr
|
|
24
24
|
# person.first_name #=> "Chris"
|
25
25
|
# person.last_name #=> "Griego"
|
26
26
|
#
|
27
|
-
# @param [Hash{#to_s => Object}, #each]
|
28
|
-
# populate the model
|
27
|
+
# @param [Hash{#to_s => Object}, #each] new_attributes Attributes
|
28
|
+
# used to populate the model
|
29
29
|
# @param [Hash, #[]] options Options that affect mass assignment
|
30
30
|
#
|
31
31
|
# @option options [Symbol] :as (:default) Mass assignment role
|
@@ -76,7 +76,7 @@ module ActiveAttr
|
|
76
76
|
@attribute_options[:default] = default_value
|
77
77
|
@description << " with a default value of #{default_value.inspect}"
|
78
78
|
@expected_ancestors << "ActiveAttr::AttributeDefaults"
|
79
|
-
@attribute_expectations << lambda {
|
79
|
+
@attribute_expectations << lambda { @model_class.allocate.send(:_attribute_default, @attribute_name) == default_value }
|
80
80
|
self
|
81
81
|
end
|
82
82
|
|
data/lib/active_attr/model.rb
CHANGED
data/lib/active_attr/railtie.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "active_attr/attributes"
|
1
2
|
require "active_attr/logger"
|
2
3
|
|
3
4
|
module ActiveAttr
|
@@ -6,5 +7,9 @@ module ActiveAttr
|
|
6
7
|
initializer "active_attr.logger" do
|
7
8
|
Logger.logger ||= ::Rails.logger
|
8
9
|
end
|
10
|
+
|
11
|
+
initializer "active_attr.attributes" do
|
12
|
+
Attributes.filter_attributes += Rails.application.config.filter_parameters
|
13
|
+
end
|
9
14
|
end
|
10
15
|
end
|
@@ -17,6 +17,13 @@ module ActiveAttr
|
|
17
17
|
# @since 0.5.0
|
18
18
|
FALSE_VALUES = ["n", "N", "no", "No", "NO", "false", "False", "FALSE", "off", "Off", "OFF", "f", "F"]
|
19
19
|
|
20
|
+
# Values which force a nil result for typecasting
|
21
|
+
#
|
22
|
+
# These values are based on the behavior of ActiveRecord
|
23
|
+
#
|
24
|
+
# @since 0.14.0
|
25
|
+
NIL_VALUES = ["", nil]
|
26
|
+
|
20
27
|
# Typecasts an object to true or false
|
21
28
|
#
|
22
29
|
# Similar to ActiveRecord, when the attribute is a zero value or
|
@@ -34,6 +41,7 @@ module ActiveAttr
|
|
34
41
|
def call(value)
|
35
42
|
case value
|
36
43
|
when *FALSE_VALUES then false
|
44
|
+
when *NIL_VALUES then nil
|
37
45
|
when Numeric, /\A[-+]?(0+\.?0*|0*\.?0+)\z/ then !value.to_f.zero?
|
38
46
|
else value.present?
|
39
47
|
end
|
data/lib/active_attr/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "active_attr/attributes"
|
3
3
|
require "active_model"
|
4
|
-
require "
|
4
|
+
require "factory_bot"
|
5
5
|
|
6
6
|
module ActiveAttr
|
7
7
|
describe Attributes do
|
@@ -149,22 +149,22 @@ module ActiveAttr
|
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
152
|
-
context "building with
|
153
|
-
subject(:model) {
|
152
|
+
context "building with FactoryBot" do
|
153
|
+
subject(:model) { FactoryBot.build(:person) }
|
154
154
|
|
155
155
|
before do
|
156
156
|
Object.const_set("Person", model_class)
|
157
157
|
|
158
|
-
|
158
|
+
FactoryBot.define do
|
159
159
|
factory :person, :class => :person do
|
160
|
-
first_name "Chris"
|
161
|
-
last_name "Griego"
|
160
|
+
first_name { "Chris" }
|
161
|
+
last_name { "Griego" }
|
162
162
|
end
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
166
166
|
after do
|
167
|
-
|
167
|
+
FactoryBot.factories.clear
|
168
168
|
Object.send :remove_const, "Person"
|
169
169
|
end
|
170
170
|
|
@@ -284,7 +284,7 @@ module ActiveAttr
|
|
284
284
|
end
|
285
285
|
|
286
286
|
def respond_to?(method_name, include_private=false)
|
287
|
-
super || method_name.to_s == "my_less_proper_missing_method"
|
287
|
+
super || method_name.to_s == "my_less_proper_missing_method"
|
288
288
|
end
|
289
289
|
end
|
290
290
|
end
|
@@ -14,6 +14,14 @@ module ActiveAttr
|
|
14
14
|
include AttributeDefaults
|
15
15
|
include TypecastedAttributes
|
16
16
|
|
17
|
+
def john
|
18
|
+
"John"
|
19
|
+
end
|
20
|
+
|
21
|
+
def doe
|
22
|
+
"Doe"
|
23
|
+
end
|
24
|
+
|
17
25
|
def self.name
|
18
26
|
"Person"
|
19
27
|
end
|
@@ -167,6 +175,25 @@ module ActiveAttr
|
|
167
175
|
end
|
168
176
|
end
|
169
177
|
|
178
|
+
context "a class with the attribute and a different default (lazy evaluation)" do
|
179
|
+
before { model_class.attribute :first_name, :default => lambda { doe } }
|
180
|
+
|
181
|
+
describe "#matches?" do
|
182
|
+
it { matcher.matches?(model_class).should == false }
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "#failure_message" do
|
186
|
+
before { matcher.matches?(model_class) }
|
187
|
+
|
188
|
+
it do
|
189
|
+
matcher.failure_message.should match Regexp.new <<-MESSAGE.strip_heredoc.chomp, Regexp::MULTILINE
|
190
|
+
expected: attribute :first_name, :default => "John"
|
191
|
+
got: attribute :first_name, :default => #<Proc.+?>
|
192
|
+
MESSAGE
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
170
197
|
context "a class with the attribute and the right default" do
|
171
198
|
before { model_class.attribute :first_name, :default => "John" }
|
172
199
|
|
@@ -187,6 +214,27 @@ module ActiveAttr
|
|
187
214
|
end
|
188
215
|
end
|
189
216
|
end
|
217
|
+
|
218
|
+
context "a class with the attribute and the right default (lazy evaluation)" do
|
219
|
+
before { model_class.attribute :first_name, :default => lambda { john } }
|
220
|
+
|
221
|
+
describe "#matches?" do
|
222
|
+
it { matcher.matches?(model_class).should == true }
|
223
|
+
end
|
224
|
+
|
225
|
+
[:negative_failure_message, :failure_message_when_negated].each do |method|
|
226
|
+
describe "##{method}" do
|
227
|
+
before { matcher.matches?(model_class) }
|
228
|
+
|
229
|
+
it do
|
230
|
+
matcher.send(method).should match Regexp.new <<-MESSAGE.strip_heredoc.chomp, Regexp::MULTILINE
|
231
|
+
expected not: attribute :first_name, :default => "John"
|
232
|
+
got: attribute :first_name, :default => #<Proc.+?>
|
233
|
+
MESSAGE
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
190
238
|
end
|
191
239
|
|
192
240
|
context "a matcher with a default value of false" do
|