activerecord-slotted_counters 0.1.4 → 0.3.0
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +4 -7
- data/lib/activerecord_slotted_counters/adapters/mysql_upsert.rb +83 -0
- data/lib/activerecord_slotted_counters/adapters/pg_upsert.rb +11 -4
- data/lib/activerecord_slotted_counters/adapters/rails7_upsert.rb +22 -0
- data/lib/activerecord_slotted_counters/adapters/rails_upsert.rb +20 -4
- data/lib/activerecord_slotted_counters/adapters/sqlite_upsert.rb +107 -0
- data/lib/activerecord_slotted_counters/has_slotted_counter.rb +15 -8
- data/lib/activerecord_slotted_counters/utils.rb +2 -2
- data/lib/activerecord_slotted_counters/version.rb +1 -1
- metadata +9 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a233ed9bbeebe36d70a84c813b01d07cf74057709b907d491ac71e688bfc44c
|
4
|
+
data.tar.gz: c93306db04ae60233c9fb0ec6b8a28f1ee75f5bf22e34f503ae462f6858baa07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce0bb7f7cdb8fe0c48de169c2e8e94d5977e74cb3840bc4aafbaf181a866e12eb7b06d4ffef603d0768d21ec4b16f7092f2884dd0c1bf1b76d99cd61e71c9353
|
7
|
+
data.tar.gz: 93d093f88f9cae152096dbc7c3e906f4a9c39a2e50db21befeef4b9ab756515bea3149ffa6fdf7391aa2dc16d2e816f9a0819567d25992f517936b872391c135
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.3.0 (2025-03-05)
|
6
|
+
|
7
|
+
- Rails 8 support. ([@palkan][])
|
8
|
+
|
9
|
+
## 0.2.0 (2023-10-27)
|
10
|
+
|
11
|
+
- Add Mysql support [#18](https://github.com/evilmartians/activerecord-slotted_counters/pull/18) ([@prog-supdex][])
|
12
|
+
|
13
|
+
- Add SQLite support [#17](https://github.com/evilmartians/activerecord-slotted_counters/pull/17) ([@prog-supdex][])
|
14
|
+
|
5
15
|
## 0.1.4 (2023-04-19)
|
6
16
|
|
7
17
|
- Fix "can't modify frozen String" for the pg adapter (ruby 2.7) [#15](https://github.com/evilmartians/activerecord-slotted_counters/pull/15) ([@LukinEgor][])
|
@@ -25,3 +35,4 @@
|
|
25
35
|
[@palkan]: https://github.com/palkan
|
26
36
|
[@LukinEgor]: https://github.com/LukinEgor
|
27
37
|
[@danielwestendorf]: https://github.com/danielwestendorf
|
38
|
+
[@prog-supdex]: https://github.com/prog-supdex
|
data/README.md
CHANGED
@@ -22,9 +22,10 @@ Add to your project:
|
|
22
22
|
gem "activerecord-slotted_counters"
|
23
23
|
```
|
24
24
|
|
25
|
-
###
|
25
|
+
### Requirements
|
26
26
|
|
27
|
-
- Ruby
|
27
|
+
- Ruby >= 2.7.0
|
28
|
+
- Rails 6+
|
28
29
|
|
29
30
|
## Usage
|
30
31
|
|
@@ -67,7 +68,7 @@ user.comments_count #=> select * from slotted_counters where ...
|
|
67
68
|
user.comments_count #=> no sql
|
68
69
|
```
|
69
70
|
|
70
|
-
If you want to
|
71
|
+
If you want to preload counters for multiple records, you can use a convenient `#with_slotted_counters` method:
|
71
72
|
|
72
73
|
```ruby
|
73
74
|
User.all.with_slotted_counters(:comments).find_each do
|
@@ -77,10 +78,6 @@ end
|
|
77
78
|
|
78
79
|
Using `counter_cache: true` on `belongs_to` associations also works as expected.
|
79
80
|
|
80
|
-
## Limitations / TODO
|
81
|
-
|
82
|
-
- Gem supports only PostgreSQL for Rails 6
|
83
|
-
|
84
81
|
## Contributing
|
85
82
|
|
86
83
|
Bug reports and pull requests are welcome on GitHub at [https://github.com/evilmartians/activerecord-slotted_counters](https://github.com/evilmartians/activerecord-slotted_counters).
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordSlottedCounters
|
4
|
+
module Adapters
|
5
|
+
class MysqlUpsert
|
6
|
+
attr_reader :klass
|
7
|
+
|
8
|
+
def initialize(klass, **)
|
9
|
+
@klass = klass
|
10
|
+
end
|
11
|
+
|
12
|
+
def apply?(current_adapter_name)
|
13
|
+
return false unless defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
|
14
|
+
|
15
|
+
current_adapter_name == ActiveRecord::ConnectionAdapters::Mysql2Adapter::ADAPTER_NAME
|
16
|
+
end
|
17
|
+
|
18
|
+
def bulk_insert(attributes, on_duplicate: nil, **)
|
19
|
+
raise ArgumentError, "Values must not be empty" if attributes.empty?
|
20
|
+
|
21
|
+
keys = attributes.first.keys + klass.all_timestamp_attributes_in_model
|
22
|
+
|
23
|
+
current_time = klass.current_time_from_proper_timezone
|
24
|
+
data = attributes.map { |attr| attr.values + [current_time, current_time] }
|
25
|
+
|
26
|
+
columns = columns_for_attributes(keys)
|
27
|
+
|
28
|
+
fields_str = quote_column_names(columns)
|
29
|
+
values_str = quote_many_records(columns, data)
|
30
|
+
|
31
|
+
sql = <<~SQL
|
32
|
+
INSERT INTO #{klass.quoted_table_name}
|
33
|
+
(#{fields_str})
|
34
|
+
VALUES #{values_str}
|
35
|
+
SQL
|
36
|
+
|
37
|
+
if on_duplicate.present?
|
38
|
+
sql += " ON DUPLICATE KEY UPDATE #{on_duplicate};"
|
39
|
+
end
|
40
|
+
|
41
|
+
# insert/update and return amount of updated rows
|
42
|
+
klass.connection.update(sql)
|
43
|
+
end
|
44
|
+
|
45
|
+
def wrap_column_name(value)
|
46
|
+
"VALUES(#{value})"
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def columns_for_attributes(attributes)
|
52
|
+
attributes.map do |attribute|
|
53
|
+
klass.column_for_attribute(attribute)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def quote_column_names(columns, table_name: false)
|
58
|
+
columns.map do |column|
|
59
|
+
column_name = klass.connection.quote_column_name(column.name)
|
60
|
+
|
61
|
+
if table_name
|
62
|
+
"#{klass.quoted_table_name}.#{column_name}"
|
63
|
+
else
|
64
|
+
column_name
|
65
|
+
end
|
66
|
+
end.join(",")
|
67
|
+
end
|
68
|
+
|
69
|
+
def quote_record(columns, record_values)
|
70
|
+
values_str = record_values.each_with_index.map do |value, i|
|
71
|
+
type = klass.connection.lookup_cast_type_from_column(columns[i])
|
72
|
+
klass.connection.quote(type.serialize(value))
|
73
|
+
end.join(",")
|
74
|
+
|
75
|
+
"(#{values_str})"
|
76
|
+
end
|
77
|
+
|
78
|
+
def quote_many_records(columns, data)
|
79
|
+
data.map { |values| quote_record(columns, values) }.join(",")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -5,12 +5,15 @@ module ActiveRecordSlottedCounters
|
|
5
5
|
class PgUpsert
|
6
6
|
attr_reader :klass
|
7
7
|
|
8
|
-
def initialize(klass)
|
8
|
+
def initialize(klass, **)
|
9
9
|
@klass = klass
|
10
10
|
end
|
11
11
|
|
12
|
-
def apply?
|
13
|
-
ActiveRecord::VERSION::MAJOR
|
12
|
+
def apply?(current_adapter_name)
|
13
|
+
return false if ActiveRecord::VERSION::MAJOR >= 7
|
14
|
+
return false unless defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
15
|
+
|
16
|
+
current_adapter_name == ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::ADAPTER_NAME
|
14
17
|
end
|
15
18
|
|
16
19
|
def bulk_insert(attributes, on_duplicate: nil, unique_by: nil)
|
@@ -46,7 +49,11 @@ module ActiveRecordSlottedCounters
|
|
46
49
|
|
47
50
|
sql += " RETURNING \"id\""
|
48
51
|
|
49
|
-
klass.connection.exec_query(sql)
|
52
|
+
klass.connection.exec_query(sql).rows.count
|
53
|
+
end
|
54
|
+
|
55
|
+
def wrap_column_name(value)
|
56
|
+
"EXCLUDED.#{value}"
|
50
57
|
end
|
51
58
|
|
52
59
|
private
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordSlottedCounters
|
4
|
+
module Adapters
|
5
|
+
class Rails7Upsert < RailsUpsert
|
6
|
+
def apply?(_)
|
7
|
+
ActiveRecord::VERSION::MAJOR == 7 && ActiveRecord::VERSION::MINOR < 2
|
8
|
+
end
|
9
|
+
|
10
|
+
def bulk_insert(attributes, on_duplicate: nil, unique_by: nil)
|
11
|
+
opts = {on_duplicate: on_duplicate, unique_by: unique_by}
|
12
|
+
opts.delete(:unique_by) unless supports_insert_conflict_target
|
13
|
+
|
14
|
+
# We have to manually call #update here to return the number of affected rows.
|
15
|
+
# In Rails <7.2, connection is obtained internally.
|
16
|
+
ActiveRecord::InsertAll.new(klass, attributes, **opts).then do |inserter|
|
17
|
+
inserter.send(:connection).update(inserter.send(:to_sql))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -3,18 +3,34 @@
|
|
3
3
|
module ActiveRecordSlottedCounters
|
4
4
|
module Adapters
|
5
5
|
class RailsUpsert
|
6
|
-
attr_reader :klass
|
6
|
+
attr_reader :klass, :supports_insert_conflict_target
|
7
7
|
|
8
|
-
def initialize(klass)
|
8
|
+
def initialize(klass, supports_insert_conflict_target: false)
|
9
9
|
@klass = klass
|
10
|
+
@supports_insert_conflict_target = supports_insert_conflict_target
|
10
11
|
end
|
11
12
|
|
12
|
-
def apply?
|
13
|
+
def apply?(_)
|
13
14
|
ActiveRecord::VERSION::MAJOR >= 7
|
14
15
|
end
|
15
16
|
|
16
17
|
def bulk_insert(attributes, on_duplicate: nil, unique_by: nil)
|
17
|
-
|
18
|
+
opts = {on_duplicate: on_duplicate, unique_by: unique_by}
|
19
|
+
opts.delete(:unique_by) unless supports_insert_conflict_target
|
20
|
+
|
21
|
+
klass.with_connection do |c|
|
22
|
+
# We have to manually call #update here to return the number of affected rows
|
23
|
+
c.update(ActiveRecord::InsertAll.new(klass.all, c, attributes, **opts).send(:to_sql))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def wrap_column_name(value)
|
28
|
+
# This is mysql
|
29
|
+
if !supports_insert_conflict_target
|
30
|
+
"VALUES(#{value})"
|
31
|
+
else
|
32
|
+
"EXCLUDED.#{value}"
|
33
|
+
end
|
18
34
|
end
|
19
35
|
end
|
20
36
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordSlottedCounters
|
4
|
+
module Adapters
|
5
|
+
class SqliteUpsert
|
6
|
+
attr_reader :klass
|
7
|
+
|
8
|
+
def initialize(klass, **)
|
9
|
+
@klass = klass
|
10
|
+
end
|
11
|
+
|
12
|
+
def apply?(current_adapter_name)
|
13
|
+
return false if ActiveRecord::VERSION::MAJOR >= 7
|
14
|
+
return false unless defined?(ActiveRecord::ConnectionAdapters::SQLite3Adapter)
|
15
|
+
|
16
|
+
current_adapter_name == ActiveRecord::ConnectionAdapters::SQLite3Adapter::ADAPTER_NAME
|
17
|
+
end
|
18
|
+
|
19
|
+
def bulk_insert(attributes, on_duplicate: nil, unique_by: nil)
|
20
|
+
raise ArgumentError, "Values must not be empty" if attributes.empty?
|
21
|
+
|
22
|
+
keys = attributes.first.keys + klass.all_timestamp_attributes_in_model
|
23
|
+
|
24
|
+
current_time = klass.current_time_from_proper_timezone
|
25
|
+
data = attributes.map { |attr| attr.values + [current_time, current_time] }
|
26
|
+
|
27
|
+
columns = columns_for_attributes(keys)
|
28
|
+
|
29
|
+
fields_str = quote_column_names(columns)
|
30
|
+
values_str = quote_many_records(columns, data)
|
31
|
+
|
32
|
+
sql = <<~SQL
|
33
|
+
INSERT INTO #{klass.quoted_table_name}
|
34
|
+
(#{fields_str})
|
35
|
+
VALUES #{values_str}
|
36
|
+
SQL
|
37
|
+
|
38
|
+
if unique_by.present?
|
39
|
+
index = unique_indexes.find { |i| i.name.to_sym == unique_by }
|
40
|
+
columns = columns_for_attributes(index.columns)
|
41
|
+
fields = quote_column_names(columns)
|
42
|
+
|
43
|
+
sql += " ON CONFLICT (#{fields})"
|
44
|
+
end
|
45
|
+
|
46
|
+
if on_duplicate.present?
|
47
|
+
sql += " DO UPDATE SET #{on_duplicate}"
|
48
|
+
end
|
49
|
+
|
50
|
+
sql += " RETURNING \"id\""
|
51
|
+
|
52
|
+
klass.connection.exec_query(sql).rows.count
|
53
|
+
end
|
54
|
+
|
55
|
+
def wrap_column_name(value)
|
56
|
+
"EXCLUDED.#{value}"
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def unique_indexes
|
62
|
+
klass.connection.schema_cache.indexes(klass.table_name).select(&:unique)
|
63
|
+
end
|
64
|
+
|
65
|
+
def columns_for_attributes(attributes)
|
66
|
+
attributes.map do |attribute|
|
67
|
+
klass.column_for_attribute(attribute)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def quote_column_names(columns, table_name: false)
|
72
|
+
columns.map do |column|
|
73
|
+
column_name = klass.connection.quote_column_name(column.name)
|
74
|
+
if table_name
|
75
|
+
"#{klass.quoted_table_name}.#{column_name}"
|
76
|
+
else
|
77
|
+
column_name
|
78
|
+
end
|
79
|
+
end.join(",")
|
80
|
+
end
|
81
|
+
|
82
|
+
def quote_record(columns, record_values)
|
83
|
+
values_str = record_values.each_with_index.map do |value, i|
|
84
|
+
type = klass.connection.lookup_cast_type_from_column(columns[i])
|
85
|
+
klass.connection.quote(type.serialize(value))
|
86
|
+
end.join(",")
|
87
|
+
"(#{values_str})"
|
88
|
+
end
|
89
|
+
|
90
|
+
def quote_many_records(columns, data)
|
91
|
+
data.map { |values| quote_record(columns, values) }.join(",")
|
92
|
+
end
|
93
|
+
|
94
|
+
def postgresql_connection?(adapter_name)
|
95
|
+
return false unless defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
96
|
+
|
97
|
+
adapter_name == ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::ADAPTER_NAME
|
98
|
+
end
|
99
|
+
|
100
|
+
def sqlite_connection?(adapter_name)
|
101
|
+
return false unless defined?(ActiveRecord::ConnectionAdapters::SQLite3Adapter)
|
102
|
+
|
103
|
+
adapter_name == ActiveRecord::ConnectionAdapters::SQLite3Adapter::ADAPTER_NAME
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -4,7 +4,10 @@ require "active_support"
|
|
4
4
|
require "activerecord_slotted_counters/utils"
|
5
5
|
|
6
6
|
require "activerecord_slotted_counters/adapters/rails_upsert"
|
7
|
+
require "activerecord_slotted_counters/adapters/rails7_upsert"
|
7
8
|
require "activerecord_slotted_counters/adapters/pg_upsert"
|
9
|
+
require "activerecord_slotted_counters/adapters/sqlite_upsert"
|
10
|
+
require "activerecord_slotted_counters/adapters/mysql_upsert"
|
8
11
|
|
9
12
|
module ActiveRecordSlottedCounters
|
10
13
|
class SlottedCounter < ::ActiveRecord::Base
|
@@ -24,7 +27,8 @@ module ActiveRecordSlottedCounters
|
|
24
27
|
|
25
28
|
class << self
|
26
29
|
def bulk_insert(attributes)
|
27
|
-
on_duplicate_clause =
|
30
|
+
on_duplicate_clause =
|
31
|
+
"count = slotted_counters.count + #{slotted_counter_db_adapter.wrap_column_name("count")}"
|
28
32
|
|
29
33
|
slotted_counter_db_adapter.bulk_insert(
|
30
34
|
attributes,
|
@@ -41,15 +45,20 @@ module ActiveRecordSlottedCounters
|
|
41
45
|
|
42
46
|
def set_slotted_counter_db_adapter
|
43
47
|
available_adapters = [
|
48
|
+
ActiveRecordSlottedCounters::Adapters::Rails7Upsert,
|
44
49
|
ActiveRecordSlottedCounters::Adapters::RailsUpsert,
|
50
|
+
ActiveRecordSlottedCounters::Adapters::MysqlUpsert,
|
51
|
+
ActiveRecordSlottedCounters::Adapters::SqliteUpsert,
|
45
52
|
ActiveRecordSlottedCounters::Adapters::PgUpsert
|
46
53
|
]
|
47
54
|
|
55
|
+
current_adapter_name = connection.adapter_name
|
56
|
+
|
48
57
|
adapter = available_adapters
|
49
|
-
.map { |adapter| adapter.new(self) }
|
50
|
-
.detect { |adapter| adapter.apply? }
|
58
|
+
.map { |adapter| adapter.new(self, supports_insert_conflict_target: connection.supports_insert_conflict_target?) }
|
59
|
+
.detect { |adapter| adapter.apply?(current_adapter_name) }
|
51
60
|
|
52
|
-
raise NotSupportedAdapter.new(
|
61
|
+
raise NotSupportedAdapter.new(current_adapter_name) if adapter.nil?
|
53
62
|
|
54
63
|
adapter
|
55
64
|
end
|
@@ -205,9 +214,7 @@ module ActiveRecordSlottedCounters
|
|
205
214
|
def insert_counters_records(ids, counters)
|
206
215
|
counters_params = prepare_slotted_counters_params(ids, counters)
|
207
216
|
|
208
|
-
|
209
|
-
|
210
|
-
result.rows.count
|
217
|
+
ActiveRecordSlottedCounters::SlottedCounter.bulk_insert(counters_params)
|
211
218
|
end
|
212
219
|
|
213
220
|
def remove_counters_records(ids, counter_name)
|
@@ -219,7 +226,7 @@ module ActiveRecordSlottedCounters
|
|
219
226
|
end
|
220
227
|
|
221
228
|
def touch_attributes(ids, touch)
|
222
|
-
scope = where(id: ids)
|
229
|
+
scope = unscoped.where(id: ids)
|
223
230
|
return scope.touch_all if touch == true
|
224
231
|
|
225
232
|
scope.touch_all(touch)
|
@@ -7,11 +7,11 @@ module ActiveRecordSlottedCounters
|
|
7
7
|
private
|
8
8
|
|
9
9
|
def slotted_counter_association_name(counter_type)
|
10
|
-
"#{counter_type}_slotted_counters"
|
10
|
+
:"#{counter_type}_slotted_counters"
|
11
11
|
end
|
12
12
|
|
13
13
|
def slotted_counter_name(counter_type)
|
14
|
-
"#{counter_type}_count"
|
14
|
+
:"#{counter_type}_count"
|
15
15
|
end
|
16
16
|
|
17
17
|
# TODO refactoring
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-slotted_counters
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Egor Lukin
|
8
8
|
- Vladimir Dementyev
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2025-03-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -67,34 +67,6 @@ dependencies:
|
|
67
67
|
- - ">="
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '3.9'
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: pg
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '1.4'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - ">="
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '1.4'
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
name: sqlite3
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - ">="
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: '0'
|
91
|
-
type: :development
|
92
|
-
prerelease: false
|
93
|
-
version_requirements: !ruby/object:Gem::Requirement
|
94
|
-
requirements:
|
95
|
-
- - ">="
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: '0'
|
98
70
|
- !ruby/object:Gem::Dependency
|
99
71
|
name: rspec-sqlimit
|
100
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -120,8 +92,11 @@ files:
|
|
120
92
|
- LICENSE.txt
|
121
93
|
- README.md
|
122
94
|
- lib/activerecord-slotted_counters.rb
|
95
|
+
- lib/activerecord_slotted_counters/adapters/mysql_upsert.rb
|
123
96
|
- lib/activerecord_slotted_counters/adapters/pg_upsert.rb
|
97
|
+
- lib/activerecord_slotted_counters/adapters/rails7_upsert.rb
|
124
98
|
- lib/activerecord_slotted_counters/adapters/rails_upsert.rb
|
99
|
+
- lib/activerecord_slotted_counters/adapters/sqlite_upsert.rb
|
125
100
|
- lib/activerecord_slotted_counters/has_slotted_counter.rb
|
126
101
|
- lib/activerecord_slotted_counters/railtie.rb
|
127
102
|
- lib/activerecord_slotted_counters/utils.rb
|
@@ -137,7 +112,7 @@ metadata:
|
|
137
112
|
documentation_uri: http://github.com/evilmartians/activerecord-slotted_counters
|
138
113
|
homepage_uri: http://github.com/evilmartians/activerecord-slotted_counters
|
139
114
|
source_code_uri: http://github.com/evilmartians/activerecord-slotted_counters
|
140
|
-
post_install_message:
|
115
|
+
post_install_message:
|
141
116
|
rdoc_options: []
|
142
117
|
require_paths:
|
143
118
|
- lib
|
@@ -152,8 +127,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
127
|
- !ruby/object:Gem::Version
|
153
128
|
version: '0'
|
154
129
|
requirements: []
|
155
|
-
rubygems_version: 3.
|
156
|
-
signing_key:
|
130
|
+
rubygems_version: 3.4.19
|
131
|
+
signing_key:
|
157
132
|
specification_version: 4
|
158
133
|
summary: Active Record slotted counters support
|
159
134
|
test_files: []
|