active_attr 0.12.0 → 0.15.1

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.

Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yaml +160 -0
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +33 -0
  5. data/Gemfile +5 -8
  6. data/README.md +142 -117
  7. data/active_attr.gemspec +13 -6
  8. data/gemfiles/rails_3_0.gemfile +0 -4
  9. data/gemfiles/rails_3_1.gemfile +1 -8
  10. data/gemfiles/rails_3_2.gemfile +1 -12
  11. data/gemfiles/rails_4_0.gemfile +1 -1
  12. data/gemfiles/rails_4_1.gemfile +1 -1
  13. data/gemfiles/rails_4_2.gemfile +1 -1
  14. data/gemfiles/rails_5_0.gemfile +1 -1
  15. data/gemfiles/rails_5_1.gemfile +1 -1
  16. data/gemfiles/rails_5_2.gemfile +9 -0
  17. data/gemfiles/rails_6_0.gemfile +9 -0
  18. data/gemfiles/rails_head.gemfile +1 -1
  19. data/lib/active_attr.rb +1 -1
  20. data/lib/active_attr/attributes.rb +63 -9
  21. data/lib/active_attr/chainable_initialization.rb +2 -6
  22. data/lib/active_attr/logger.rb +1 -1
  23. data/lib/active_attr/mass_assignment.rb +2 -2
  24. data/lib/active_attr/matchers/have_attribute_matcher.rb +1 -1
  25. data/lib/active_attr/model.rb +1 -0
  26. data/lib/active_attr/query_attributes.rb +1 -1
  27. data/lib/active_attr/railtie.rb +5 -0
  28. data/lib/active_attr/typecasting/big_decimal_typecaster.rb +5 -2
  29. data/lib/active_attr/typecasting/boolean_typecaster.rb +8 -0
  30. data/lib/active_attr/typecasting/float_typecaster.rb +3 -1
  31. data/lib/active_attr/typecasting/integer_typecaster.rb +3 -1
  32. data/lib/active_attr/version.rb +1 -1
  33. data/spec/functional/active_attr/attributes_spec.rb +8 -40
  34. data/spec/functional/active_attr/mass_assignment_spec.rb +2 -2
  35. data/spec/functional/active_attr/matchers/have_attribute_matcher_spec.rb +48 -0
  36. data/spec/functional/active_attr/model_spec.rb +47 -0
  37. data/spec/functional/active_attr/typecasted_attributes_spec.rb +1 -1
  38. data/spec/unit/active_attr/attribute_defaults_spec.rb +8 -0
  39. data/spec/unit/active_attr/attributes_spec.rb +91 -0
  40. data/spec/unit/active_attr/query_attributes_spec.rb +4 -4
  41. data/spec/unit/active_attr/typecasting/big_decimal_typecaster_spec.rb +17 -9
  42. data/spec/unit/active_attr/typecasting/boolean_typecaster_spec.rb +7 -7
  43. data/spec/unit/active_attr/typecasting/float_typecaster_spec.rb +10 -2
  44. data/spec/unit/active_attr/typecasting/integer_typecaster_spec.rb +10 -2
  45. metadata +44 -29
  46. data/.ruby-version +0 -1
  47. data/.travis.yml +0 -83
