activerecord-typedstore 1.4.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17f01f8701f6af429d7fb15bfbcf9cf202f1d82cbe0f6878aa4a503ed69fc0e8
4
- data.tar.gz: e5e1dd4100a034b074fe2682e485ae1365c8bcbe9c4bb7bc96f697a5042c0f62
3
+ metadata.gz: 5f56fb7b6456937890a85e139a296727a04b2c87a2452f45810782849378357a
4
+ data.tar.gz: 17e694f93e4db21a4b5a08c7795285fd10aaf9bc203f74376a14356873c84802
5
5
  SHA512:
6
- metadata.gz: f1703f9023ab91ac749d74335276a0824d3072060eac8a68270f2b52e41198dfeebecffd54738cd4b097fe97948371479e2db1c1fa8ebc30ca07eeeaf63bac91
7
- data.tar.gz: 8e48b1f137a374053092ccf52f3411759fcf421d1e4d8cf8b7f47b3c25d935aaf958f568ee35423381171aca879bf68a7b943493cf4947a8d3af19f66d54616f
6
+ metadata.gz: 02d3ce60427903e2c039818c4c77368a42b20c4f9fbf5bf167357e0e932d01c717f5eea6172ea03170b018a468b6b8f0a00d59fd49073a38eb620a27e05870c3
7
+ data.tar.gz: 96ff55688ac6c1b2d9f56d9a45e796b4c633e6d3d1dd89cdbdb14025044d97c9f5deefce7a53494f35ff5a227f340e8ec9d9fba70b7d861fe488ee6f52ad98fe
@@ -5,43 +5,26 @@ on: [push, pull_request]
5
5
  jobs:
6
6
  build:
7
7
  runs-on: ubuntu-latest
8
- name: Ruby ${{ matrix.ruby }}
8
+ name: ${{ matrix.ruby }} / Rails ${{ matrix.rails }} / TZ ${{ matrix.timezone_aware }}
9
9
  strategy:
10
+ fail-fast: false
10
11
  matrix:
11
- ruby: [2.6, 2.7]
12
- gemfile: [Gemfile.ar-5.2, Gemfile.ar-6.0, Gemfile.ar-6.1, Gemfile.ar-master]
13
- exclude:
14
- - ruby: 2.6
15
- gemfile: Gemfile.ar-6.0
16
- - ruby: 2.6
17
- gemfile: Gemfile.ar-6.1
18
- - ruby: 2.6
19
- gemfile: Gemfile.ar-master
12
+ ruby: ['2.7', '3.0', '3.1']
13
+ rails: ['6.1', '7.0', 'edge']
20
14
  timezone_aware: [0, 1]
21
15
  env:
22
- BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}
16
+ BUNDLE_GEMFILE: gemfiles/Gemfile.ar-${{ matrix.rails }}
23
17
  TIMEZONE_AWARE: ${{ matrix.timezone_aware }}
24
18
  POSTGRES: 1
25
19
  MYSQL: 1
26
20
  POSTGRES_JSON: 1
27
21
  steps:
