activerecord-typedstore 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'