@@ -4,11 +4,15 @@ require File.expand_path("../lib/active_attr/version", __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Chris Griego", "Ben Poweski"]
6
6
  gem.email = ["cgriego@gmail.com", "bpoweski@gmail.com"]
7
- gem.description = %q{Create plain old ruby models without reinventing the wheel.}
7
+ gem.description = %q{Create plain old Ruby models without reinventing the wheel.}
8
8
  gem.summary = %q{What ActiveModel left out}
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,13 +20,16 @@ Gem::Specification.new do |gem|
16
20
  gem.require_paths = ["lib"]
17
21
  gem.version = ActiveAttr::VERSION
18
22
 
19
- gem.add_runtime_dependency "activemodel", ">= 3.0.2", "< 6.0"
20
- gem.add_runtime_dependency "activesupport", ">= 3.0.2", "< 6.0"
23
+ gem.required_ruby_version = ">= 2.1.0"
24
+
25
+ gem.add_runtime_dependency "actionpack", ">= 3.0.2", "< 6.2"
26
+ gem.add_runtime_dependency "activemodel", ">= 3.0.2", "< 6.2"
27
+ gem.add_runtime_dependency "activesupport", ">= 3.0.2", "< 6.2"
21
28
 
22
- gem.add_development_dependency "bundler", "~> 1.0"
23
- gem.add_development_dependency "factory_girl", ">= 2.2", "< 5.0"
29
+ gem.add_development_dependency "bundler"
30
+ gem.add_development_dependency "factory_bot", "< 5.0"
24
31
  gem.add_development_dependency "minitest"
25
- gem.add_development_dependency "rake", ">= 0.9.0", "< 10.6"
32
+ gem.add_development_dependency "rake", ">= 0.9.0", "< 13.1"
26
33
  gem.add_development_dependency "rspec", "~> 3.0"
27
34
  gem.add_development_dependency "tzinfo"
28
35
  end
@@ -2,9 +2,5 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec :development_group => :test, :path => ".."
4
4
 
5
- if RUBY_VERSION < "1.9.2"
6
- gem "factory_girl", "< 3.0", :group => :test
7
- end
8
-
9
5
  gem "activemodel", "~> 3.0.2"
10
6
  gem "activesupport", "~> 3.0.2"
@@ -2,13 +2,6 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec :development_group => :test, :path => ".."
4
4
 
5
- if RUBY_VERSION < "1.9.2"
6
- gem "factory_girl", "< 3.0", :group => :test
7
- end
8
-
9
- if RUBY_VERSION < "1.9.3"
10
- gem "i18n", "< 0.7"
11
- end
12
-
13
5
  gem "activemodel", "~> 3.1.0"
14
6
  gem "activesupport", "~> 3.1.0"
7
+ gem "tzinfo", "< 2"
@@ -2,18 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec :development_group => :test, :path => ".."
4
4
 
5
- if RUBY_VERSION < "1.9.2"
6
- gem "factory_girl", "< 3.0", :group => :test
7
- end
8
-
9
- if RUBY_VERSION < "1.9.3"
10
- gem "i18n", "< 0.7"
11
- end
12
-
13
- if RUBY_VERSION < "1.9.3"
14
- gem "rack-cache", "< 1.3", :group => :test
15
- end
16
-
17
5
  gem "activemodel", "~> 3.2.0"
18
6
  gem "activesupport", "~> 3.2.0"
19
7
  gem "strong_parameters", ">= 0.2.0", :group => :test
8
+ gem "tzinfo", "< 2"
@@ -4,5 +4,5 @@ gemspec :development_group => :test, :path => ".."
4
4
 
5
5
  gem "activemodel", "~> 4.0.0"
6
6
  gem "activesupport", "~> 4.0.0"
7
- gem "actionpack", "~> 4.0.0", :group => :test
7
+ gem "actionpack", "~> 4.0.0"
8
8
  gem "protected_attributes", :group => :test
@@ -4,5 +4,5 @@ gemspec :development_group => :test, :path => ".."
4
4
 
5
5
  gem "activemodel", "~> 4.1.0"
6
6
  gem "activesupport", "~> 4.1.0"
7
- gem "actionpack", "~> 4.1.0", :group => :test
7
+ gem "actionpack", "~> 4.1.0"
8
8
  gem "protected_attributes", :group => :test
@@ -4,5 +4,5 @@ gemspec :development_group => :test, :path => ".."
4
4
 
5
5
  gem "activemodel", "~> 4.2.0"
6
6
  gem "activesupport", "~> 4.2.0"
7
- gem "actionpack", "~> 4.2.0", :group => :test
7
+ gem "actionpack", "~> 4.2.0"
8
8
  gem "protected_attributes", :group => :test
@@ -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", :group => :test
7
+ gem "actionpack", "~> 5.0.0"
8
8
  gem "activemodel-serializers-xml", :group => :test
9
9
  gem "protected_attributes_continued", :group => :test
@@ -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", :group => :test
7
+ gem "actionpack", "~> 5.1.0"
8
8
  gem "activemodel-serializers-xml", :group => :test
9
9
  gem "protected_attributes_continued", :group => :test
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec :development_group => :test, :path => ".."
4
+
5
+ gem "activemodel", "~> 5.2.0"
6
+ gem "activesupport", "~> 5.2.0"
7
+ gem "actionpack", "~> 5.2.0"
8
+ gem "activemodel-serializers-xml", :group => :test
9
+ gem "protected_attributes_continued", :group => :test
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec :development_group => :test, :path => ".."
4
+
5
+ gem "activemodel", "~> 6.0.0"
6
+ gem "activesupport", "~> 6.0.0"
7
+ gem "actionpack", "~> 6.0.0"
8
+ gem "activemodel-serializers-xml", :group => :test
9
+ gem "protected_attributes_continued", :group => :test
@@ -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", :group => :test
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"
@@ -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, DataMapper, and
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
@@ -3,8 +3,15 @@ require "active_attr/dangerous_attribute_error"
3
3
  require "active_attr/unknown_attribute_error"
4
4
  require "active_model"
5
5
  require "active_support/concern"
6
+ require "active_support/core_ext/class/attribute"
6
7
  require "active_support/hash_with_indifferent_access"
7
8
 
9
+ begin
10
+ require "active_support/parameter_filter"
11
+ rescue LoadError
12
+ require "action_dispatch/http/parameter_filter"
13
+ end
14
+
8
15
  module ActiveAttr
9
16
  # Attributes provides a set of class methods for defining an attributes
10
17
  # schema and instance methods for reading and writing attributes.
@@ -23,11 +30,42 @@ module ActiveAttr
23
30
  extend ActiveSupport::Concern
24
31
  include ActiveModel::AttributeMethods
25
32
 
26
- # Methods deprecated on the Object class which can be safely overridden
27
- # @since 0.3.0
28
- DEPRECATED_OBJECT_METHODS = %w(id type)
33
+ # @private
34
+ # @since 0.14.0
35
+ FILTERED = '[FILTERED]'.freeze
36
+
37
+ # @private
38
+ # @since 0.14.0
39
+ PARAMETER_FILTER = if defined?(ActiveSupport::ParameterFilter)
40
+ ActiveSupport::ParameterFilter
41
+ else
42
+ ActionDispatch::Http::ParameterFilter
43
+ end
44
+
45
+ # Specifies attributes which won't be exposed while calling #inspect
46
+ #
47
+ # @return [Array<#to_s, Regexp, Proc>] filter_attributes Configured
48
+ # global default filtered attributes
49
+ #
50
+ # @since 0.14.0
51
+ def self.filter_attributes
52
+ @filter_attributes ||= []
53
+ end
54
+
55
+ # Configure the global default filtered attributes
56
+ #
57
+ # @param [Array<#to_s, Regexp, Proc>] new_filter_attributes The new
58
+ # global default filtered attributes
59
+ #
60
+ # @since 0.14.0
61
+ def self.filter_attributes=(new_filter_attributes)
62
+ @filter_attributes = new_filter_attributes
63
+ end
29
64
 
30
65
  included do
66
+ class_attribute :filter_attributes, :instance_writer => false
67
+ self.filter_attributes = Attributes.filter_attributes
68
+
31
69
  attribute_method_suffix "" if attribute_method_matchers.none? { |matcher| matcher.prefix == "" && matcher.suffix == "" }
32
70
  attribute_method_suffix "="
33
71
  end
@@ -70,7 +108,20 @@ module ActiveAttr
70
108
  #
71
109
  # @since 0.2.0
72
110
  def inspect
73
- attribute_descriptions = attributes.sort.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
111
+ inspection_filter = PARAMETER_FILTER.new(filter_attributes)
112
+ original_attributes = attributes
113
+ filtered_attributes = inspection_filter.filter(original_attributes)
114
+
115
+ attribute_descriptions = filtered_attributes.sort.map { |key, value|
116
+ inspect_value = case
117
+ when original_attributes[key].nil? then nil.inspect
118
+ when value == FILTERED then FILTERED
119
+ else value.inspect
120
+ end
121
+
122
+ "#{key}: #{inspect_value}"
123
+ }.join(", ")
124
+
74
125
  separator = " " unless attribute_descriptions.empty?
75
126
  "#<#{self.class.name}#{separator}#{attribute_descriptions}>"
76
127
  end
@@ -150,7 +201,9 @@ module ActiveAttr
150
201
  #
151
202
  # @since 0.7.0
152
203
  def attributes_map
153
- Hash[ self.class.attribute_names.map { |name| [name, yield(name)] } ]
204
+ self.class.attribute_names.each_with_object({}) do |name, hash|
205
+ hash[name] = yield(name)
206
+ end
154
207
  end
155
208
 
156
209
  module ClassMethods
@@ -202,6 +255,7 @@ module ActiveAttr
202
255
  # Force active model to generate attribute methods
203
256
  remove_instance_variable("@attribute_methods_generated") if instance_variable_defined?("@attribute_methods_generated")
204
257
  define_attribute_methods([attribute_definition.name]) unless attribute_names.include? attribute_name
258
+ remove_instance_variable("@attribute_names") if instance_variable_defined?("@attribute_names")
205
259
  attributes[attribute_name] = attribute_definition
206
260
  end
207
261
  end
@@ -215,7 +269,7 @@ module ActiveAttr
215
269
  #
216
270
  # @since 0.5.0
217
271
  def attribute_names
218
- attributes.keys
272
+ @attribute_names ||= attributes.keys
219
273
  end
220
274
 
221
275
  # Returns a Hash of AttributeDefinition instances
@@ -251,7 +305,7 @@ module ActiveAttr
251
305
  # @since 0.6.0
252
306
  def dangerous_attribute?(name)
253
307
  attribute_methods(name).detect do |method_name|
254
- !DEPRECATED_OBJECT_METHODS.include?(method_name.to_s) && allocate.respond_to?(method_name, true)
308
+ allocate.respond_to?(method_name, true)
255
309
  end unless attribute_names.include? name.to_s
256
310
  end
257
311
 
@@ -273,8 +327,8 @@ module ActiveAttr
273
327
 
274
328
  # Assign a set of attribute definitions, used when subclassing models
275
329
  #
276
- # @param [Array<ActiveAttr::AttributeDefinition>] The Array of
277
- # AttributeDefinition instances
330
+ # @param [Array<ActiveAttr::AttributeDefinition>] attributes The
331
+ # Array of AttributeDefinition instances
278
332
  #
279
333
  # @since 0.2.2
280
334
  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] on Ruby 1.8
