diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 5a734e4..ee141db 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -21,13 +21,13 @@ jobs: strategy: fail-fast: false matrix: - # TODO: unlock jruby when https://github.com/jruby/jruby/issues/8116 is fixed - ruby: ["jruby-9.4.5.0", "3.0", 3.1, 3.2] + ruby: ["jruby-9.4.8.0", "3.0", 3.1, 3.2] gemfile: [ "gemfiles/jruby.gemfile", - "gemfiles/rails_6_2.gemfile", - "gemfiles/rails_7_0.gemfile" + "gemfiles/rails_7_0.gemfile", + "gemfiles/rails_7_1.gemfile", + "gemfiles/rails_7_2.gemfile" ] enable_parent_assignment: ["true", "false"] @@ -36,13 +36,18 @@ jobs: - false exclude: - - ruby: "jruby-9.4.5.0" - gemfile: gemfiles/rails_6_2.gemfile - - ruby: "jruby-9.4.5.0" + - ruby: "jruby-9.4.8.0" gemfile: gemfiles/rails_7_0.gemfile - - ruby: "jruby-9.4.5.0" + - ruby: "jruby-9.4.8.0" + gemfile: gemfiles/rails_7_1.gemfile + - ruby: "jruby-9.4.8.0" + gemfile: gemfiles/rails_7_2.gemfile + - ruby: "jruby-9.4.8.0" gemfile: gemfiles/railsmaster.gemfile + - ruby: "3.0" + gemfile: gemfiles/rails_7_2.gemfile + - ruby: "3.0" gemfile: gemfiles/jruby.gemfile - ruby: 3.1 diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index d3c2783..287906b 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7 + ruby-version: 3.0 - name: Lint Ruby code with RuboCop run: | bundle install --gemfile gemfiles/rails_7_0.gemfile --jobs 4 --retry 3 diff --git a/.rubocop.yml b/.rubocop.yml index a406044..4f3d01d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,5 @@ AllCops: - TargetRubyVersion: 2.5 + TargetRubyVersion: 3.0 Include: - 'lib/**/*.rb' - 'spec/**/*.rb' @@ -22,5 +22,11 @@ Metrics/BlockLength: Exclude: - 'spec/**/*.rb' +Metrics/CyclomaticComplexity: + Max: 8 + Style/NumericLiterals: Enabled: false + +Lint/ConstantDefinitionInBlock: + Enabled: false diff --git a/CHANGELOG.md b/CHANGELOG.md index e1fb0e8..f778822 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## master +## 4.0.0 (TBD) + +- [PR #182](https://github.com/DmitryTsepelev/store_model/pull/182) Update Ruby and Rails versions, raise an error when database is not available (cause private API we use is deprecated https://github.com/rails/rails/commit/e0a55b038f7f2f50d1467876558be183be6cedaa) ([@DmitryTsepelev]) +- [PR #181](https://github.com/DmitryTsepelev/store_model/pull/181) Treat all Hash-like input as a Hash ([@ql]) +- [PR #180](https://github.com/DmitryTsepelev/store_model/pull/180) Add update_only option to StoreModel#accepts_nested_attributes_for ([@ql]) + ## 3.0.2 (2024-06-05) - [PR #176](https://github.com/DmitryTsepelev/store_model/pull/176) fix: serialize empty has-many array ([@bf4]) @@ -232,3 +238,4 @@ [@osanay]: https://github.com/osanay [@manuelvanrijn]: https://github.com/manuelvanrijn [@bf4]: https://github.com/bf4 +[@ql]: https://github.com/ql diff --git a/Gemfile b/Gemfile index 2726e12..fd46d8a 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,6 @@ local_gemfile = File.join(__dir__, "Gemfile.local") if File.exist?(local_gemfile) eval(File.read(local_gemfile)) # rubocop:disable Security/Eval else - gem "sqlite3", "~> 1.4.0" - gem "activerecord", "~> 6.0" + gem "sqlite3", "~> 1.7.0" + gem "activerecord", "~> 7.0" end diff --git a/gemfiles/jruby.gemfile b/gemfiles/jruby.gemfile index 74bc386..04484c6 100644 --- a/gemfiles/jruby.gemfile +++ b/gemfiles/jruby.gemfile @@ -2,6 +2,6 @@ source "https://rubygems.org" gem "jdbc-sqlite3", platform: :jruby gem "activerecord-jdbcmysql-adapter", platform: :jruby -gem "activerecord", "~> 6.0.0" +gem "activerecord", "~> 7.0.0" gemspec path: "../" diff --git a/gemfiles/rails_6_2.gemfile b/gemfiles/rails_7_1.gemfile similarity index 71% rename from gemfiles/rails_6_2.gemfile rename to gemfiles/rails_7_1.gemfile index 293566f..9a9f647 100644 --- a/gemfiles/rails_6_2.gemfile +++ b/gemfiles/rails_7_1.gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" gem "sqlite3", "~> 1.4.0" -gem "activerecord", "~> 6.0.2" +gem "activerecord", "~> 7.1.0" gemspec path: "../" diff --git a/gemfiles/rails_7_2.gemfile b/gemfiles/rails_7_2.gemfile new file mode 100644 index 0000000..fc2e8de --- /dev/null +++ b/gemfiles/rails_7_2.gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem "sqlite3", "~> 1.4.0" +gem "activerecord", "~> 7.2.0" + +gemspec path: "../" diff --git a/lib/store_model/model.rb b/lib/store_model/model.rb index 898eb47..cd082a2 100644 --- a/lib/store_model/model.rb +++ b/lib/store_model/model.rb @@ -228,7 +228,7 @@ def serialize_enums!(json) .attribute_types .select { |_, type| type.is_a?(StoreModel::Types::EnumType) } - enum_mappings.each do |name, _| + enum_mappings.each_key do |name| next unless json.key?(name) json[name] = public_send(name).as_json unless json[name].nil? diff --git a/lib/store_model/nested_attributes.rb b/lib/store_model/nested_attributes.rb index b38b956..d40397d 100644 --- a/lib/store_model/nested_attributes.rb +++ b/lib/store_model/nested_attributes.rb @@ -60,11 +60,7 @@ def accepts_nested_attributes_for(*attributes) # If schema loaded the attribute_types already populated and we can safely use it # See ActiveRecord::ModelSchema#load_schema! def nested_attribute_type(attribute) - if self < ActiveRecord::Base && !schema_loaded? - attributes_to_define_after_schema_loads[attribute.to_s]&.first - else - attribute_types[attribute.to_s] - end + attribute_types[attribute.to_s] end def define_store_model_attr_accessors(attribute, options) # rubocop:disable Metrics/MethodLength @@ -104,7 +100,7 @@ def define_association_setter_for_single(association, options) return unless options&.dig(:allow_destroy) define_method "#{association}=" do |attributes| - if ActiveRecord::Type::Boolean.new.cast(attributes.stringify_keys.dig("_destroy")) + if ActiveRecord::Type::Boolean.new.cast(attributes.stringify_keys["_destroy"]) super(nil) else super(attributes) @@ -121,7 +117,7 @@ def assign_nested_attributes_for_collection_association(association, attributes, if options&.dig(:allow_destroy) attributes.reject! do |attribute| - ActiveRecord::Type::Boolean.new.cast(attribute.stringify_keys.dig("_destroy")) + ActiveRecord::Type::Boolean.new.cast(attribute.stringify_keys["_destroy"]) end end diff --git a/lib/store_model/types/enum_type.rb b/lib/store_model/types/enum_type.rb index 54ab2f9..0e3f067 100644 --- a/lib/store_model/types/enum_type.rb +++ b/lib/store_model/types/enum_type.rb @@ -14,6 +14,7 @@ class EnumType < ActiveModel::Type::Value def initialize(mapping, raise_on_invalid_values) @mapping = mapping @raise_on_invalid_values = raise_on_invalid_values + super() end # Returns type diff --git a/lib/store_model/types/many.rb b/lib/store_model/types/many.rb index 1c2528d..521d751 100644 --- a/lib/store_model/types/many.rb +++ b/lib/store_model/types/many.rb @@ -14,6 +14,7 @@ class Many < ManyBase # @return [StoreModel::Types::Many] def initialize(model_klass) @model_klass = model_klass + super() end # Returns type diff --git a/lib/store_model/types/many_polymorphic.rb b/lib/store_model/types/many_polymorphic.rb index da0a608..5e04c06 100644 --- a/lib/store_model/types/many_polymorphic.rb +++ b/lib/store_model/types/many_polymorphic.rb @@ -16,6 +16,7 @@ class ManyPolymorphic < ManyBase # @return [StoreModel::Types::PolymorphicArrayType ] def initialize(model_wrapper) @model_wrapper = model_wrapper + super() end # Returns type diff --git a/lib/store_model/types/one.rb b/lib/store_model/types/one.rb index b9da78d..b75d1fa 100644 --- a/lib/store_model/types/one.rb +++ b/lib/store_model/types/one.rb @@ -13,6 +13,7 @@ class One < OneBase # @return [StoreModel::Types::One] def initialize(model_klass) @model_klass = model_klass + super() end # Returns type diff --git a/lib/store_model/types/one_polymorphic.rb b/lib/store_model/types/one_polymorphic.rb index 9608aa2..f226f0a 100644 --- a/lib/store_model/types/one_polymorphic.rb +++ b/lib/store_model/types/one_polymorphic.rb @@ -15,6 +15,7 @@ class OnePolymorphic < OneBase # @return [StoreModel::Types::OnePolymorphic ] def initialize(model_wrapper) @model_wrapper = model_wrapper + super() end # Returns type diff --git a/spec/dummy/app/models/configuration.rb b/spec/dummy/app/models/configuration.rb index 0dc551c..eeabb9b 100644 --- a/spec/dummy/app/models/configuration.rb +++ b/spec/dummy/app/models/configuration.rb @@ -8,11 +8,11 @@ class Encrypted < ActiveModel::Type::Value ENCODING = "MOhqm0PnycUZeLdK8YvDCgNfb7FJtiHT52BrxoAkas9RWlXpEujSGI64VzQ31w" def serialize(value) - value&.tr(ALPHABET, ENCODING) if value + value&.tr(ALPHABET, ENCODING) end def deserialize(value) - value&.tr(ENCODING, ALPHABET) if value + value&.tr(ENCODING, ALPHABET) end end diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb index 79bbfc1..11a3b46 100644 --- a/spec/dummy/db/schema.rb +++ b/spec/dummy/db/schema.rb @@ -5,6 +5,7 @@ t.string :name t.references :store, null: true t.json :configuration, default: {} + t.json :product_configuration, default: {} end create_table :stores do |t| diff --git a/spec/store_model/nested_attributes_spec.rb b/spec/store_model/nested_attributes_spec.rb index 925365f..b642993 100644 --- a/spec/store_model/nested_attributes_spec.rb +++ b/spec/store_model/nested_attributes_spec.rb @@ -45,15 +45,17 @@ class ProductConfiguration end class Product < ActiveRecord::Base - attribute :configuration, ProductConfiguration.to_type, default: ProductConfiguration.new + attribute :product_configuration, ProductConfiguration.to_type, default: ProductConfiguration.new end suppliers = [Supplier.new(title: "The Supplier")] - product = Product.create!(configuration: ProductConfiguration.new(color: "red", suppliers: suppliers)) + product = Product.create!(product_configuration: ProductConfiguration.new(color: "red", suppliers: suppliers)) - configuration_type = Product.select('json_type(configuration, "$") as config').where(id: product.id).first - suppliers_type = Product.select('json_type(configuration, "$.suppliers") as config').where(id: product.id).first - supplier_type = Product.select('json_type(configuration, "$.suppliers[0]") as config').where(id: product.id).first + configuration_type = Product.select('json_type(product_configuration, "$") as config').where(id: product.id).first + suppliers_type = Product.select('json_type(product_configuration, "$.suppliers") as config') + .where(id: product.id).first + supplier_type = Product.select('json_type(product_configuration, "$.suppliers[0]") as config') + .where(id: product.id).first expect(configuration_type.config).to eq("object") expect(suppliers_type.config).to eq("array") @@ -70,6 +72,7 @@ class NestedStore enum :status, in: { active: 1, inactive: 2, archived: 3 } end + class Store include StoreModel::Model @@ -532,8 +535,7 @@ def self.name end end - it { expect { subject }.not_to(raise_error) } - it { expect(subject.instance_methods).to(include(:suppliers_attributes=)) } + it { expect { subject }.to raise_error(ActiveRecord::StatementInvalid) } end end end diff --git a/store_model.gemspec b/store_model.gemspec index 4c223e8..885630a 100644 --- a/store_model.gemspec +++ b/store_model.gemspec @@ -16,11 +16,11 @@ Gem::Specification.new do |spec| spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] - spec.required_ruby_version = ">= 2.6" + spec.required_ruby_version = ">= 3.0" - spec.add_dependency "activerecord", ">= 5.2" + spec.add_dependency "activerecord", ">= 7.0" spec.add_development_dependency "rspec" spec.add_development_dependency "rspec-rails" - spec.add_development_dependency "rubocop", "0.64.0" + spec.add_development_dependency "rubocop", "1.65.1" end