28
- - name: Check out code
29
- uses: actions/checkout@v2
30
- - name: Set up Ruby ${{ matrix.ruby }}
31
- uses: ruby/setup-ruby@v1
32
- with:
33
- ruby-version: ${{ matrix.ruby }}
34
- bundler-cache: true
35
- - name: Start MySQL and create DB
36
- run: |
37
- sudo systemctl start mysql.service
38
- mysql -uroot -proot -e 'create database typed_store_test;'
39
- - name: Start PostgresQL and create DB
40
- run: |
41
- sudo sed -i s/md5/trust/g /etc/postgresql/*/main/pg_hba.conf
42
- sudo systemctl start postgresql.service
43
- sudo -u postgres createuser --createdb --superuser ${USER}
44
- createdb typed_store_test
45
- echo "POSTGRES_URL=postgres://localhost/typed_store_test" >> $GITHUB_ENV
46
- - name: Ruby Tests
47
- run: bundle exec rake
22
+ - name: Check out code
23
+ uses: actions/checkout@v3
24
+ - name: Set up Ruby ${{ matrix.ruby }}
25
+ uses: ruby/setup-ruby@v1
26
+ with:
27
+ ruby-version: ${{ matrix.ruby }}
28
+ bundler-cache: true
29
+ - name: Ruby Tests
30
+ run: bundle exec rake
data/README.md CHANGED
@@ -1,8 +1,5 @@
1
1
  # ActiveRecord::TypedStore
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/byroot/activerecord-typedstore.png)](http://travis-ci.org/byroot/activerecord-typedstore)
4
- [![Code Climate](https://codeclimate.com/github/byroot/activerecord-typedstore.png)](https://codeclimate.com/github/byroot/activerecord-typedstore)
5
- [![Coverage Status](https://coveralls.io/repos/byroot/activerecord-typedstore/badge.png)](https://coveralls.io/r/byroot/activerecord-typedstore)
6
3
  [![Gem Version](https://badge.fury.io/rb/activerecord-typedstore.png)](http://badge.fury.io/rb/activerecord-typedstore)
7
4
 
8
5
  [ActiveRecord::Store](http://api.rubyonrails.org/classes/ActiveRecord/Store.html) but with typed attributes.
@@ -79,6 +76,12 @@ shop.save
79
76
  shop.reload
80
77
  shop.settings[:unknown] # => 'Hello World'
81
78
 
79
+ # You can group attributes with a prefix or suffix
80
+ typed_store(:browser, prefix: true) { |s| s.string :ip } # => #browser_ip
81
+ typed_store(:browser, prefix: :web) { |s| s.string :ip } # => #web_ip
82
+ typed_store(:browser, suffix: true) { |s| s.string :ip } # => #ip_browser
83
+ typed_store(:browser, suffix: :web) { |s| s.string :ip } # => #ip_web
84
+
82
85
  # If you only want type casting and default handling without accessors
83
86
 
84
87
  # you can disable them store wide
@@ -18,14 +18,11 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'activerecord', '>= 5.2'
21
+ spec.add_dependency 'activerecord', '>= 6.1'
22
22
 
23
23
  spec.add_development_dependency 'bundler'
24
- spec.add_development_dependency 'rake', '~> 10'
24
+ spec.add_development_dependency 'rake'
25
25
  spec.add_development_dependency 'rspec', '~> 3'
26
- spec.add_development_dependency 'coveralls', '~> 0'
27
26
  spec.add_development_dependency 'sqlite3', '~> 1'
28
- spec.add_development_dependency 'pg', ENV.fetch('PG_VERSION', '~> 0.18')
29
- spec.add_development_dependency 'mysql2', '> 0.3'
30
27
  spec.add_development_dependency 'database_cleaner', '~> 1'
31
28
  end
@@ -1,6 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- ENV['PG_VERSION'] = '~> 1.1'
4
3
  gemspec path: '..'
5
4
 
6
5
  gem 'activerecord', '~> 6.1.0'
@@ -2,4 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec path: '..'
4
4
 
5
- gem 'activerecord', '~> 5.2.0'
5
+ gem 'activerecord', '~> 7.0.0'
@@ -1,6 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- ENV['PG_VERSION'] = '~> 1.1'
4
3
  gemspec path: '..'
5
4
 
6
5
  gem 'activerecord', github: 'rails/rails'
@@ -71,5 +71,24 @@ module ActiveRecord::TypedStore
71
71
  super
72
72
  end
73
73
  end
74
+
75
+ private
76
+
77
+ def attribute_names_for_partial_inserts
78
+ # Contrary to all vanilla Rails types, typedstore attribute have an inherent default
79
+ # value that doesn't match the database column default.
80
+ # As such we need to insert them on partial inserts even if they weren't changed.
81
+ super | self.class.typed_stores.keys.map(&:to_s)
82
+ end
83
+
84
+ def attribute_names_for_partial_updates
85
+ # On partial updates we shouldn't need to force stores to be persisted. However since
86
+ # we weren't persisting them for a while on insertion, we now need to gracefully deal
87
+ # with existing records that may have been persisted with a `NULL` store
88
+ # We use `blank?` as an heuristic to detect these.
89
+ super | self.class.typed_stores.keys.map(&:to_s).select do |store|
90
+ @attributes.key?(store) && @attributes[store].value_before_type_cast.blank?
91
+ end
92
+ end
74
93
  end
75
94
  end
@@ -6,10 +6,38 @@ module ActiveRecord::TypedStore
6
6
  class DSL
7
7
  attr_reader :fields, :coder
8
8
 
9
- def initialize(attribute_name, options)
10
- @coder = options.fetch(:coder) { default_coder(attribute_name) }
11
- @accessors = options[:accessors]
12
- @accessors = [] if options[:accessors] == false
9
+ def initialize(store_name, options)
10
+ @coder = options.fetch(:coder) { default_coder(store_name) }
11
+ @store_name = store_name
12
+ @prefix =
13
+ case options[:prefix]
14
+ when String, Symbol
15
+ "#{options[:prefix]}_"
16
+ when true
17
+ "#{store_name}_"
18
+ when false, nil
19
+ ""
20
+ else
21
+ raise ArgumentError, "Unexpected type for prefix option. Expected string, symbol, or boolean"
22
+ end
23
+ @suffix =
24
+ case options[:suffix]
25
+ when String, Symbol
26
+ "_#{options[:suffix]}"
27
+ when true
28
+ "_#{store_name}"
29
+ when false, nil
30
+ ""
31
+ else
32
+ raise ArgumentError, "Unexpected type for suffix option. Expected string, symbol, or boolean"
33
+ end
34
+ @accessors = if options[:accessors] == false
35
+ {}
36
+ elsif options[:accessors].is_a?(Array)
37
+ options[:accessors].each_with_object({}) do |accessor_name, hash|
38
+ hash[accessor_name] = accessor_key_for(accessor_name)
39
+ end
40
+ end
13
41
  @fields = {}
14
42
  yield self
15
43
  end
@@ -25,7 +53,9 @@ module ActiveRecord::TypedStore
25
53
  end
26
54
 
27
55
  def accessors
28
- @accessors || @fields.values.select(&:accessor).map(&:name)
56
+ @accessors || @fields.values.select(&:accessor).each_with_object({}) do |field, hash|
57
+ hash[field.name] = accessor_key_for(field.name)
58
+ end
29
59
  end
30
60
 
31
61
  delegate :keys, to: :@fields
@@ -37,5 +67,11 @@ module ActiveRecord::TypedStore
37
67
  end
38
68
  end
39
69
  alias_method :date_time, :datetime
70
+
71
+ private
72
+
73
+ def accessor_key_for(name)
74
+ "#{@prefix}#{name}#{@suffix}"
75
+ end
40
76
  end
41
77
  end
@@ -14,9 +14,10 @@ module ActiveRecord::TypedStore
14
14
  class_attribute :typed_stores, :store_accessors, instance_accessor: false
15
15
  end
16
16
 
17
+ store_options = options.slice(:prefix, :suffix)
17
18
  dsl = DSL.new(store_attribute, options, &block)
18
19
  self.typed_stores = (self.typed_stores || {}).merge(store_attribute => dsl)
19
- self.store_accessors = typed_stores.each_value.flat_map(&:accessors).map { |a| -a.to_s }.to_set
20
+ self.store_accessors = typed_stores.each_value.flat_map { |d| d.accessors.values }.map { |a| -a.to_s }.to_set
20
21
 
21
22
  typed_klass = TypedHash.create(dsl.fields.values)
22
23
  const_set("#{store_attribute}_hash".camelize, typed_klass)
@@ -31,20 +32,20 @@ module ActiveRecord::TypedStore
31
32
  Type.new(typed_klass, dsl.coder, subtype)
32
33
  end
33
34
  end
34
- store_accessor(store_attribute, dsl.accessors)
35
+ store_accessor(store_attribute, dsl.accessors.keys, **store_options)
35
36
 
36
- dsl.accessors.each do |accessor_name|
37
- define_method("#{accessor_name}_changed?") do
37
+ dsl.accessors.each do |accessor_name, accessor_key|
38
+ define_method("#{accessor_key}_changed?") do
38
39
  send("#{store_attribute}_changed?") &&
39
40
  send(store_attribute)[accessor_name] != send("#{store_attribute}_was")[accessor_name]
40
41
  end
41
42
 
42
- define_method("#{accessor_name}_was") do
43
+ define_method("#{accessor_key}_was") do
43
44
  send("#{store_attribute}_was")[accessor_name]
44
45
  end
45
46
 
46
- define_method("restore_#{accessor_name}!") do
47
- send("#{accessor_name}=", send("#{accessor_name}_was"))
47
+ define_method("restore_#{accessor_key}!") do
48
+ send("#{accessor_key}=", send("#{accessor_name}_was"))
48
49
  end
49
50
  end
50
51
  end
@@ -41,12 +41,7 @@ module ActiveRecord::TypedStore
41
41
 
42
42
  def changed_in_place?(raw_old_value, value)
43
43
  return false if value.nil?
44
- if ActiveRecord.version.segments.first >= 5
45
- raw_new_value = serialize(value)
46
- else
47
- # 4.2 capability
48
- raw_new_value = type_cast_for_database(value)
49
- end
44
+ raw_new_value = serialize(value)
50
45
  raw_old_value.nil? != raw_new_value.nil? || raw_old_value != raw_new_value
51
46
  end
52
47
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module TypedStore
5
- VERSION = '1.4.0'
5
+ VERSION = '1.5.1'
6
6
  end
7
7
  end
@@ -696,6 +696,82 @@ shared_examples 'a store' do |retain_type = true, settings_type = :text|
696
696
 
697
697
  end
698
698
 
699
+ describe 'with prefix true' do
700
+
701
+ it 'defines prefixed accessors' do
702
+ expect(model).to respond_to :prefixed_settings_language
703
+ expect(model).to respond_to :prefixed_settings_language=
704
+ end
705
+
706
+ it 'does not define unprefixed accessors' do
707
+ expect(model).not_to respond_to :language
708
+ expect(model).not_to respond_to :language=
709
+ end
710
+
711
+ it 'can be updated' do
712
+ model.update(prefixed_settings_language: 'en')
713
+ expect(model.reload.prefixed_settings_language).to be == 'en'
714
+ end
715
+
716
+ end
717
+
718
+ describe 'with custom prefix' do
719
+
720
+ it 'defines prefixed accessors' do
721
+ expect(model).to respond_to :custom_language
722
+ expect(model).to respond_to :custom_language=
723
+ end
724
+
725
+ it 'does not define unprefixed accessors' do
726
+ expect(model).not_to respond_to :language
727
+ expect(model).not_to respond_to :language=
728
+ end
729
+
730
+ it 'can be updated' do
731
+ model.update(custom_language: 'en')
732
+ expect(model.reload.custom_language).to be == 'en'
733
+ end
734
+
735
+ end
736
+
737
+ describe 'with suffix true' do
738
+
739
+ it 'defines suffixed accessors' do
740
+ expect(model).to respond_to :language_suffixed_settings
741
+ expect(model).to respond_to :language_suffixed_settings=
742
+ end
743
+
744
+ it 'does not define unprefixed accessors' do
745
+ expect(model).not_to respond_to :language
746
+ expect(model).not_to respond_to :language=
747
+ end
748
+
749
+ it 'can be updated' do
750
+ model.update(language_suffixed_settings: 'en')
751
+ expect(model.reload.language_suffixed_settings).to be == 'en'
752
+ end
753
+
754
+ end
755
+
756
+ describe 'with custom suffix' do
757
+
758
+ it 'defines suffixed accessors' do
759
+ expect(model).to respond_to :language_custom
760
+ expect(model).to respond_to :language_custom=
761
+ end
762
+
763
+ it 'does not define unprefixed accessors' do
764
+ expect(model).not_to respond_to :language
765
+ expect(model).not_to respond_to :language=
766
+ end
767
+
768
+ it 'can be updated' do
769
+ model.update(language_custom: 'en')
770
+ expect(model.reload.language_custom).to be == 'en'
771
+ end
772
+
773
+ end
774
+
699
775
  describe '`any` attributes' do
700
776
 
701
777
  it 'accept any type' do
@@ -849,7 +925,7 @@ describe YamlTypedStoreModel do
849
925
 
850
926
  it 'nested hashes are not serialized as HashWithIndifferentAccess' do
851
927
  model = described_class.create!
852
- expect(model.settings_before_type_cast).not_to include('HashWithIndifferentAccess')
928
+ expect(model.settings_before_type_cast.to_s).not_to include('HashWithIndifferentAccess')
853
929
  end
854
930
  end
855
931
 
@@ -878,3 +954,23 @@ describe InheritedTypedStoreModel do
878
954
  expect(model.settings[:new_attribute]).to be == '42'
879
955
  end
880
956
  end
957
+
958
+ describe DirtyTrackingModel do
959
+ it 'stores the default on creation' do
960
+ model = DirtyTrackingModel.create!
961
+ expect(model.settings_before_type_cast).to_not be_blank
962
+ end
963
+
964
+ it 'handles loaded records having uninitialized defaults' do
965
+ model = DirtyTrackingModel.create!
966
+ DirtyTrackingModel.update_all("settings = NULL") # bypass validation
967
+ model = DirtyTrackingModel.find(model.id)
968
+ expect(model.settings_changed?).to be false
969
+ expect(model.changes).to be_empty
970
+
971
+ model.update!(title: "Hello")
972
+
973
+ expect(model.settings_changed?).to be false
974
+ expect(model.changes).to be_empty
975
+ end
976
+ end
data/spec/spec_helper.rb CHANGED
@@ -2,21 +2,18 @@ lib = File.expand_path('../lib', __FILE__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  require 'database_cleaner'
5
-
6
- require 'simplecov'
7
- require 'coveralls'
8
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
9
- SimpleCov::Formatter::HTMLFormatter,
10
- Coveralls::SimpleCov::Formatter
11
- ])
12
- SimpleCov.start
13
-
14
5
  require 'activerecord-typedstore'
15
6
 
16
7
  Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '**', '*.rb'))].each { |f| require f }
17
8
 
18
9
  Time.zone = 'UTC'
19
10
 
11
+ if ActiveRecord.respond_to?(:yaml_column_permitted_classes)
12
+ ActiveRecord.yaml_column_permitted_classes |= ['Date', 'Time', 'BigDecimal']
13
+ elsif ActiveRecord::Base.respond_to?(:yaml_column_permitted_classes)
14
+ ActiveRecord::Base.yaml_column_permitted_classes |= ['Date', 'Time', 'BigDecimal']
15
+ end
16
+
20
17
  RSpec.configure do |config|
21
18
  config.order = 'random'
22
19
  end
@@ -1,15 +1,19 @@
1
1
  require 'active_record'
2
+ require 'base64'
2
3
  require 'json'
3
4
  require 'yaml'
4
5
 
6
+ ENV["RAILS_ENV"] = "test"
7
+
5
8
  ActiveRecord::Base.time_zone_aware_attributes = ENV['TIMEZONE_AWARE'] != '0'
9
+ credentials = { 'database' => 'typed_store_test', 'username' => 'typed_store', 'password' => 'typed_store' }
6
10
  ActiveRecord::Base.configurations = {
7
- 'test_sqlite3' => { 'adapter' => 'sqlite3', 'database' => '/tmp/typed_store.db' },
8
- 'test_postgresql' => { 'adapter' => 'postgresql', 'database' => 'typed_store_test', 'username' => 'postgres' },
9
- 'test_mysql' => { 'adapter' => 'mysql2', 'database' => 'typed_store_test', 'username' => 'root', 'password' => 'root' },
11
+ test: {
12
+ 'test_sqlite3' => { 'adapter' => 'sqlite3', 'database' => '/tmp/typed_store.db' },
13
+ }
10
14
  }
11
15
 
12
- def define_columns(t)
16
+ def define_columns(t, array: false)
13
17
  t.integer :no_default
14
18
 
15
19
  t.string :name, default: '', null: false
@@ -37,16 +41,12 @@ def define_columns(t)
37
41
  t.decimal :total_price, default: 4.2, null: false, precision: 16, scale: 2
38
42
  t.decimal :shipping_cost, precision: 16, scale: 2
39
43
 
40
- t.integer :grades, array: true
41
-
42
- if t.respond_to?(:name) && t.name =~ /sqlite|mysql/
43
- # native sqlite cannot automatically cast array to yaml
44
+ if t.is_a?(ActiveRecord::TypedStore::DSL)
45
+ t.integer :grades, array: true
44
46
  t.string :tags, array: true, null: false, default: [].to_yaml
45
- else
46
- t.string :tags, array: true, null: false, default: []
47
- end
48
47
 
49
- t.string :nickname, blank: false, default: 'Please enter your nickname'
48
+ t.string :nickname, blank: false, default: 'Please enter your nickname'
49
+ end
50
50
  end
51
51
 
52
52
  def define_store_with_no_attributes(**options)
@@ -74,37 +74,28 @@ def define_store_with_attributes(**options)
74
74
  end
75
75
  end
76
76
 
77
- MigrationClass = ActiveRecord::Migration["5.0"]
78
- class CreateAllTables < MigrationClass
79
-
80
- def self.recreate_table(name, *args, &block)
81
- execute "drop table if exists #{name}"
82
- create_table(name, *args, &block)
83
- end
77
+ def define_stores_with_prefix_and_suffix(**options)
78
+ typed_store(:prefixed_settings, prefix: true, **options) { |t| t.any :language }
79
+ typed_store(:suffixed_settings, suffix: true, **options) { |t| t.any :language }
80
+ typed_store(:custom_prefixed_settings, prefix: :custom, **options) { |t| t.any :language }
81
+ typed_store(:custom_suffixed_settings, suffix: :custom, **options) { |t| t.any :language }
82
+ end
84
83
 
84
+ MigrationClass = ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"]
85
+ class CreateAllTables < MigrationClass
85
86
  def self.up
86
- if ENV['MYSQL']
87
- ActiveRecord::Base.establish_connection(:test_mysql)
88
- recreate_table(:mysql_regular_ar_models) { |t| define_columns(t); t.text :untyped_settings }
89
- end
87
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations.configs_for(env_name: "test", name: :test_sqlite3))
88
+ create_table(:sqlite3_regular_ar_models, force: true) { |t| define_columns(t); t.text :untyped_settings }
89
+ create_table(:yaml_typed_store_models, force: true) { |t| %i[settings explicit_settings partial_settings untyped_settings prefixed_settings suffixed_settings custom_prefixed_settings custom_suffixed_settings].each { |column| t.text column}; t.string :regular_column }
90
+ create_table(:json_typed_store_models, force: true) { |t| %i[settings explicit_settings partial_settings untyped_settings prefixed_settings suffixed_settings custom_prefixed_settings custom_suffixed_settings].each { |column| t.text column}; t.string :regular_column }
91
+ create_table(:marshal_typed_store_models, force: true) { |t| %i[settings explicit_settings partial_settings untyped_settings prefixed_settings suffixed_settings custom_prefixed_settings custom_suffixed_settings].each { |column| t.text column}; t.string :regular_column }
90
92
 
91
- if ENV['POSTGRES']
92
- ActiveRecord::Base.establish_connection(ENV['POSTGRES_URL'] || :test_postgresql)
93
- recreate_table(:postgresql_regular_ar_models) { |t| define_columns(t); t.text :untyped_settings }
93
+ create_table(:dirty_tracking_models, force: true) do |t|
94
+ t.string :title
95
+ t.text :settings
94
96
 
95
- execute "create extension if not exists hstore"
96
- recreate_table(:postgres_hstore_typed_store_models) { |t| t.hstore :settings; t.text :untyped_settings }
97
-
98
- if ENV['POSTGRES_JSON']
99
- recreate_table(:postgres_json_typed_store_models) { |t| t.json :settings; t.text :explicit_settings; t.text :partial_settings; t.text :untyped_settings }
100
- end
97
+ t.timestamps
101
98
  end
102
-
103
- ActiveRecord::Base.establish_connection(:test_sqlite3)
104
- recreate_table(:sqlite3_regular_ar_models) { |t| define_columns(t); t.text :untyped_settings }
105
- recreate_table(:yaml_typed_store_models) { |t| t.text :settings; t.text :explicit_settings; t.text :partial_settings; t.text :untyped_settings }
106
- recreate_table(:json_typed_store_models) { |t| t.text :settings; t.text :explicit_settings; t.text :partial_settings; t.text :untyped_settings }
107
- recreate_table(:marshal_typed_store_models) { |t| t.text :settings; t.text :explicit_settings; t.text :partial_settings; t.text :untyped_settings }
108
99
  end
109
100
  end
110
101
  ActiveRecord::Migration.verbose = true
@@ -129,44 +120,6 @@ class ColumnCoder
129
120
 
130
121
  end
131
122
 
132
- module AsJson
133
- extend self
134
-
135
- def load(value)
136
- value
137
- end
138
-
139
- def dump(value)
140
- value.as_json
141
- end
142
-
143
- end
144
-
145
- if ENV['MYSQL']
146
- class MysqlRegularARModel < ActiveRecord::Base
147
- establish_connection :test_mysql
148
- store :untyped_settings, accessors: [:title]
149
- end
150
- end
151
-
152
- if ENV['POSTGRES']
153
- class PostgresqlRegularARModel < ActiveRecord::Base
154
- establish_connection ENV['POSTGRES_URL'] || :test_postgresql
155
- store :untyped_settings, accessors: [:title]
156
- end
157
-
158
- if ENV['POSTGRES_JSON']
159
- class PostgresJsonTypedStoreModel < ActiveRecord::Base
160
- establish_connection ENV['POSTGRES_URL'] || :test_postgresql
161
- store :untyped_settings, accessors: [:title]
162
-
163
- define_store_with_attributes(coder: ColumnCoder.new(AsJson))
164
- define_store_with_no_attributes(coder: ColumnCoder.new(AsJson))
165
- define_store_with_partial_attributes(coder: ColumnCoder.new(AsJson))
166
- end
167
- end
168
- end
169
-
170
123
  class Sqlite3RegularARModel < ActiveRecord::Base
171
124
  establish_connection :test_sqlite3
172
125
  store :untyped_settings, accessors: [:title]
@@ -176,9 +129,15 @@ class YamlTypedStoreModel < ActiveRecord::Base
176
129
  establish_connection :test_sqlite3
177
130
  store :untyped_settings, accessors: [:title]
178
131
 
132
+ after_update :read_active
133
+ def read_active
134
+ enabled
135
+ end
136
+
179
137
  define_store_with_attributes
180
138
  define_store_with_no_attributes
181
139
  define_store_with_partial_attributes
140
+ define_stores_with_prefix_and_suffix
182
141
  end
183
142
 
184
143
  class InheritedTypedStoreModel < YamlTypedStoreModel
@@ -196,6 +155,7 @@ class JsonTypedStoreModel < ActiveRecord::Base
196
155
  define_store_with_attributes(coder: ColumnCoder.new(JSON))
197
156
  define_store_with_no_attributes(coder: ColumnCoder.new(JSON))
198
157
  define_store_with_partial_attributes(coder: ColumnCoder.new(JSON))
158
+ define_stores_with_prefix_and_suffix(coder: ColumnCoder.new(JSON))
199
159
  end
200
160
 
201
161
  module MarshalCoder
@@ -218,6 +178,7 @@ class MarshalTypedStoreModel < ActiveRecord::Base
218
178
  define_store_with_attributes(coder: ColumnCoder.new(MarshalCoder))
219
179
  define_store_with_no_attributes(coder: ColumnCoder.new(MarshalCoder))
220
180
  define_store_with_partial_attributes(coder: ColumnCoder.new(MarshalCoder))
181
+ define_stores_with_prefix_and_suffix(coder: ColumnCoder.new(MarshalCoder))
221
182
  end
222
183
 
223
184
  Models = [
@@ -227,6 +188,15 @@ Models = [
227
188
  JsonTypedStoreModel,
228
189
  MarshalTypedStoreModel
229
190
  ]
230
- Models << MysqlRegularARModel if defined?(MysqlRegularARModel)
231
- Models << PostgresqlRegularARModel if defined?(PostgresqlRegularARModel)
232
- Models << PostgresJsonTypedStoreModel if defined?(PostgresJsonTypedStoreModel)
191
+
192
+ class DirtyTrackingModel < ActiveRecord::Base
193
+ after_update :read_active
194
+
195
+ typed_store(:settings) do |f|
196
+ f.boolean :active, default: false, null: false
197
+ end
198
+
199
+ def read_active
200
+ active
201
+ end
202
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-typedstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-28 00:00:00.000000000 Z
11
+ date: 2022-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.2'
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5.2'
26
+ version: '6.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '10'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '10'
54
+ version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3'
69
- - !ruby/object:Gem::Dependency
70
- name: coveralls
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: sqlite3
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -94,34 +80,6 @@ dependencies:
94
80
  - - "~>"
95
81
  - !ruby/object:Gem::Version
96
82
  version: '1'
97
- - !ruby/object:Gem::Dependency
98
- name: pg
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '0.18'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '0.18'
111
- - !ruby/object:Gem::Dependency
112
- name: mysql2
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">"
116
- - !ruby/object:Gem::Version
117
- version: '0.3'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">"
123
- - !ruby/object:Gem::Version
124
- version: '0.3'
125
83
  - !ruby/object:Gem::Dependency
126
84
  name: database_cleaner
127
85
  requirement: !ruby/object:Gem::Requirement
@@ -151,11 +109,9 @@ files:
151
109
  - README.md
152
110
  - Rakefile
153
111
  - activerecord-typedstore.gemspec
154
- - gemfiles/Gemfile.ar-5.0
155
- - gemfiles/Gemfile.ar-5.2
156
- - gemfiles/Gemfile.ar-6.0
157
112
  - gemfiles/Gemfile.ar-6.1
158
- - gemfiles/Gemfile.ar-master
113
+ - gemfiles/Gemfile.ar-7.0
114
+ - gemfiles/Gemfile.ar-edge
159
115
  - lib/active_record/typed_store.rb
160
116
  - lib/active_record/typed_store/behavior.rb
161
117
  - lib/active_record/typed_store/dsl.rb
@@ -190,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
146
  - !ruby/object:Gem::Version
191
147
  version: '0'
192
148
  requirements: []
193
- rubygems_version: 3.1.2
149
+ rubygems_version: 3.3.7
194
150
  signing_key:
195
151
  specification_version: 4
196
152
  summary: Add type casting and full method attributes support to АctiveRecord store
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec path: '..'
4
-
5
- gem 'sqlite3', '~> 1.3.0'
6
- gem 'activerecord', '~> 5.0.0'
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec path: '..'
4
-
5
- gem 'activerecord', '~> 6.0.0'