Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 6 additions & 28 deletions .github/workflows/base_benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,13 @@ jobs:
checks: write
strategy:
matrix:
include:
- ruby: '2.7'
gemfile: '5.2.7'
couchbase: '7.1.0'
- ruby: '2.7'
gemfile: '6.0.0'
couchbase: '7.1.0'
- ruby: '2.7'
gemfile: '6.0.0'
couchbase: '7.6.3'
- ruby: '2.7'
gemfile: '7.0.0'
couchbase: '7.1.0'
- ruby: '2.7'
gemfile: '7.0.0'
couchbase: '7.6.3'
# ruby 3.0 minimimun required rails 6.0.3
# - ruby: '3.0'
# gemfile: '5.2.7'
# couchbase: '7.1.0'
- ruby: '3.0'
ruby: ['3.0', '3.1', '3.2', '3.3']
gemfile: ['6.1.7.7', '7.0.0', '7.1.0']
couchbase: ['7.2.0', '7.6.3', '8.0.0']
exclude:
# Ruby 3.3 doesn't support Rails 6.1
- ruby: '3.3'
gemfile: '6.1.7.7'
couchbase: '7.1.0'
- ruby: '3.0'
gemfile: '7.0.0'
couchbase: '7.1.0'
- ruby: '3.0'
gemfile: '7.0.0'
couchbase: '7.6.3'
fail-fast: false
runs-on: ubuntu-22.04
name: Base Benchmark ${{ matrix.ruby }} rails-${{ matrix.gemfile }} couchbase-${{ matrix.couchbase }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ jobs:
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
ruby-version: 2.7
ruby-version: 3.0
- name: Run rubocop
run: bundle exec rubocop
34 changes: 6 additions & 28 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,13 @@ jobs:
test:
strategy:
matrix:
include:
- ruby: '2.7'
gemfile: '5.2.7'
couchbase: '7.1.0'
- ruby: '2.7'
gemfile: '6.0.0'
couchbase: '7.1.0'
- ruby: '2.7'
gemfile: '6.0.0'
couchbase: '7.6.3'
- ruby: '2.7'
gemfile: '7.0.0'
couchbase: '7.1.0'
- ruby: '2.7'
gemfile: '7.0.0'
couchbase: '7.6.3'
# ruby 3.0 minimimun required rails 6.0.3
# - ruby: '3.0'
# gemfile: '5.2.7'
# couchbase: '7.1.0'
- ruby: '3.0'
ruby: ['3.0', '3.1', '3.2', '3.3']
gemfile: ['6.1.7.7', '7.0.0', '7.1.0']
couchbase: ['7.2.0', '7.6.3', '8.0.0']
exclude:
# Ruby 3.3 doesn't support Rails 6.1
- ruby: '3.3'
gemfile: '6.1.7.7'
couchbase: '7.1.0'
- ruby: '3.0'
gemfile: '7.0.0'
couchbase: '7.1.0'
- ruby: '3.0'
gemfile: '7.0.0'
couchbase: '7.6.3'
fail-fast: false
runs-on: ubuntu-22.04
name: ${{ matrix.ruby }} rails-${{ matrix.gemfile }} couchbase-${{ matrix.couchbase }}
Expand Down
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require:
- rubocop-rake

AllCops:
TargetRubyVersion: 2.7
TargetRubyVersion: 3.0
CacheRootDirectory: rubocop_cache
Exclude:
- 'tmp/**/*'
Expand Down
5 changes: 3 additions & 2 deletions ci/run_couchbase.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ sudo service couchbase-server status
/opt/couchbase/bin/couchbase-cli cluster-init -c 127.0.0.1:8091 --cluster-username=admin --cluster-password=password --cluster-ramsize=320 --cluster-index-ramsize=256 --cluster-fts-ramsize=256 --services=data,index,query,fts
sleep 5
/opt/couchbase/bin/couchbase-cli server-info -c 127.0.0.1:8091 -u admin -p password
/opt/couchbase/bin/couchbase-cli bucket-create -c 127.0.0.1:8091 -u admin -p password --bucket=$BUCKET --bucket-type=couchbase --bucket-ramsize=160 --bucket-replica=0 --enable-flush=1 --wait
sleep 1
/opt/couchbase/bin/couchbase-cli bucket-create -c 127.0.0.1:8091 -u admin -p password --bucket=$BUCKET --bucket-type=couchbase --bucket-ramsize=160 --bucket-replica=0 --enable-flush=1 --storage-backend couchstore --wait
sleep 3
/opt/couchbase/bin/couchbase-cli user-manage -c 127.0.0.1:8091 -u admin -p password --set --rbac-username $USER --rbac-password $PASSWORD --rbac-name "Auto Tester" --roles admin --auth-domain local
sleep 2
curl http://admin:password@localhost:8093/query/service -d "statement=CREATE INDEX \`default_type\` ON \`$BUCKET\`(\`type\`)"
curl http://admin:password@localhost:8093/query/service -d "statement=CREATE INDEX \`default_rating\` ON \`$BUCKET\`(\`rating\`)"
curl http://admin:password@localhost:8093/query/service -d "statement=CREATE INDEX \`default_name\` ON \`$BUCKET\`(\`name\`)"
10 changes: 5 additions & 5 deletions couchbase-orm.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ Gem::Specification.new do |gem|
gem.summary = 'Couchbase ORM for Rails'
gem.description = 'A Couchbase ORM for Rails'

gem.required_ruby_version = '>= 2.7.0'
gem.required_ruby_version = '>= 3.0'
gem.require_paths = ['lib']

gem.add_runtime_dependency 'activemodel', ENV['ACTIVE_MODEL_VERSION'] || '>= 5.2', '< 7.1'
gem.add_runtime_dependency 'activerecord', ENV['ACTIVE_MODEL_VERSION'] || '>= 5.2', '< 7.1'
gem.add_runtime_dependency 'activemodel', ENV['ACTIVE_MODEL_VERSION'] || '>= 6.1.7.7', '<= 7.1'
gem.add_runtime_dependency 'activerecord', ENV['ACTIVE_MODEL_VERSION'] || '>= 6.1.7.7', '<= 7.1'

gem.add_runtime_dependency 'couchbase', '~> 3.3.0'
gem.add_runtime_dependency 'couchbase', '~> 3.4.5'
gem.add_runtime_dependency 'radix', '~> 2.2' # converting numbers to and from any base

gem.add_development_dependency 'actionpack', ENV['ACTIVE_MODEL_VERSION'] || '>= 5.2', '< 7.1'
gem.add_development_dependency 'actionpack', ENV['ACTIVE_MODEL_VERSION'] || '>= 6.1.7.7', '<= 7.1'
gem.add_development_dependency 'base64'
gem.add_development_dependency 'mapotempo_rubocop', '<1.0'
gem.add_development_dependency 'pry'
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: '3.7'

x-app-args: &app-args
BUNDLE_VERSION: ${BUNDLE_VERSION:-2.4.22}
RUBY_VERSION: ${RUBY_VERSION:-2.7-slim-bullseye}
RUBY_VERSION: ${RUBY_VERSION:-3.0-slim-bullseye}
BUNDLE_WITHOUT: production

services:
Expand All @@ -12,7 +12,7 @@ services:
<<: *app-args
context: .
dockerfile: Dockerfile
image: dev.example.com/mapotempo/couchbase-orm:ruby-${RUBY_VERSION:-2.7-slim-bullseye}_bundle-${BUNDLE_VERSION:-2.4.22}
image: dev.example.com/mapotempo/couchbase-orm:ruby-${RUBY_VERSION:-3.0-slim-bullseye}_bundle-${BUNDLE_VERSION:-2.4.22}
volumes:
- ./:/srv/app/
- app_cache_vendor:/srv/app/vendor
Expand All @@ -21,7 +21,7 @@ services:
- COUCHBASE_USER=tester
- COUCHBASE_PASSWORD=password123
- COUCHBASE_BUCKET=default
- ACTIVE_MODEL_VERSION=5.2.7
- ACTIVE_MODEL_VERSION=6.1.7.7
- LOG_LEVEL=debug
tty: true
depends_on:
Expand Down
46 changes: 46 additions & 0 deletions lib/couchbase-orm/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ def table_exists?
true
end

# Stub connection for ActiveRecord::Timestamp compatibility
def connection
@connection ||= Struct.new(:default_timezone).new(:utc)
end

# ActiveRecord 7.1 compatibility
def composite_primary_key?
false
end

def _reflect_on_association(_attribute)
false
end
Expand All @@ -85,12 +95,30 @@ def attribute_names
attribute_types.keys
end
end

# Rails 7.1+ renamed generate_alias_attributes to generate_alias_attribute_methods
# ActiveRecord still calls the old method name, so we need to provide compatibility
# The old method had no parameters, so we just provide an empty implementation
if ActiveModel::VERSION::MAJOR >= 7 && ActiveModel::VERSION::MINOR >= 1
def generate_alias_attributes(*args)
# In Rails 7.1+, this method was renamed and signature changed
# ActiveRecord 7.1 still calls it with no args, so we handle that case
return if args.empty?

generate_alias_attribute_methods(*args)
end
end
end

def _has_attribute?(attr_name)
attribute_names.include?(attr_name.to_s)
end

# ActiveRecord 7.1 compatibility
def primary_key_values_present?
!id.nil?
end

def attribute_for_inspect(attr_name)
value = send(attr_name)
value.inspect
Expand Down Expand Up @@ -136,6 +164,17 @@ def read_attribute(attr_name, &block)
end

class Document
class << self
def descendants
@__descendants ||= [] # rubocop:disable Naming/MemoizedInstanceVariableName
end

def inherited(subclass)
super
descendants << subclass
end
end

include ::ActiveModel::Model
include ::ActiveModel::Dirty
include ::ActiveModel::Attributes
Expand All @@ -154,6 +193,9 @@ class Document
define_model_callbacks :initialize, :find, only: :after
define_model_callbacks :create, :destroy, :save, :update

# Prevent duplicate validation errors (similar to ActiveRecord::AutosaveAssociation)
after_validation :_ensure_no_duplicate_errors

Metadata = Struct.new(:cas)

class MismatchTypeError < RuntimeError; end
Expand Down Expand Up @@ -218,6 +260,10 @@ def write_attribute(attr_name, value)

@attributes.write_from_user(name, value)
end

def _ensure_no_duplicate_errors
errors.uniq!
end
end

class NestedDocument < Document
Expand Down
19 changes: 18 additions & 1 deletion lib/couchbase-orm/views.rb
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,24 @@ def ensure_design_document!
document = Couchbase::Management::DesignDocument.new
document.views = views_actual
document.name = @design_document
bucket.view_indexes.upsert_design_document(document, :production)

# Retry logic for view document creation (handles race conditions and storage backend issues)
max_retries = 3
retry_count = 0
begin
bucket.view_indexes.upsert_design_document(document, :production)
rescue Couchbase::Error::DesignDocumentNotFound, Couchbase::Error::InternalServerFailure => e
retry_count += 1
if retry_count <= max_retries
sleep_time = retry_count * 0.5 # exponential backoff: 0.5s, 1s, 1.5s
CouchbaseOrm.logger.warn("View document upsert failed (attempt #{retry_count}/#{max_retries}), retrying in #{sleep_time}s: #{e.message}")
sleep(sleep_time)
retry
else
CouchbaseOrm.logger.error("View document upsert failed after #{max_retries} retries: #{e.message}")
raise
end
end

true
else
Expand Down
Loading