diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..28cd875 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: ['3.0', '3.2'] + mongoid: ['7_4', '8_0', '8_1'] + env: + BUNDLE_GEMFILE: gemfiles/mongoid_${{ matrix.mongoid }}.gemfile + CI: 'true' + services: + mongo: + image: mongo:7.0 + ports: + - 27017:27017 + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - run: bundle exec rspec diff --git a/.gitignore b/.gitignore index 31cafb5..489e485 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ tmp *.o *.a mkmf.log +gemfiles/*.gemfile.lock diff --git a/changes.md b/changes.md index 901b29d..44d1333 100644 --- a/changes.md +++ b/changes.md @@ -1,3 +1,9 @@ +# 0.3.0 +- Added GitHub Actions CI: Mongoid 7.4 / 8.0 / 8.1 x Ruby 3.0 / 3.2 against mongo:7.0 +- Verified compatibility with Mongoid 8.0 and 8.1 +- Fixed BulkSavable#bulk_assign NameError when no embeds are given +- Fixed Nesting#nested_save! argument forwarding on Ruby 3 +- Loosened dev dependencies (mongoid >= 7.4, < 8.2; dropped bundler ~> 1.6) # 0.2.6 - Upgraded Mongoid to 7.4.x # 0.2.5 diff --git a/gemfiles/mongoid_7_4.gemfile b/gemfiles/mongoid_7_4.gemfile new file mode 100644 index 0000000..caf6ad3 --- /dev/null +++ b/gemfiles/mongoid_7_4.gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'mongoid', '~> 7.4.0' + +gemspec path: '..' diff --git a/gemfiles/mongoid_8_0.gemfile b/gemfiles/mongoid_8_0.gemfile new file mode 100644 index 0000000..d976ee6 --- /dev/null +++ b/gemfiles/mongoid_8_0.gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'mongoid', '~> 8.0.0' + +gemspec path: '..' diff --git a/gemfiles/mongoid_8_1.gemfile b/gemfiles/mongoid_8_1.gemfile new file mode 100644 index 0000000..6a394a4 --- /dev/null +++ b/gemfiles/mongoid_8_1.gemfile @@ -0,0 +1,5 @@ +source 'https://rubygems.org' + +gem 'mongoid', '~> 8.1.0' + +gemspec path: '..' diff --git a/lib/mongoidal/bulk_savable.rb b/lib/mongoidal/bulk_savable.rb index cfdf76c..115486c 100644 --- a/lib/mongoidal/bulk_savable.rb +++ b/lib/mongoidal/bulk_savable.rb @@ -8,7 +8,7 @@ module BulkSavable # If a specific set of embed relations are not provided then all embeds_many relations will be used def bulk_assign(attributes, *embeds) if embeds.blank? - embeds = self.class.relations.select {|k, v| v.is_a?(Mongoid::Association::Referenced::EmbesMany) }.keys + embeds = self.class.relations.select {|k, v| v.is_a?(Mongoid::Association::Embedded::EmbedsMany) }.keys end root_attrs = attributes.except(*embeds) diff --git a/lib/mongoidal/nesting.rb b/lib/mongoidal/nesting.rb index fa01d2a..17bacbf 100644 --- a/lib/mongoidal/nesting.rb +++ b/lib/mongoidal/nesting.rb @@ -56,8 +56,8 @@ def nested_assign(attributes, position: nil) # and insert/update/delete them one by one based off of the existing data, # instead of just replacing the entire array like a normal save would do. # Any items not present within the new array will be destroyed - def nested_save!(attributes, *embeds, &block) - to_delete = nested_assign(attributes, *embeds) + def nested_save!(attributes, **options, &block) + to_delete = nested_assign(attributes, **options) if valid? block.call(to_delete) if block_given? to_delete.each(&:destroy) diff --git a/lib/mongoidal/version.rb b/lib/mongoidal/version.rb index fad807e..ec61efe 100644 --- a/lib/mongoidal/version.rb +++ b/lib/mongoidal/version.rb @@ -1,3 +1,3 @@ module Mongoidal - VERSION = "0.2.6" + VERSION = "0.3.0" end diff --git a/mongoidal.gemspec b/mongoidal.gemspec index fc2e833..99879dc 100644 --- a/mongoidal.gemspec +++ b/mongoidal.gemspec @@ -18,10 +18,11 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "mongoid-slug" - spec.add_development_dependency "mongoid", '~> 7.4.0' - spec.add_development_dependency "bundler", "~> 1.6" + spec.add_development_dependency "mongoid", ">= 7.4", "< 8.2" spec.add_development_dependency "sidekiq" spec.add_development_dependency "globalid" + # json 3 rejects the quirks_mode option that ActiveSupport 7.x passes to JSON.generate + spec.add_development_dependency "json", "< 3" spec.add_development_dependency "rake" spec.add_development_dependency "rspec" spec.add_development_dependency "rspec-its" diff --git a/spec/bulk_savable_spec.rb b/spec/bulk_savable_spec.rb new file mode 100644 index 0000000..8a3effe --- /dev/null +++ b/spec/bulk_savable_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +class BulkParent + include Mongoid::Document + include Mongoidal::BulkSavable + field :name, type: String + embeds_many :kids, class_name: 'BulkKid' +end + +class BulkKid + include Mongoid::Document + embedded_in :bulk_parent + field :name, type: String +end + +describe Mongoidal::BulkSavable do + let(:parent) { BulkParent.create(name: 'Parent') } + let!(:kid1) { parent.kids.create(name: 'Kid 1') } + let!(:kid2) { parent.kids.create(name: 'Kid 2') } + + # with no embeds given, every embeds_many relation is used + it 'updates, adds and flags deletes across embeds_many relations' do + to_delete = parent.bulk_assign( + 'name' => 'Renamed', + 'kids' => [ + { 'id' => kid1.id.to_s, 'name' => 'Kid 1b' }, + { 'id' => kid2.id.to_s, 'deleted' => true }, + { 'name' => 'Kid 3' } + ] + ) + + expect(parent.name).to eq 'Renamed' + expect(parent.kids.map(&:name)).to match_array ['Kid 1b', 'Kid 2', 'Kid 3'] + expect(to_delete).to eq [kid2] + end + + it 'accepts an explicit list of embeds' do + parent.bulk_assign({ kids: [{ id: kid1.id, name: 'Kid 1b' }] }, :kids) + expect(parent.kids.find(kid1.id).name).to eq 'Kid 1b' + end +end diff --git a/spec/global_id_spec.rb b/spec/global_id_spec.rb new file mode 100644 index 0000000..48d0dc2 --- /dev/null +++ b/spec/global_id_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +class GidParent + include Mongoid::Document + include Mongoidal::Identification + field :name, type: String + embeds_many :kids, class_name: 'GidKid' +end + +class GidKid + include Mongoid::Document + include Mongoidal::Identification + embedded_in :gid_parent + field :name, type: String +end + +GlobalID.app = 'mongoidal' + +describe Mongoidal::Identification do + let(:parent) { GidParent.create(name: 'Parent') } + let(:kid) { parent.kids.create(name: 'Kid') } + + def locate(doc) + Mongoidal::Locator.new(GlobalID.parse(doc.to_global_id.to_s)).locate + end + + it 'round trips a root document' do + expect(locate(parent)).to eq parent + end + + it 'round trips an embedded document through its root' do + expect(locate(kid)).to eq kid + end + + it 'rebuilds a destroyed embedded document from its attributes' do + kid.destroy + gid = kid.to_global_id.to_s + found = Mongoidal::Locator.new(GlobalID.parse(gid)).locate + expect(found.name).to eq 'Kid' + expect(found).to be_destroyed + end +end diff --git a/spec/nesting_spec.rb b/spec/nesting_spec.rb index eeac66d..0407e31 100644 --- a/spec/nesting_spec.rb +++ b/spec/nesting_spec.rb @@ -76,7 +76,7 @@ def self.find_within_collection(collection, attrs) end it 'should match using find_within_collection' do - parent.nested_save!(kids:[{name: 'Kid 1', age: 4}]) + parent.nested_save!({kids: [{name: "Kid 1", age: 4}]}) expect(kid1.age).to eq 4 end @@ -107,7 +107,7 @@ def self.find_within_collection(collection, attrs) # this is standard functionality it 'should update existing favorite' do expect(parent.favorite.name).to eq 'Kid 1' - parent.nested_assign(favorite: {name: 'Kid 2'}) + parent.nested_assign({favorite: {name: 'Kid 2'}}) expect(parent.favorite.id).to eq favorite.id end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2107a59..8ae14d8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -35,7 +35,7 @@ end config.after(:suite) do - Mongoid::Threaded.sessions[:default].drop if ENV['CI'] + Mongoid.default_client.database.drop if ENV["CI"] end # rspec-expectations config goes here. You can use an alternate