activerecord-typedstore 1.5.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: 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