activerecord-typedstore 1.5.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: e4f681cbd23a432d2e40094499e45a693ebacc8784d639dbd35f5d7467d3f024
4
- data.tar.gz: 2adde8a50a784c41a23e188d51ababc7c19a553d6e4599e2265dad9c70ea5ebe
3
+ metadata.gz: 5f56fb7b6456937890a85e139a296727a04b2c87a2452f45810782849378357a
4
+ data.tar.gz: 17e694f93e4db21a4b5a08c7795285fd10aaf9bc203f74376a14356873c84802
5
5
  SHA512:
6
- metadata.gz: 67570832d5d8f6d7d05617f2f6572ed1e629bb1463f4bc123513d84219306a2aca59439082eed57a00d80e97474bd1b5a63269a94caabd530769bd8c098c3da6
7
- data.tar.gz: a9f3f3d3ab6bbbe4b47625674fde96e3f3c8c0fbb697f2b557b1aa089ed45bd91e2c3bff98c0bbee8c55d52bc3d473bf16fc92759b65ad5a84548d91a5e81b14
6
+ metadata.gz: 02d3ce60427903e2c039818c4c77368a42b20c4f9fbf5bf167357e0e932d01c717f5eea6172ea03170b018a468b6b8f0a00d59fd49073a38eb620a27e05870c3
7
+ data.tar.gz: 96ff55688ac6c1b2d9f56d9a45e796b4c633e6d3d1dd89cdbdb14025044d97c9f5deefce7a53494f35ff5a227f340e8ec9d9fba70b7d861fe488ee6f52ad98fe
@@ -5,22 +5,22 @@ 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
10
  fail-fast: false
11
11
  matrix:
12
12
  ruby: ['2.7', '3.0', '3.1']
13
- gemfile: [Gemfile.ar-6.1, Gemfile.ar-7.0, Gemfile.ar-master]
13
+ rails: ['6.1', '7.0', 'edge']
14
14
  timezone_aware: [0, 1]
15
15
  env:
16
- BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}
16
+ BUNDLE_GEMFILE: gemfiles/Gemfile.ar-${{ matrix.rails }}
17
17
  TIMEZONE_AWARE: ${{ matrix.timezone_aware }}
18
18
  POSTGRES: 1
19
19
  MYSQL: 1
20
20
  POSTGRES_JSON: 1
21
21
  steps:
22
22
  - name: Check out code
23
- uses: actions/checkout@v2
23
+ uses: actions/checkout@v3
24
24
  - name: Set up Ruby ${{ matrix.ruby }}
25
25
  uses: ruby/setup-ruby@v1
26
26
  with:
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
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
26
  spec.add_development_dependency 'sqlite3', '~> 1'
27
27
  spec.add_development_dependency 'database_cleaner', '~> 1'
File without changes
@@ -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
@@ -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.5.0'
5
+ VERSION = '1.5.1'
6
6
  end
7
7
  end
@@ -925,7 +925,7 @@ describe YamlTypedStoreModel do
925
925
 
926
926
  it 'nested hashes are not serialized as HashWithIndifferentAccess' do
927
927
  model = described_class.create!
928
- expect(model.settings_before_type_cast).not_to include('HashWithIndifferentAccess')
928
+ expect(model.settings_before_type_cast.to_s).not_to include('HashWithIndifferentAccess')
929
929
  end
930
930
  end
931
931
 
@@ -954,3 +954,23 @@ describe InheritedTypedStoreModel do
954
954
  expect(model.settings[:new_attribute]).to be == '42'
955
955
  end
956
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
@@ -8,6 +8,12 @@ Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '**', '*.rb'))
8
8
 
9
9
  Time.zone = 'UTC'
10
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
+
11
17
  RSpec.configure do |config|
12
18
  config.order = 'random'
13
19
  end
@@ -1,4 +1,5 @@
1
1
  require 'active_record'
2
+ require 'base64'
2
3
  require 'json'
3
4
  require 'yaml'
4
5
 
@@ -12,7 +13,7 @@ ActiveRecord::Base.configurations = {
12
13
  }
13
14
  }
14
15
 
15
- def define_columns(t)
16
+ def define_columns(t, array: false)
16
17
  t.integer :no_default
17
18
 
18
19
  t.string :name, default: '', null: false
@@ -40,11 +41,12 @@ def define_columns(t)
40
41
  t.decimal :total_price, default: 4.2, null: false, precision: 16, scale: 2
41
42
  t.decimal :shipping_cost, precision: 16, scale: 2
42
43
 
43
- t.integer :grades, array: true
44
+ if t.is_a?(ActiveRecord::TypedStore::DSL)
45
+ t.integer :grades, array: true
46
+ t.string :tags, array: true, null: false, default: [].to_yaml
44
47
 
45
- t.string :tags, array: true, null: false, default: [].to_yaml
46
-
47
- t.string :nickname, blank: false, default: 'Please enter your nickname'
48
+ t.string :nickname, blank: false, default: 'Please enter your nickname'
49
+ end
48
50
  end
49
51
 
50
52
  def define_store_with_no_attributes(**options)
@@ -79,15 +81,21 @@ def define_stores_with_prefix_and_suffix(**options)
79
81
  typed_store(:custom_suffixed_settings, suffix: :custom, **options) { |t| t.any :language }
80
82
  end
81
83
 
82
- MigrationClass = ActiveRecord::Migration["5.0"]
84
+ MigrationClass = ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"]
83
85
  class CreateAllTables < MigrationClass
84
-
85
86
  def self.up
86
87
  ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations.configs_for(env_name: "test", name: :test_sqlite3))
87
88
  create_table(:sqlite3_regular_ar_models, force: true) { |t| define_columns(t); t.text :untyped_settings }
88
- 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} }
89
- 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} }
90
- 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} }
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 }
92
+
93
+ create_table(:dirty_tracking_models, force: true) do |t|
94
+ t.string :title
95
+ t.text :settings
96
+
97
+ t.timestamps
98
+ end
91
99
  end
92
100
  end
93
101
  ActiveRecord::Migration.verbose = true
@@ -121,6 +129,11 @@ class YamlTypedStoreModel < ActiveRecord::Base
121
129
  establish_connection :test_sqlite3
122
130
  store :untyped_settings, accessors: [:title]
123
131
 
132
+ after_update :read_active
133
+ def read_active
134
+ enabled
135
+ end
136
+
124
137
  define_store_with_attributes
125
138
  define_store_with_no_attributes
126
139
  define_store_with_partial_attributes
@@ -175,3 +188,15 @@ Models = [
175
188
  JsonTypedStoreModel,
176
189
  MarshalTypedStoreModel
177
190
  ]
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.5.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: 2022-05-24 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
@@ -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
@@ -111,7 +111,7 @@ files:
111
111
  - activerecord-typedstore.gemspec
112
112
  - gemfiles/Gemfile.ar-6.1
113
113
  - gemfiles/Gemfile.ar-7.0
114
- - gemfiles/Gemfile.ar-master
114
+ - gemfiles/Gemfile.ar-edge
115
115
  - lib/active_record/typed_store.rb
116
116
  - lib/active_record/typed_store/behavior.rb
117
117
  - lib/active_record/typed_store/dsl.rb