active_attr 0.13.0 → 0.15.2

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.

Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yaml +180 -0
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +34 -0
  5. data/Gemfile +6 -8
  6. data/README.md +145 -117
  7. data/active_attr.gemspec +14 -7
  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 +2 -1
  15. data/gemfiles/rails_5_1.gemfile +2 -1
  16. data/gemfiles/rails_5_2.gemfile +10 -0
  17. data/gemfiles/rails_6_0.gemfile +10 -0
  18. data/gemfiles/rails_head.gemfile +3 -2
  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 +3 -0
  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/unit/active_attr/attribute_defaults_spec.rb +8 -0
  38. data/spec/unit/active_attr/attributes_spec.rb +91 -0
  39. data/spec/unit/active_attr/typecasting/big_decimal_typecaster_spec.rb +11 -3
  40. data/spec/unit/active_attr/typecasting/boolean_typecaster_spec.rb +3 -3
  41. data/spec/unit/active_attr/typecasting/float_typecaster_spec.rb +10 -2
  42. data/spec/unit/active_attr/typecasting/integer_typecaster_spec.rb +10 -2
  43. metadata +48 -33
  44. data/.ruby-version +0 -1
  45. data/.travis.yml +0 -94
data/active_attr.gemspec CHANGED
@@ -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", "< 7.0"
26
+ gem.add_runtime_dependency "activemodel", ">= 3.0.2", "< 7.0"
27
+ gem.add_runtime_dependency "activesupport", ">= 3.0.2", "< 7.0"
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
- gem.add_development_dependency "tzinfo", "< 2.0"
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,7 @@ 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
+ gem "rexml", :group => :test
9
10
  gem "protected_attributes_continued", :group => :test
@@ -4,6 +4,7 @@ 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
+ gem "rexml", :group => :test
9
10
  gem "protected_attributes_continued", :group => :test
@@ -0,0 +1,10 @@
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 "rexml", :group => :test
10
+ gem "protected_attributes_continued", :group => :test
@@ -0,0 +1,10 @@
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 "rexml", :group => :test
10
+ gem "protected_attributes_continued", :group => :test
@@ -2,12 +2,13 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec :development_group => :test, :path => ".."
4
4
 
5
- git "git://github.com/rails/rails.git" do
5
+ git "git://github.com/rails/rails.git", :branch => "main" 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"
12
+ gem "rexml", :group => :test
12
13
  gem "protected_attributes_continued", :group => :test, :git => "git://github.com/westonganger/protected_attributes_continued"
13
14
  gem "rack", :group => :test, :git => "git://github.com/rack/rack.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, 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,6 +30,8 @@ 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
@@ -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