diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 0000000..f773bf6 --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,68 @@ +name: Build and Tests + +on: + push: + branches: + - main + pull_request: + +jobs: + linter: + runs-on: ubuntu-latest + name: "Rubocop" + env: + BUNDLE_GEMFILE: ci/Gemfile.rails-6.1 + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.0" + bundler-cache: true + - name: Run linter + run: bundle exec rubocop + ruby-2: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: + - "2.7" + gemfile: + - ci/Gemfile.rails-5.2 + name: "ruby-${{ matrix.ruby }}/${{ matrix.gemfile }} specs" + env: + BUNDLE_GEMFILE: ${{ matrix.gemfile }} + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run tests + run: bundle exec rspec + ruby-3: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: + - "3.0" + - "3.1" + - "3.2" + - "3.3" + gemfile: + - ci/Gemfile.rails-6.0 + - ci/Gemfile.rails-6.1 + - ci/Gemfile.rails-7.0 + - ci/Gemfile.rails-7.1 + name: "ruby-${{ matrix.ruby }}/${{ matrix.gemfile }} specs" + env: + BUNDLE_GEMFILE: ${{ matrix.gemfile }} + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run tests + run: bundle exec rspec diff --git a/.rubocop.yml b/.rubocop.yml index 5367340..c8a7804 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -17,6 +17,22 @@ AllCops: - "db/**/*" - "tmp/**/*" - "vendor/**/*" + NewCops: enable + +RSpec/MultipleExpectations: + Enabled: false + +RSpec/ExampleLength: + Enabled: false + +RSpec/MessageSpies: + Enabled: false + +RSpec/FilePath: + Enabled: false + +RSpec/SpecFilePathFormat: + Enabled: false Layout/SpaceInsideHashLiteralBraces: Enabled: false diff --git a/Gemfile b/Gemfile index da9266e..fdf2ef4 100644 --- a/Gemfile +++ b/Gemfile @@ -2,9 +2,11 @@ source 'https://rubygems.org' -gem 'esse', github: 'marcosgz/esse', branch: 'master' +gem 'esse', '~> 0.2.4' gem 'sqlite3', '~> 1.3.6' gem 'activerecord', '~> 5.2' +gem 'esse-rspec', '~> 0.0.6' +gem 'elasticsearch', '~> 7.17', '>= 7.17.10' # Specify your gem's dependencies in esse-active_record.gemspec gemspec diff --git a/Gemfile.lock b/Gemfile.lock index c342aa4..59ae7f8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,18 +1,9 @@ -GIT - remote: https://github.com/marcosgz/esse.git - revision: fa4eb4d8c54aae89c388415585b4b27c75b31198 - branch: master - specs: - esse (0.2.1) - multi_json - thor (>= 0.19) - PATH remote: . specs: - esse-active_record (0.1.1) + esse-active_record (0.2.0) activerecord (>= 4.2, < 8) - esse + esse (>= 0.2.3) GEM remote: https://rubygems.org/ @@ -28,80 +19,119 @@ GEM i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) arel (9.0.0) ast (2.4.2) awesome_print (1.9.2) + base64 (0.2.0) coderay (1.1.3) - concurrent-ruby (1.1.10) + concurrent-ruby (1.2.3) crack (0.4.5) rexml diff-lcs (1.5.0) - dotenv (2.7.6) - hashdiff (1.0.1) - i18n (1.12.0) + dotenv (2.8.1) + elasticsearch (7.17.10) + elasticsearch-api (= 7.17.10) + elasticsearch-transport (= 7.17.10) + elasticsearch-api (7.17.10) + multi_json + elasticsearch-transport (7.17.10) + faraday (>= 1, < 3) + multi_json + esse (0.2.6) + multi_json + thor (>= 0.19) + esse-rspec (0.0.6) + esse (>= 0.2.4) + rspec (>= 3) + factory_bot (6.2.0) + activesupport (>= 5.0.0) + faraday (2.8.1) + base64 + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-net_http (3.0.2) + hashdiff (1.1.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) + json (2.7.1) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) method_source (1.0.0) - minitest (5.16.2) + minitest (5.21.2) multi_json (1.15.0) - parallel (1.22.1) - parser (3.1.2.0) + parallel (1.24.0) + parser (3.3.0.5) ast (~> 2.4.1) - pry (0.14.1) + racc + pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) - public_suffix (4.0.7) + public_suffix (5.0.4) + racc (1.7.3) rainbow (3.1.1) - rake (12.3.3) - regexp_parser (2.5.0) - rexml (3.2.5) - rspec (3.11.0) - rspec-core (~> 3.11.0) - rspec-expectations (~> 3.11.0) - rspec-mocks (~> 3.11.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rake (13.1.0) + regexp_parser (2.9.0) + rexml (3.2.6) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-mocks (3.11.1) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-support (3.11.0) - rubocop (1.29.1) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) + rubocop (1.50.2) + json (~> 2.3) parallel (~> 1.10) - parser (>= 3.1.0.0) + parser (>= 3.2.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.17.0, < 2.0) + rubocop-ast (>= 1.28.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.18.0) - parser (>= 3.1.1.0) - rubocop-performance (1.13.3) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.30.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.18.0) + rubocop (~> 1.41) + rubocop-performance (1.16.0) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) - rubocop-rspec (2.11.1) - rubocop (~> 1.19) - ruby-progressbar (1.11.0) + rubocop-rspec (2.20.0) + rubocop (~> 1.33) + rubocop-capybara (~> 2.17) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) sqlite3 (1.3.13) - standard (1.12.1) - rubocop (= 1.29.1) - rubocop-performance (= 1.13.3) - thor (1.2.1) + standard (1.28.5) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50.2) + standard-custom (~> 1.0.0) + standard-performance (~> 1.0.1) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.0.1) + lint_roller (~> 1.0) + rubocop-performance (~> 1.16.0) + thor (1.3.0) thread_safe (0.3.6) - tzinfo (1.2.10) + tzinfo (1.2.11) thread_safe (~> 0.1) - unicode-display_width (2.2.0) - webmock (3.14.0) + unicode-display_width (2.5.0) + webmock (3.19.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.7.0) - yard (0.9.28) - webrick (~> 1.7.0) + yard (0.9.34) PLATFORMS x86_64-darwin-19 @@ -111,18 +141,21 @@ DEPENDENCIES activerecord (~> 5.2) awesome_print dotenv - esse! + elasticsearch (~> 7.17, >= 7.17.10) + esse (~> 0.2.4) esse-active_record! + esse-rspec (~> 0.0.6) + factory_bot pry - rake (~> 12.3) - rspec (~> 3.0) - rubocop (~> 1.20) - rubocop-performance (~> 1.11, >= 1.11.5) - rubocop-rspec (~> 2.4) + rake + rspec + rubocop + rubocop-performance + rubocop-rspec sqlite3 (~> 1.3.6) - standard (~> 1.3) - webmock (~> 3.14) - yard (~> 0.9.20) + standard + webmock + yard BUNDLED WITH - 2.3.21 + 2.3.22 diff --git a/README.md b/README.md index a718d29..76d2656 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ class UsersIndex < Esse::Index repository :user do collection ::User - serializer # ... + document # ... end end ``` @@ -41,12 +41,12 @@ class UsersIndex < Esse::Index repository :account do collection ::Account - serializer # ... + document # ... end repository :admin do collection ::User.where(admin: true) - serializer # ... + document # ... end end ``` @@ -63,12 +63,12 @@ class UsersIndex < Esse::Index scope :active, -> { where(active: true) } scope :role, ->(role) { where(role: role) } end - serializer # ... + document # ... end end # Import data using the scopes -# > UsersIndex.elasticsearch.import(context: { active: true, role: 'admin' }) +# > UsersIndex.import(context: { active: true, role: 'admin' }) # # Streaming data using the scopes # > UsersIndex.documents(active: true, role: 'admin').first @@ -76,7 +76,7 @@ end ## Collection Batch Context -Assume that you have a collection of orders and you want to also include the customer data that lives in a external system. To avoid making a request for each order, you can use the `batch_context` to fetch the data in batches and make it available in the serializer context. +Assume that you have a collection of orders and you want to also include the customer data that lives in a external system. To avoid making a request for each order, you can use the `batch_context` to fetch the data in batches and make it available in the document context. ```ruby class OrdersIndex < Esse::Index @@ -85,12 +85,12 @@ class OrdersIndex < Esse::Index repository :order do collection ::Order do batch_context :customers do |orders, **_existing_context| - # The return value will be available in the serializer context + # The return value will be available in the document context # { customers: } ExternalSystem::Customer.find_all_by_ids(orders.map(&:customer_id)).index_by(&:id) # => { 1 => , 2 => } end end - serializer do |order, customers: {}, **_| + document do |order, customers: {}, **_| customer = customers[order.customer_id] { id: order.id, @@ -113,7 +113,7 @@ class OrdersIndex < Esse::Index repository :order do collection ::Order.includes(:customer) - serializer do |order, **_| + document do |order, **_| { id: order.id, customer: { @@ -136,7 +136,7 @@ As default the active record support 3 streaming options: This is useful when you want to import simultaneous data. You can make one process import all records between 1 and 10,000, and another from 10,000 and beyond ```ruby -UsersIndex.elasticsearch.import(context: { start: 1, finish: 10000, batch_size: 500 }) +UsersIndex.import(context: { start: 1, finish: 10000, batch_size: 500 }) ``` The default valueof `batch_size` can be also defined in the `collection` configuration: @@ -147,7 +147,7 @@ class UsersIndex < Esse::Index repository :user do collection ::User, batch_size: 500 - serializer # ... + document # ... end end ``` @@ -162,7 +162,7 @@ class UsersIndex < Esse::Index repository :user, const: true do collection ::User - serializer # ... + document # ... end end diff --git a/bin/setup b/bin/setup index dce67d8..1ffbc59 100755 --- a/bin/setup +++ b/bin/setup @@ -4,5 +4,4 @@ IFS=$'\n\t' set -vx bundle install - -# Do any other automated setup that you need to do here +find ci -type f \( -iname "Gemfile.*" ! -iname "*.lock" \) -exec bundle install --gemfile {} \; diff --git a/ci/Gemfile.rails-5.2 b/ci/Gemfile.rails-5.2 new file mode 100644 index 0000000..8752437 --- /dev/null +++ b/ci/Gemfile.rails-5.2 @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'sqlite3', '~> 1.3.6' +gem 'activerecord', '~> 5.2' +gem 'esse-rspec', '~> 0.0.6' +gem 'elasticsearch', '~> 7.17', '>= 7.17.10' + +gemspec path: ".." diff --git a/ci/Gemfile.rails-5.2.lock b/ci/Gemfile.rails-5.2.lock new file mode 100644 index 0000000..c125964 --- /dev/null +++ b/ci/Gemfile.rails-5.2.lock @@ -0,0 +1,160 @@ +PATH + remote: .. + specs: + esse-active_record (0.2.0) + activerecord (>= 4.2, < 8) + esse (>= 0.2.3) + +GEM + remote: https://rubygems.org/ + specs: + activemodel (5.2.8.1) + activesupport (= 5.2.8.1) + activerecord (5.2.8.1) + activemodel (= 5.2.8.1) + activesupport (= 5.2.8.1) + arel (>= 9.0) + activesupport (5.2.8.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + arel (9.0.0) + ast (2.4.2) + awesome_print (1.9.2) + base64 (0.2.0) + coderay (1.1.3) + concurrent-ruby (1.2.3) + crack (0.4.5) + rexml + diff-lcs (1.5.0) + dotenv (2.8.1) + elasticsearch (7.17.10) + elasticsearch-api (= 7.17.10) + elasticsearch-transport (= 7.17.10) + elasticsearch-api (7.17.10) + multi_json + elasticsearch-transport (7.17.10) + faraday (>= 1, < 3) + multi_json + esse (0.2.6) + multi_json + thor (>= 0.19) + esse-rspec (0.0.6) + esse (>= 0.2.4) + rspec (>= 3) + faraday (2.8.1) + base64 + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-net_http (3.0.2) + hashdiff (1.1.0) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + json (2.7.1) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) + method_source (1.0.0) + minitest (5.21.2) + multi_json (1.15.0) + parallel (1.24.0) + parser (3.3.0.5) + ast (~> 2.4.1) + racc + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + public_suffix (5.0.4) + racc (1.7.3) + rainbow (3.1.1) + rake (13.1.0) + regexp_parser (2.9.0) + rexml (3.2.6) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) + rubocop (1.59.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.4) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.30.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.30.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.20.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.25.1) + rubocop (~> 1.41) + rubocop-performance (1.20.2) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) + rubocop-rspec (2.26.1) + rubocop (~> 1.40) + rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + sqlite3 (1.3.13) + standard (1.33.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.59.0) + standard-custom (~> 1.0.0) + standard-performance (~> 1.3) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.3.1) + lint_roller (~> 1.1) + rubocop-performance (~> 1.20.2) + thor (1.3.0) + thread_safe (0.3.6) + tzinfo (1.2.11) + thread_safe (~> 0.1) + unicode-display_width (2.5.0) + webmock (3.19.1) + addressable (>= 2.8.0) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) + yard (0.9.34) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + activerecord (~> 5.2) + awesome_print + dotenv + elasticsearch (~> 7.17, >= 7.17.10) + esse-active_record! + esse-rspec (~> 0.0.6) + pry + rake + rspec + rubocop + rubocop-performance + rubocop-rspec + sqlite3 (~> 1.3.6) + standard + webmock + yard + +BUNDLED WITH + 2.3.21 diff --git a/ci/Gemfile.rails-6.0 b/ci/Gemfile.rails-6.0 new file mode 100644 index 0000000..2f56edf --- /dev/null +++ b/ci/Gemfile.rails-6.0 @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'sqlite3' +gem 'activerecord', '~> 6.0' +gem 'esse-rspec', '~> 0.0.6' +gem 'elasticsearch', '~> 7.17', '>= 7.17.10' + +gemspec path: ".." diff --git a/ci/Gemfile.rails-6.0.lock b/ci/Gemfile.rails-6.0.lock new file mode 100644 index 0000000..c2abdd7 --- /dev/null +++ b/ci/Gemfile.rails-6.0.lock @@ -0,0 +1,159 @@ +PATH + remote: .. + specs: + esse-active_record (0.2.0) + activerecord (>= 4.2, < 8) + esse (>= 0.2.3) + +GEM + remote: https://rubygems.org/ + specs: + activemodel (6.1.7.6) + activesupport (= 6.1.7.6) + activerecord (6.1.7.6) + activemodel (= 6.1.7.6) + activesupport (= 6.1.7.6) + activesupport (6.1.7.6) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + ast (2.4.2) + awesome_print (1.9.2) + coderay (1.1.3) + concurrent-ruby (1.2.3) + crack (0.4.5) + rexml + diff-lcs (1.5.0) + dotenv (2.8.1) + elasticsearch (7.17.10) + elasticsearch-api (= 7.17.10) + elasticsearch-transport (= 7.17.10) + elasticsearch-api (7.17.10) + multi_json + elasticsearch-transport (7.17.10) + faraday (>= 1, < 3) + multi_json + esse (0.2.6) + multi_json + thor (>= 0.19) + esse-rspec (0.0.6) + esse (>= 0.2.4) + rspec (>= 3) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) + faraday-net_http (3.1.0) + net-http + hashdiff (1.1.0) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + json (2.7.1) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) + method_source (1.0.0) + minitest (5.21.2) + multi_json (1.15.0) + net-http (0.4.1) + uri + parallel (1.24.0) + parser (3.3.0.5) + ast (~> 2.4.1) + racc + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + public_suffix (5.0.4) + racc (1.7.3) + rainbow (3.1.1) + rake (13.1.0) + regexp_parser (2.9.0) + rexml (3.2.6) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) + rubocop (1.59.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.4) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.30.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.30.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.20.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.25.1) + rubocop (~> 1.41) + rubocop-performance (1.20.2) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) + rubocop-rspec (2.26.1) + rubocop (~> 1.40) + rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) + ruby-progressbar (1.13.0) + sqlite3 (1.7.0-x86_64-linux) + standard (1.33.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.59.0) + standard-custom (~> 1.0.0) + standard-performance (~> 1.3) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.3.1) + lint_roller (~> 1.1) + rubocop-performance (~> 1.20.2) + thor (1.3.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + uri (0.13.0) + webmock (3.19.1) + addressable (>= 2.8.0) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) + yard (0.9.34) + zeitwerk (2.6.12) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + activerecord (~> 6.0) + awesome_print + dotenv + elasticsearch (~> 7.17, >= 7.17.10) + esse-active_record! + esse-rspec (~> 0.0.6) + pry + rake + rspec + rubocop + rubocop-performance + rubocop-rspec + sqlite3 + standard + webmock + yard + +BUNDLED WITH + 2.3.22 diff --git a/ci/Gemfile.rails-6.1 b/ci/Gemfile.rails-6.1 new file mode 100644 index 0000000..f361544 --- /dev/null +++ b/ci/Gemfile.rails-6.1 @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'sqlite3' +gem 'activerecord', '~> 6.1' +gem 'esse-rspec', '~> 0.0.6' +gem 'elasticsearch', '~> 7.17', '>= 7.17.10' + +gemspec path: ".." diff --git a/ci/Gemfile.rails-6.1.lock b/ci/Gemfile.rails-6.1.lock new file mode 100644 index 0000000..be9e223 --- /dev/null +++ b/ci/Gemfile.rails-6.1.lock @@ -0,0 +1,159 @@ +PATH + remote: .. + specs: + esse-active_record (0.2.0) + activerecord (>= 4.2, < 8) + esse (>= 0.2.3) + +GEM + remote: https://rubygems.org/ + specs: + activemodel (6.1.7.6) + activesupport (= 6.1.7.6) + activerecord (6.1.7.6) + activemodel (= 6.1.7.6) + activesupport (= 6.1.7.6) + activesupport (6.1.7.6) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + ast (2.4.2) + awesome_print (1.9.2) + coderay (1.1.3) + concurrent-ruby (1.2.3) + crack (0.4.5) + rexml + diff-lcs (1.5.0) + dotenv (2.8.1) + elasticsearch (7.17.10) + elasticsearch-api (= 7.17.10) + elasticsearch-transport (= 7.17.10) + elasticsearch-api (7.17.10) + multi_json + elasticsearch-transport (7.17.10) + faraday (>= 1, < 3) + multi_json + esse (0.2.6) + multi_json + thor (>= 0.19) + esse-rspec (0.0.6) + esse (>= 0.2.4) + rspec (>= 3) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) + faraday-net_http (3.1.0) + net-http + hashdiff (1.1.0) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + json (2.7.1) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) + method_source (1.0.0) + minitest (5.21.2) + multi_json (1.15.0) + net-http (0.4.1) + uri + parallel (1.24.0) + parser (3.3.0.5) + ast (~> 2.4.1) + racc + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + public_suffix (5.0.4) + racc (1.7.3) + rainbow (3.1.1) + rake (13.1.0) + regexp_parser (2.9.0) + rexml (3.2.6) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) + rubocop (1.59.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.4) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.30.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.30.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.20.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.25.1) + rubocop (~> 1.41) + rubocop-performance (1.20.2) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) + rubocop-rspec (2.26.1) + rubocop (~> 1.40) + rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) + ruby-progressbar (1.13.0) + sqlite3 (1.7.0-x86_64-linux) + standard (1.33.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.59.0) + standard-custom (~> 1.0.0) + standard-performance (~> 1.3) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.3.1) + lint_roller (~> 1.1) + rubocop-performance (~> 1.20.2) + thor (1.3.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + uri (0.13.0) + webmock (3.19.1) + addressable (>= 2.8.0) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) + yard (0.9.34) + zeitwerk (2.6.12) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + activerecord (~> 6.1) + awesome_print + dotenv + elasticsearch (~> 7.17, >= 7.17.10) + esse-active_record! + esse-rspec (~> 0.0.6) + pry + rake + rspec + rubocop + rubocop-performance + rubocop-rspec + sqlite3 + standard + webmock + yard + +BUNDLED WITH + 2.3.22 diff --git a/ci/Gemfile.rails-7.0 b/ci/Gemfile.rails-7.0 new file mode 100644 index 0000000..2f56edf --- /dev/null +++ b/ci/Gemfile.rails-7.0 @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'sqlite3' +gem 'activerecord', '~> 6.0' +gem 'esse-rspec', '~> 0.0.6' +gem 'elasticsearch', '~> 7.17', '>= 7.17.10' + +gemspec path: ".." diff --git a/ci/Gemfile.rails-7.0.lock b/ci/Gemfile.rails-7.0.lock new file mode 100644 index 0000000..c2abdd7 --- /dev/null +++ b/ci/Gemfile.rails-7.0.lock @@ -0,0 +1,159 @@ +PATH + remote: .. + specs: + esse-active_record (0.2.0) + activerecord (>= 4.2, < 8) + esse (>= 0.2.3) + +GEM + remote: https://rubygems.org/ + specs: + activemodel (6.1.7.6) + activesupport (= 6.1.7.6) + activerecord (6.1.7.6) + activemodel (= 6.1.7.6) + activesupport (= 6.1.7.6) + activesupport (6.1.7.6) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + ast (2.4.2) + awesome_print (1.9.2) + coderay (1.1.3) + concurrent-ruby (1.2.3) + crack (0.4.5) + rexml + diff-lcs (1.5.0) + dotenv (2.8.1) + elasticsearch (7.17.10) + elasticsearch-api (= 7.17.10) + elasticsearch-transport (= 7.17.10) + elasticsearch-api (7.17.10) + multi_json + elasticsearch-transport (7.17.10) + faraday (>= 1, < 3) + multi_json + esse (0.2.6) + multi_json + thor (>= 0.19) + esse-rspec (0.0.6) + esse (>= 0.2.4) + rspec (>= 3) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) + faraday-net_http (3.1.0) + net-http + hashdiff (1.1.0) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + json (2.7.1) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) + method_source (1.0.0) + minitest (5.21.2) + multi_json (1.15.0) + net-http (0.4.1) + uri + parallel (1.24.0) + parser (3.3.0.5) + ast (~> 2.4.1) + racc + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + public_suffix (5.0.4) + racc (1.7.3) + rainbow (3.1.1) + rake (13.1.0) + regexp_parser (2.9.0) + rexml (3.2.6) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) + rubocop (1.59.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.4) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.30.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.30.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.20.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.25.1) + rubocop (~> 1.41) + rubocop-performance (1.20.2) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) + rubocop-rspec (2.26.1) + rubocop (~> 1.40) + rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) + ruby-progressbar (1.13.0) + sqlite3 (1.7.0-x86_64-linux) + standard (1.33.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.59.0) + standard-custom (~> 1.0.0) + standard-performance (~> 1.3) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.3.1) + lint_roller (~> 1.1) + rubocop-performance (~> 1.20.2) + thor (1.3.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + uri (0.13.0) + webmock (3.19.1) + addressable (>= 2.8.0) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) + yard (0.9.34) + zeitwerk (2.6.12) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + activerecord (~> 6.0) + awesome_print + dotenv + elasticsearch (~> 7.17, >= 7.17.10) + esse-active_record! + esse-rspec (~> 0.0.6) + pry + rake + rspec + rubocop + rubocop-performance + rubocop-rspec + sqlite3 + standard + webmock + yard + +BUNDLED WITH + 2.3.22 diff --git a/ci/Gemfile.rails-7.1 b/ci/Gemfile.rails-7.1 new file mode 100644 index 0000000..55043a5 --- /dev/null +++ b/ci/Gemfile.rails-7.1 @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem 'sqlite3' +gem 'activerecord', '~> 7.1' +gem 'esse-rspec', '~> 0.0.6' +gem 'elasticsearch', '~> 7.17', '>= 7.17.10' + +gemspec path: ".." diff --git a/ci/Gemfile.rails-7.1.lock b/ci/Gemfile.rails-7.1.lock new file mode 100644 index 0000000..c5f29ab --- /dev/null +++ b/ci/Gemfile.rails-7.1.lock @@ -0,0 +1,171 @@ +PATH + remote: .. + specs: + esse-active_record (0.2.0) + activerecord (>= 4.2, < 8) + esse (>= 0.2.3) + +GEM + remote: https://rubygems.org/ + specs: + activemodel (7.1.3) + activesupport (= 7.1.3) + activerecord (7.1.3) + activemodel (= 7.1.3) + activesupport (= 7.1.3) + timeout (>= 0.4.0) + activesupport (7.1.3) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + ast (2.4.2) + awesome_print (1.9.2) + base64 (0.2.0) + bigdecimal (3.1.6) + coderay (1.1.3) + concurrent-ruby (1.2.3) + connection_pool (2.4.1) + crack (0.4.5) + rexml + diff-lcs (1.5.0) + dotenv (2.8.1) + drb (2.2.0) + ruby2_keywords + elasticsearch (7.17.10) + elasticsearch-api (= 7.17.10) + elasticsearch-transport (= 7.17.10) + elasticsearch-api (7.17.10) + multi_json + elasticsearch-transport (7.17.10) + faraday (>= 1, < 3) + multi_json + esse (0.2.6) + multi_json + thor (>= 0.19) + esse-rspec (0.0.6) + esse (>= 0.2.4) + rspec (>= 3) + faraday (2.9.0) + faraday-net_http (>= 2.0, < 3.2) + faraday-net_http (3.1.0) + net-http + hashdiff (1.1.0) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + json (2.7.1) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) + method_source (1.0.0) + minitest (5.21.2) + multi_json (1.15.0) + mutex_m (0.2.0) + net-http (0.4.1) + uri + parallel (1.24.0) + parser (3.3.0.5) + ast (~> 2.4.1) + racc + pry (0.14.2) + coderay (~> 1.1) + method_source (~> 1.0) + public_suffix (5.0.4) + racc (1.7.3) + rainbow (3.1.1) + rake (13.1.0) + regexp_parser (2.9.0) + rexml (3.2.6) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) + rubocop (1.59.0) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.2.2.4) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.30.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.30.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.20.0) + rubocop (~> 1.41) + rubocop-factory_bot (2.25.1) + rubocop (~> 1.41) + rubocop-performance (1.20.2) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) + rubocop-rspec (2.26.1) + rubocop (~> 1.40) + rubocop-capybara (~> 2.17) + rubocop-factory_bot (~> 2.22) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + sqlite3 (1.7.0-x86_64-linux) + standard (1.33.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.59.0) + standard-custom (~> 1.0.0) + standard-performance (~> 1.3) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.3.1) + lint_roller (~> 1.1) + rubocop-performance (~> 1.20.2) + thor (1.3.0) + timeout (0.4.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) + uri (0.13.0) + webmock (3.19.1) + addressable (>= 2.8.0) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) + yard (0.9.34) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + activerecord (~> 7.1) + awesome_print + dotenv + elasticsearch (~> 7.17, >= 7.17.10) + esse-active_record! + esse-rspec (~> 0.0.6) + pry + rake + rspec + rubocop + rubocop-performance + rubocop-rspec + sqlite3 + standard + webmock + yard + +BUNDLED WITH + 2.3.22 diff --git a/esse-active_record.gemspec b/esse-active_record.gemspec index 1d8086a..2bc5e4d 100644 --- a/esse-active_record.gemspec +++ b/esse-active_record.gemspec @@ -29,17 +29,17 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency 'esse' + spec.add_dependency 'esse', '>= 0.2.3' spec.add_dependency 'activerecord', '>= 4.2', '< 8' spec.add_development_dependency 'awesome_print' spec.add_development_dependency 'dotenv' spec.add_development_dependency 'pry' - spec.add_development_dependency 'rake', '~> 12.3' - spec.add_development_dependency 'rspec', '~> 3.0' - spec.add_development_dependency 'webmock', '~> 3.14' - spec.add_development_dependency 'yard', '~> 0.9.20' - spec.add_development_dependency 'standard', '~> 1.3' - spec.add_development_dependency 'rubocop', '~> 1.20' - spec.add_development_dependency 'rubocop-performance', '~> 1.11', '>= 1.11.5' - spec.add_development_dependency 'rubocop-rspec', '~> 2.4' + spec.add_development_dependency 'rake' + spec.add_development_dependency 'rspec' + spec.add_development_dependency 'webmock' + spec.add_development_dependency 'yard' + spec.add_development_dependency 'standard' + spec.add_development_dependency 'rubocop' + spec.add_development_dependency 'rubocop-performance' + spec.add_development_dependency 'rubocop-rspec' end diff --git a/lib/esse/active_record/hooks.rb b/lib/esse/active_record/hooks.rb index f610ffd..1b0e124 100644 --- a/lib/esse/active_record/hooks.rb +++ b/lib/esse/active_record/hooks.rb @@ -141,7 +141,7 @@ def resolve_index_repository(name) private def all_repos - models.flat_map(&method(:model_repos)).uniq + models.flat_map { |model_class| model_repos(model_class) }.uniq end # Returns a list of all repositories for the given model @@ -171,7 +171,7 @@ def expand_index_repos(*repos) repos.flat_map do |repo_name| case repo_name when Class - repo_name <= Esse::Index ? repo_name.repo_hash.values : repo_name + (repo_name <= Esse::Index) ? repo_name.repo_hash.values : repo_name when String, Symbol resolve_index_repository(repo_name) else diff --git a/lib/esse/active_record/model.rb b/lib/esse/active_record/model.rb index cf3c379..04c6ab5 100644 --- a/lib/esse/active_record/model.rb +++ b/lib/esse/active_record/model.rb @@ -31,7 +31,7 @@ def index_callbacks(index_repo_name, on: %i[create update destroy], **options, & esse_index_repos[index_repo_name] ||= {} esse_index_repos[index_repo_name][operation_name] = { - record: (block || -> { self }), + record: block || -> { self }, options: options, } @@ -45,7 +45,7 @@ def index_callbacks(index_repo_name, on: %i[create update destroy], **options, & record = instance_exec(&record) if record.respond_to?(:call) repo = Esse::ActiveRecord::Hooks.resolve_index_repository(index_repo_name) document = repo.serialize(record) - repo.elasticsearch.index_document(document, **opts[:options]) if document + repo.index.index(document, **opts[:options]) if document true end end @@ -56,7 +56,9 @@ def index_callbacks(index_repo_name, on: %i[create update destroy], **options, & record = instance_exec(&record) if record.respond_to?(:call) repo = Esse::ActiveRecord::Hooks.resolve_index_repository(index_repo_name) document = repo.serialize(record) - repo.elasticsearch.delete_document(document, **opts[:options]) if document + repo.index.delete(document, **opts[:options]) if document + true + rescue Esse::Transport::NotFoundError true end end diff --git a/lib/esse/active_record/version.rb b/lib/esse/active_record/version.rb index bd29548..0252e1a 100644 --- a/lib/esse/active_record/version.rb +++ b/lib/esse/active_record/version.rb @@ -2,6 +2,6 @@ module Esse module ActiveRecord - VERSION = '0.1.1' + VERSION = '0.2.0' end end diff --git a/spec/esse/active_record/collection_batch_context_spec.rb b/spec/esse/active_record/collection_batch_context_spec.rb index bf15297..5c6c692 100644 --- a/spec/esse/active_record/collection_batch_context_spec.rb +++ b/spec/esse/active_record/collection_batch_context_spec.rb @@ -68,7 +68,7 @@ it 'does not apply any context when it is not defined' do lake_county = County.create!(name: 'Lake') instance = collection_class.new - expect { |b| instance.each(&b) }.to yield_successive_args([[lake_county], {}]) + expect { |b| instance.each(&b) }.to yield_successive_args([lake_county]) end it 'only applies the params as context when no batch context is defined' do diff --git a/spec/esse/active_record/collection_spec.rb b/spec/esse/active_record/collection_spec.rb index 4e685e0..37aba75 100644 --- a/spec/esse/active_record/collection_spec.rb +++ b/spec/esse/active_record/collection_spec.rb @@ -59,19 +59,19 @@ it 'stream data in batches according to the :batch_size option' do instance = collection_class.new(batch_size: 1) - expect { |b| instance.each(&b) }.to yield_successive_args([dogs[0..0], {}], [dogs[1..1], {}], [dogs[2..2], {}]) + expect { |b| instance.each(&b) }.to yield_successive_args(dogs[0..0], dogs[1..1], dogs[2..2]) end it 'stream data in batches according to the :batch_size option and :start option' do instance = collection_class.new(batch_size: 1, start: dogs[1].id) - expect { |b| instance.each(&b) }.to yield_successive_args([dogs[1..1], {}], [dogs[2..2], {}]) + expect { |b| instance.each(&b) }.to yield_successive_args(dogs[1..1], dogs[2..2]) end it 'stream data in batches according to the :batch_size option and :finish option' do instance = collection_class.new(batch_size: 1, finish: dogs[1].id) - expect { |b| instance.each(&b) }.to yield_successive_args([dogs[0..0], {}], [dogs[1..1], {}]) + expect { |b| instance.each(&b) }.to yield_successive_args(dogs[0..0], dogs[1..1]) end end diff --git a/spec/esse/active_record/hooks_spec.rb b/spec/esse/active_record/hooks_spec.rb index e978fa7..a105768 100644 --- a/spec/esse/active_record/hooks_spec.rb +++ b/spec/esse/active_record/hooks_spec.rb @@ -27,7 +27,7 @@ let(:repositories) { AnimalsIndex.repo_hash.values + UsersIndex.repo_hash.values } before do - stub_index(:animals) do + stub_esse_index(:animals) do plugin :active_record repository :cat, const: true do @@ -36,14 +36,14 @@ end end - stub_index(:users) do + stub_esse_index(:users) do plugin :active_record repository :user, const: true do end end - Esse::ActiveRecord::Hooks.send(:global_store)[Esse::ActiveRecord::Hooks::STORE_STATE_KEY] = nil + described_class.send(:global_store)[Esse::ActiveRecord::Hooks::STORE_STATE_KEY] = nil allow(described_class).to receive(:all_repos).and_return(repositories) end diff --git a/spec/esse/active_record/model_spec.rb b/spec/esse/active_record/model_spec.rb index ee15233..ea12758 100644 --- a/spec/esse/active_record/model_spec.rb +++ b/spec/esse/active_record/model_spec.rb @@ -1,229 +1,336 @@ require 'spec_helper' -class DummyIndexableModel - extend ActiveModel::Callbacks - include Esse::ActiveRecord::Model - - attr_reader :event - - define_model_callbacks :commit, :rollback - - %i[create update destroy].each do |event| - define_method(event) do |succeed: true| - @event = event - succeed ? run_commit_callbacks : run_rollback_callbacks - end - end - - def run_commit_callbacks - run_callbacks :commit - end - - def run_rollback_callbacks - run_callbacks :rollback - end -end - -RSpec.describe Esse::ActiveRecord::Model, model_hooks: true do +RSpec.describe Esse::ActiveRecord::Model do let(:backend_proxy) { double } before do Thread.current[Esse::ActiveRecord::Hooks::STORE_STATE_KEY] = nil @models_value_backup = Esse::ActiveRecord::Hooks.models.dup Esse::ActiveRecord::Hooks.models.clear + stub_cluster_info + + stub_esse_index(:states) do + repository :state, const: true do + document do |state, **| + { + _id: state.id, + name: state.name, + } + end + end + end + + stub_esse_index(:geographies) do + repository :state, const: true do + document do |state, **| + { + _id: state.id, + name: state.name, + type: 'state', + } + end + end + + repository :county, const: true do + document do |county, **| + { + _id: county.id, + name: county.name, + type: 'county', + }.tap do |doc| + doc[:state] = { id: county.state.id, name: county.state.name } if county.state + end + end + end + end end after do - Esse::ActiveRecord::Hooks.instance_variable_set(:@models, @models_value_backup) + Esse::ActiveRecord::Hooks.instance_variable_set(:@models, @models_value_backup) # rubocop:disable RSpec/InstanceVariable + clean_db end describe '.index_callbacks' do - shared_examples 'index document callbacks' do |event| - context "on #{event}" do - let(:document) { double } - - before do - stub_index(:dummies) do - repository :dummy, const: true do - end - end - end + context 'when on :create' do + let(:index_ok_response) { { 'result' => 'indexed' } } - it 'register the model class into Esse::ActiveRecord::Hooks.models' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: [event] - end - expect(Esse::ActiveRecord::Hooks.models).to include(model_class) + it 'register the model class into Esse::ActiveRecord::Hooks.models' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'states_index', on: %i[create] end + expect(Esse::ActiveRecord::Hooks.models).to include(model_class) + end - it 'index the model on create' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: [event] - end - model = model_class.new - expect(DummiesIndex.repo).to receive(:serialize).with(model).and_return(document) - expect(DummiesIndex.repo).to receive(:elasticsearch).and_return(backend_proxy) - expect(backend_proxy).to receive(:index_document).with(document, {}).and_return(:ok) - model.send(event) + it 'index the model on create' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'states_index', on: %i[create] end + model = build_record(model_class, name: 'Illinois', id: SecureRandom.uuid) - it 'index the associated model using the block definition' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: [event] do - association - end + expect(StatesIndex).to receive(:index).and_call_original + expect(StatesIndex).to esse_receive_request(:index).with( + id: model.id, + index: StatesIndex.index_name, + body: {name: 'Illinois'}, + ).and_return(index_ok_response) - protected + model.save + end - def association - :other - end + it 'index the associated model using the block definition' do + model_class = Class.new(County) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[create] do + state end - model = model_class.new - expect(DummiesIndex.repo).to receive(:serialize).with(:other).and_return(document) - expect(DummiesIndex.repo).to receive(:elasticsearch).and_return(backend_proxy) - expect(backend_proxy).to receive(:index_document).with(document, {}).and_return(:ok) - model.send(event) end + state = build_record(State, name: 'Illinois', id: SecureRandom.uuid) + county = build_record(model_class, name: 'Cook', state: state) - it 'does not index when the hooks are globally disabled' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: [event] - end - model = model_class.new + expect(GeographiesIndex).to receive(:index).and_call_original + expect(GeographiesIndex).to esse_receive_request(:index).with( + id: state.id, + index: GeographiesIndex.index_name, + body: {name: 'Illinois', type: 'state'}, + ).and_return(index_ok_response) - Esse::ActiveRecord::Hooks.without_indexing do - expect(DummiesIndex.repo).not_to receive(:serialize) - expect(DummiesIndex.repo).not_to receive(:elasticsearch) - model.send(event) - end + county.save + end + + it 'does not index when the hooks are globally disabled' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[create] end + model = build_record(model_class, name: 'Illinois', id: SecureRandom.uuid) - it 'does not index when the hooks are disabled for the model' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: [event] - end - model = model_class.new - model_class.without_indexing do - expect(DummiesIndex.repo).not_to receive(:serialize) - expect(DummiesIndex.repo).not_to receive(:elasticsearch) - model.send(event) - end + expect(GeographiesIndex).not_to receive(:index) + Esse::ActiveRecord::Hooks.without_indexing do + model.save end + end - it 'allows to select which indices will not execute indexing callbacks' do - stub_index(:others) do - repository(:other, const: true) {} - end + it 'does not index when the hooks are disabled for the model' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[create] + end + model = build_record(model_class, name: 'Illinois', id: SecureRandom.uuid) + expect(GeographiesIndex).not_to receive(:index) + model_class.without_indexing do + model.save + end + end - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies', on: [event] - index_callbacks 'others:other', on: [event] - end - model = model_class.new - model_class.without_indexing(DummiesIndex) do - expect(DummiesIndex::Dummy).not_to receive(:serialize) - expect(DummiesIndex::Dummy).not_to receive(:index_document) - expect(OthersIndex::Other).to receive(:serialize).with(model).and_return(document) - expect(OthersIndex::Other).to receive(:elasticsearch).and_return(backend_proxy) - expect(backend_proxy).to receive(:index_document).with(document, {}).and_return(:ok) - model.send(event) - end + it 'allows to select which indices will not execute indexing callbacks' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'states', on: %i[create] + index_callbacks 'geographies:state', on: %i[create] + end + model = build_record(model_class, name: 'Illinois', id: SecureRandom.uuid) + expect(GeographiesIndex).not_to receive(:index) + expect(StatesIndex).to receive(:index).and_call_original + expect(StatesIndex).to esse_receive_request(:index).with( + id: model.id, + index: StatesIndex.index_name, + body: {name: 'Illinois'}, + ).and_return(index_ok_response) + model_class.without_indexing(GeographiesIndex) do + model.save end end end - include_examples 'index document callbacks', :create - include_examples 'index document callbacks', :update + context 'when on :update' do + let(:index_ok_response) { { 'result' => 'indexed' } } + + it 'register the model class into Esse::ActiveRecord::Hooks.models' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'states_index', on: %i[update] + end + expect(Esse::ActiveRecord::Hooks.models).to include(model_class) + end + + it 'index the model on update' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'states_index', on: %i[update] + end + model = create_record(model_class, name: 'Illinois') + + expect(StatesIndex).to receive(:index).and_call_original + expect(StatesIndex).to esse_receive_request(:index).with( + id: model.id, + index: StatesIndex.index_name, + body: {name: 'Illinois'}, + ).and_return(index_ok_response) - context 'on destroy' do - let(:document) { double } + model.touch + end - before do - stub_index(:dummies) do - repository :dummy, const: true do + it 'index the associated model using the block definition' do + model_class = Class.new(County) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[update] do + state end end + state = create_record(State, name: 'Illinois') + county = create_record(model_class, name: 'Cook', state: state) + + expect(GeographiesIndex).to receive(:index).and_call_original + expect(GeographiesIndex).to esse_receive_request(:index).with( + id: state.id, + index: GeographiesIndex.index_name, + body: {name: 'Illinois', type: 'state'}, + ).and_return(index_ok_response) + + county.touch + end + + it 'does not index when the hooks are globally disabled' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[update] + end + model = create_record(model_class, name: 'Illinois') + + expect(GeographiesIndex).not_to receive(:index) + Esse::ActiveRecord::Hooks.without_indexing do + model.touch + end + end + + it 'does not index when the hooks are disabled for the model' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[update] + end + model = create_record(model_class, name: 'Illinois') + expect(GeographiesIndex).not_to receive(:index) + model_class.without_indexing do + model.touch + end + end + + it 'allows to select which indices will not execute indexing callbacks' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'states', on: %i[update] + index_callbacks 'geographies:state', on: %i[update] + end + model = create_record(model_class, name: 'Illinois') + expect(GeographiesIndex).not_to receive(:index) + expect(StatesIndex).to receive(:index).and_call_original + expect(StatesIndex).to esse_receive_request(:index).with( + id: model.id, + index: StatesIndex.index_name, + body: {name: 'Illinois'}, + ).and_return(index_ok_response) + model_class.without_indexing(GeographiesIndex) do + model.touch + end end + end + + context 'when on destroy' do + let(:delete_ok_response) { { 'result' => 'deleted' } } it 'register the model class into Esse::ActiveRecord::Hooks.models' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: %i[destroy] + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'states', on: %i[destroy] end expect(Esse::ActiveRecord::Hooks.models).to include(model_class) end - it 'index the model on create' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: %i[destroy] + it 'removes the document on destroy' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'states', on: %i[destroy] end - model = model_class.new - expect(DummiesIndex.repo).to receive(:serialize).with(model).and_return(document) - expect(DummiesIndex.repo).to receive(:elasticsearch).and_return(backend_proxy) - expect(backend_proxy).to receive(:delete_document).with(document, {}).and_return(:ok) + model = create_record(model_class, name: 'Illinois') + expect(StatesIndex).to receive(:delete).and_call_original + expect(StatesIndex).to esse_receive_request(:delete).with( + id: model.id, + ).and_return(delete_ok_response) model.destroy end - it 'index the associated model using the block definition' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: %i[destroy] do - association - end - - protected + it 'does not raise error when the document does not exist' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[destroy] + end + model = create_record(model_class, name: 'Illinois') + expect(GeographiesIndex).to receive(:delete).and_call_original + expect(GeographiesIndex).to esse_receive_request(:delete).with( + id: model.id, + ).and_raise_http_status(404, { 'error' => { 'type' => 'not_found' } }) + expect { model.destroy }.not_to raise_error + end - def association - :other + it 'removes the associated model using the block definition' do + model_class = Class.new(County) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[destroy] do + state end end - model = model_class.new - expect(DummiesIndex.repo).to receive(:serialize).with(:other).and_return(document) - expect(DummiesIndex.repo).to receive(:elasticsearch).and_return(backend_proxy) - expect(backend_proxy).to receive(:delete_document).with(document, {}).and_return(:ok) - model.destroy + state = create_record(State, name: 'Illinois') + county = create_record(model_class, name: 'Cook', state: state) + expect(GeographiesIndex).to receive(:delete).and_call_original + expect(GeographiesIndex).to esse_receive_request(:delete).with( + id: state.id, + ).and_return(delete_ok_response) + county.destroy end - it 'does not index when the hooks are globally disabled' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: %i[destroy] + it 'does not perform delete request when the hooks are globally disabled' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[destroy] end - model = model_class.new + model = create_record(model_class, name: 'Illinois') + expect(GeographiesIndex).not_to receive(:delete) Esse::ActiveRecord::Hooks.without_indexing do - expect(DummiesIndex.repo).not_to receive(:serialize) - expect(DummiesIndex.repo).not_to receive(:elasticsearch) model.destroy end end - it 'does not index when the hooks are disabled for the model' do - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies_index', on: %i[destroy] + it 'does not perform delete request when the hooks are disabled for the model' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'geographies:state', on: %i[destroy] end - model = model_class.new + model = create_record(model_class, name: 'Illinois') + + expect(GeographiesIndex).not_to receive(:delete) model_class.without_indexing do - expect(DummiesIndex.repo).not_to receive(:serialize) - expect(DummiesIndex.repo).not_to receive(:elasticsearch) model.destroy end end - it 'allows to select which indices will not execute indexing callbacks' do - stub_index(:others) do - repository(:other, const: true) {} + it 'allows to select which indices will NOT perform :delete request during callbacks' do + model_class = Class.new(State) do + include Esse::ActiveRecord::Model + index_callbacks 'states:state', on: %i[destroy] + index_callbacks 'geographies:state', on: %i[destroy] end + model = create_record(model_class, name: 'Illinois') - model_class = Class.new(DummyIndexableModel) do - index_callbacks 'dummies:dummy', on: %i[destroy] - index_callbacks 'others:other', on: %i[destroy] - end - model = model_class.new - model_class.without_indexing(DummiesIndex) do - expect(DummiesIndex::Dummy).not_to receive(:serialize) - expect(DummiesIndex::Dummy).not_to receive(:delete_document) - expect(OthersIndex::Other).to receive(:serialize).with(model).and_return(document) - expect(OthersIndex::Other).to receive(:elasticsearch).and_return(backend_proxy) - expect(backend_proxy).to receive(:delete_document).with(document, {}).and_return(:ok) + expect(GeographiesIndex).not_to receive(:delete) + expect(StatesIndex).to receive(:delete).and_call_original + expect(StatesIndex).to esse_receive_request(:delete).with( + id: model.id, + ).and_return(delete_ok_response) + + model_class.without_indexing(GeographiesIndex) do model.destroy end end diff --git a/spec/esse/active_record_spec.rb b/spec/esse/active_record_spec.rb index 0f10e98..bfeacba 100644 --- a/spec/esse/active_record_spec.rb +++ b/spec/esse/active_record_spec.rb @@ -4,6 +4,6 @@ RSpec.describe Esse::ActiveRecord do it 'has a version number' do - expect(Esse::ActiveRecord::VERSION).not_to be nil + expect(Esse::ActiveRecord::VERSION).not_to be_nil end end diff --git a/spec/esse/plugin/active_record/collection_definition_spec.rb b/spec/esse/plugin/active_record/collection_definition_spec.rb index 2e34860..ecfb02a 100644 --- a/spec/esse/plugin/active_record/collection_definition_spec.rb +++ b/spec/esse/plugin/active_record/collection_definition_spec.rb @@ -7,10 +7,12 @@ context 'when the type is a model class' do it 'define collection with only class name without raise an error' do expect { - stub_index(:animals) do + stub_esse_index(:animals) do plugin :active_record - collection Animal + repository :animal do + collection Animal + end end }.not_to raise_error expect(AnimalsIndex.repo.instance_variable_get(:@collection_proc)).to be < Esse::ActiveRecord::Collection @@ -19,7 +21,7 @@ it 'define collection with name and class name without raise an error' do expect { - stub_index(:animals) do + stub_esse_index(:animals) do plugin :active_record repository :cat do @@ -33,10 +35,12 @@ it 'define collection with an activerecord relation' do expect { - stub_index(:animals) do + stub_esse_index(:animals) do plugin :active_record - collection Animal.all + repository :animal do + collection Animal.all + end end }.not_to raise_error expect(AnimalsIndex.repo.instance_variable_get(:@collection_proc)).to be < Esse::ActiveRecord::Collection @@ -45,10 +49,12 @@ it 'define a collection with custom batch_size' do expect { - stub_index(:animals) do + stub_esse_index(:animals) do plugin :active_record - collection(Animal, batch_size: 10) + repository :animal do + collection(Animal, batch_size: 10) + end end }.not_to raise_error expect(col = AnimalsIndex.repo.instance_variable_get(:@collection_proc)).to be < Esse::ActiveRecord::Collection @@ -57,12 +63,14 @@ it 'evaluates the block in the Esse::ActiveRecord::Collection' do expect { - stub_index(:animals) do + stub_esse_index(:animals) do plugin :active_record - collection Animal do - def self.foo - 'bar' + repository :animal do + collection Animal do + def self.foo + 'bar' + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d20c0e9..523cd17 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,8 +3,8 @@ require 'bundler/setup' require 'dotenv/load' require 'esse/active_record' +require 'esse/rspec' -require 'support/class_helpers' require 'support/config_helpers' require 'support/webmock' require 'support/models' @@ -16,6 +16,5 @@ config.expect_with :rspec do |c| c.syntax = :expect end - config.include ClassHelpers config.include ConfigHelpers end diff --git a/spec/support/class_helpers.rb b/spec/support/class_helpers.rb deleted file mode 100755 index de97611..0000000 --- a/spec/support/class_helpers.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -# Helper methods to create Index classes -module ClassHelpers - def stub_index(name, superclass = nil, &block) - superclass ||= Esse::Index - klass_name = "#{Esse::Hstring.new(name).camelize}Index" - klass = stub_class(klass_name, superclass) - klass.class_eval(&block) if block - klass.define_singleton_method(:index_directory) do - Esse.config.indices_directory.join(Esse::Hstring.new(klass_name).underscore.to_s).to_s - end - klass - end - - def stub_class(name, superclass = nil, &block) - klass = Class.new(superclass || Object, &block) - stub_const(Esse::Hstring.new(name).camelize.to_s, klass) - end -end diff --git a/spec/support/config_helpers.rb b/spec/support/config_helpers.rb index 9f14456..78680d1 100644 --- a/spec/support/config_helpers.rb +++ b/spec/support/config_helpers.rb @@ -29,6 +29,15 @@ def with_config(opts = {}) reset_config! end + def stub_cluster_info(distribution: 'elasticsearch', version: '7.11.0') + Esse.config.cluster_ids.each do |id| + Esse.config.cluster(id).instance_variable_set(:@info, { + distribution: distribution, + version: version, + }) + end + end + def with_cluster_config(id: :default, **opts) with_config { |c| c.cluster(id).assign(opts) } end diff --git a/spec/support/models.rb b/spec/support/models.rb index 5811205..1edb548 100644 --- a/spec/support/models.rb +++ b/spec/support/models.rb @@ -16,12 +16,14 @@ t.column :name, :string t.column :abbr_name, :string t.column :iso_country, :string # ISO 3166-1 country/territory code + t.timestamps end create_table :counties do |t| t.column :name, :string t.column :iso_country, :string # ISO 3166-1 country/territory code t.column :state_id, :integer + t.timestamps end end @@ -48,6 +50,12 @@ def create_record(klass, **attrs) klass.create!(attrs) end +def build_record(klass, **attrs) + @tables ||= [] + @tables |= [klass.table_name] + klass.new(attrs) +end + def clean_db (@tables || []).each do |table| ActiveRecord::Base.connection.execute("DELETE FROM #{table}")