22
- # [Object, BasicObject] on Ruby 1.9
21
+ # [Object, BasicObject]
23
22
  #
24
23
  # @private
25
- BASE_OBJECTS = [].tap do |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
@@ -24,7 +24,7 @@ module ActiveAttr
24
24
 
25
25
  # Configure the global default logger
26
26
  #
27
- # @param [Logger, #debug] logger The new global default logger
27
+ # @param [Logger, #debug] new_logger The new global default logger
28
28
  #
29
29
  # @since 0.3.0
30
30
  def self.logger=(new_logger)
@@ -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] attributes Attributes used to
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 { actual_attribute_definition[:default] == default_value }
79
+ @attribute_expectations << lambda { @model_class.allocate.send(:_attribute_default, @attribute_name) == default_value }
80
80
  self
81
81
  end
82
82
 
@@ -23,6 +23,7 @@ module ActiveAttr
23
23
  module Model
24
24
  extend ActiveSupport::Concern
25
25
  include BasicModel
26
+ include ActiveModel::Validations::Callbacks
26
27
  include BlockInitialization
27
28
  include Logger
28
29
  include MassAssignment
@@ -52,7 +52,7 @@ module ActiveAttr
52
52
  private
53
53
 
54
54
  def attribute?(name)
55
- Typecasting::BooleanTypecaster.new.call(read_attribute(name))
55
+ !!Typecasting::BooleanTypecaster.new.call(read_attribute(name))
56
56
  end
57
57
  end
58
58
  end
@@ -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
@@ -1,6 +1,7 @@
1
1
  require "bigdecimal"
2
2
  require "bigdecimal/util"
3
3
  require "active_support/core_ext/big_decimal/conversions"
4
+ require "active_support/core_ext/object/blank"
4
5
 
5
6
  module ActiveAttr
6
7
  module Typecasting
@@ -29,13 +30,15 @@ module ActiveAttr
29
30
  value
30
31
  elsif value.is_a? Rational
31
32
  value.to_f.to_d
33
+ elsif value.blank?
34
+ nil
32
35
  elsif value.respond_to? :to_d
33
36
  value.to_d
34
37
  else
35
- BigDecimal.new value.to_s
38
+ BigDecimal(value.to_s)
36
39
  end
37
40
  rescue ArgumentError
38
- BigDecimal.new "0"
41
+ BigDecimal("0")
39
42
  end
40
43
  end
41
44
  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
@@ -1,3 +1,5 @@
1
+ require "active_support/core_ext/object/blank"
2
+
1
3
  module ActiveAttr
2
4
  module Typecasting
3
5
  # Typecasts an Object to a Float
@@ -20,7 +22,7 @@ module ActiveAttr
20
22
  #
21
23
  # @since 0.5.0
22
24
  def call(value)
23
- value.to_f if value.respond_to? :to_f
25
+ value.to_f if value.present? && value.respond_to?(:to_f)
24
26
  end
25
27
  end
26
28
  end
@@ -1,3 +1,5 @@
1
+ require "active_support/core_ext/object/blank"
2
+
1
3
  module ActiveAttr
2
4
  module Typecasting
3
5
  # Typecasts an Object to an Integer
@@ -21,7 +23,7 @@ module ActiveAttr
21
23
  #
22
24
  # @since 0.5.0
23
25
  def call(value)
24
- value.to_i if value.respond_to? :to_i
26
+ value.to_i if value.present? && value.respond_to?(:to_i)
25
27
  rescue FloatDomainError
26
28
  end
27
29
  end