vorpal 1.0.1 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +72 -56
- data/lib/vorpal/aggregate_mapper.rb +11 -0
- data/lib/vorpal/config/class_config.rb +71 -0
- data/lib/vorpal/configs.rb +9 -66
- data/lib/vorpal/driver/postgresql.rb +39 -3
- data/lib/vorpal/dsl/config_builder.rb +12 -50
- data/lib/vorpal/dsl/configuration.rb +131 -42
- data/lib/vorpal/dsl/defaults_generator.rb +14 -4
- data/lib/vorpal/engine.rb +18 -5
- data/lib/vorpal/identity_map.rb +15 -14
- data/lib/vorpal/version.rb +1 -1
- data/vorpal.gemspec +9 -9
- metadata +36 -88
- data/.editorconfig +0 -13
- data/.envrc +0 -4
- data/.gitignore +0 -16
- data/.rspec +0 -1
- data/.yardopts +0 -1
- data/Appraisals +0 -34
- data/Gemfile +0 -4
- data/Rakefile +0 -10
- data/bin/appraisal +0 -29
- data/bin/rake +0 -29
- data/bin/rspec +0 -29
- data/gemfiles/rails_4_1.gemfile +0 -11
- data/gemfiles/rails_4_1.gemfile.lock +0 -92
- data/gemfiles/rails_4_2.gemfile +0 -11
- data/gemfiles/rails_4_2.gemfile.lock +0 -90
- data/gemfiles/rails_5_0.gemfile +0 -11
- data/gemfiles/rails_5_0.gemfile.lock +0 -88
- data/gemfiles/rails_5_1.gemfile +0 -11
- data/gemfiles/rails_5_1.gemfile.lock +0 -88
- data/gemfiles/rails_5_2.gemfile +0 -11
- data/gemfiles/rails_5_2.gemfile.lock +0 -88
- data/spec/helpers/db_helpers.rb +0 -66
- data/spec/helpers/profile_helpers.rb +0 -26
- data/spec/integration_spec_helper.rb +0 -36
- data/spec/unit_spec_helper.rb +0 -0
- data/spec/vorpal/acceptance/aggregate_mapper_spec.rb +0 -911
- data/spec/vorpal/integration/driver/postgresql_spec.rb +0 -42
- data/spec/vorpal/performance/performance_spec.rb +0 -235
- data/spec/vorpal/unit/configs_spec.rb +0 -117
- data/spec/vorpal/unit/db_loader_spec.rb +0 -103
- data/spec/vorpal/unit/dsl/config_builder_spec.rb +0 -18
- data/spec/vorpal/unit/dsl/defaults_generator_spec.rb +0 -75
- data/spec/vorpal/unit/identity_map_spec.rb +0 -62
- data/spec/vorpal/unit/loaded_objects_spec.rb +0 -22
- data/spec/vorpal/unit/util/string_utils_spec.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83737ac0a684cae35b5e849cba0c7851234dd83644ec04458e2c3c49ae0213f1
|
4
|
+
data.tar.gz: 8ac93c1267e080bc874dc2526d7b9f2c5abc1635c579d0f4be7cbd9af4fd7fc9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 959ef178a6479c47053c2d100c323a48134fdcf7c63479c21afff0cc7cb91f2a198962013e5b324c7a8a4b1946e19332b15198c957c3e1b097e7734036b2ab09
|
7
|
+
data.tar.gz: 2783c22ba9362a9285e4e80369565a8be0695feb910325f77233b43060826dfffafd03e74c2c4cdcb554e6246eba1e42441b7b2f94d7ba0748cda140effee044
|
data/README.md
CHANGED
@@ -1,16 +1,13 @@
|
|
1
|
-
# Vorpal [![Build Status](https://travis-ci.
|
1
|
+
# Vorpal [![Build Status](https://travis-ci.com/nulogy/vorpal.svg?branch=main)](https://travis-ci.com/nulogy/vorpal) [![Code Climate](https://codeclimate.com/github/nulogy/vorpal/badges/gpa.svg)](https://codeclimate.com/github/nulogy/vorpal) [![Code Coverage](https://codecov.io/gh/nulogy/vorpal/branch/main/graph/badge.svg)](https://codecov.io/gh/nulogy/vorpal/branch/main)
|
2
2
|
|
3
3
|
Separate your domain model from your persistence mechanism. Some problems call for a really sharp tool.
|
4
4
|
|
5
|
-
|
6
|
-
>
|
7
|
-
|
8
|
-
> The vorpal blade went snicker-snack!
|
9
|
-
|
10
|
-
> He left it dead, and with its head
|
11
|
-
|
5
|
+
> One, two! One, two! and through and through<br/>
|
6
|
+
> The vorpal blade went snicker-snack!<br/>
|
7
|
+
> He left it dead, and with its head<br/>
|
12
8
|
> He went galumphing back.
|
13
9
|
|
10
|
+
\- [Jabberwocky](https://www.poetryfoundation.org/poems/42916/jabberwocky) by Lewis Carroll
|
14
11
|
|
15
12
|
## Overview
|
16
13
|
Vorpal is a [Data Mapper](http://martinfowler.com/eaaCatalog/dataMapper.html)-style ORM (object relational mapper) framelet that persists POROs (plain old Ruby objects) to a relational DB. It has been heavily influenced by concepts from [Domain Driven Design](http://www.infoq.com/minibooks/domain-driven-design-quickly).
|
@@ -48,27 +45,21 @@ Or install it yourself as:
|
|
48
45
|
Start with a domain model of POROs and AR::Base objects that form an aggregate:
|
49
46
|
|
50
47
|
```ruby
|
51
|
-
class Tree; end
|
52
|
-
|
53
48
|
class Branch
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
attribute :diameter, Decimal
|
59
|
-
attribute :tree, Tree
|
49
|
+
attr_accessor :id
|
50
|
+
attr_accessor :length
|
51
|
+
attr_accessor :diameter
|
52
|
+
attr_accessor :tree
|
60
53
|
end
|
61
54
|
|
62
|
-
class Gardener
|
55
|
+
class Gardener
|
63
56
|
end
|
64
57
|
|
65
58
|
class Tree
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
attribute :gardener, Gardener
|
71
|
-
attribute :branches, Array[Branch]
|
59
|
+
attr_accessor :id
|
60
|
+
attr_accessor :name
|
61
|
+
attr_accessor :gardener
|
62
|
+
attr_accessor :branches
|
72
63
|
end
|
73
64
|
```
|
74
65
|
|
@@ -143,7 +134,8 @@ module TreeRepository
|
|
143
134
|
end
|
144
135
|
```
|
145
136
|
|
146
|
-
Here we've used the `owned` flag on the `belongs_to` from the Tree to the Gardener to show
|
137
|
+
Here we've used the `owned: false` flag on the `belongs_to` from the Tree to the Gardener to show
|
138
|
+
that the Gardener is on the aggregate boundary.
|
147
139
|
|
148
140
|
And use it:
|
149
141
|
|
@@ -164,9 +156,33 @@ TreeRepository.destroy(dead_tree)
|
|
164
156
|
TreeRepository.destroy_by_id(dead_tree_id)
|
165
157
|
```
|
166
158
|
|
159
|
+
### Ids
|
160
|
+
|
161
|
+
Vorpal by default will use auto-incrementing Integers from a DB sequence for ids. However, UUID v4 ids are also
|
162
|
+
supported:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
Vorpal.define do
|
166
|
+
# UUID v4 id!
|
167
|
+
map Tree, primary_key_type: :uuid do
|
168
|
+
# ..
|
169
|
+
end
|
170
|
+
|
171
|
+
# Also a UUID v4 id, the Rails Way!
|
172
|
+
map Trunk, id: :uuid do
|
173
|
+
# ..
|
174
|
+
end
|
175
|
+
|
176
|
+
# If you feel the need to specify an auto-incrementing integer id.
|
177
|
+
map Branch, primary_key_type: :serial do
|
178
|
+
# ..
|
179
|
+
end
|
180
|
+
end
|
181
|
+
```
|
182
|
+
|
167
183
|
## API Documentation
|
168
184
|
|
169
|
-
http://rubydoc.info/github/nulogy/vorpal/
|
185
|
+
http://rubydoc.info/github/nulogy/vorpal/main/frames
|
170
186
|
|
171
187
|
## Caveats
|
172
188
|
It also does not do some things that you might expect from other ORMs:
|
@@ -174,7 +190,7 @@ It also does not do some things that you might expect from other ORMs:
|
|
174
190
|
1. No lazy loading of associations. This might sound like a big deal, but with [correctly designed aggregates](http://dddcommunity.org/library/vernon_2011/) it turns out not to be.
|
175
191
|
1. No managing of transactions. It is the strong opinion of the authors that managing transactions is an application-level concern.
|
176
192
|
1. No support for validations. Validations are not a persistence concern.
|
177
|
-
1. No AR-style callbacks. Use Infrastructure, Application, or Domain
|
193
|
+
1. No AR-style callbacks. Use [Infrastructure, Application, or Domain services](http://martinfowler.com/bliki/EvansClassification.html) instead.
|
178
194
|
1. No has-many-through associations. Use two has-many associations to a join entity instead.
|
179
195
|
1. The `id` attribute is reserved for database primary keys. If you have a natural key/id on your domain model, name it something that makes sense for your domain. It is the strong opinion of the authors that using natural keys as foreign keys is a bad idea. This mixes domain and persistence concerns.
|
180
196
|
|
@@ -183,13 +199,10 @@ It also does not do some things that you might expect from other ORMs:
|
|
183
199
|
1. Only supports PostgreSQL.
|
184
200
|
|
185
201
|
## Future Enhancements
|
186
|
-
*
|
187
|
-
* Support for other DBMSs (no MySQL support until ids can be generated without inserting into a table!)
|
188
|
-
* Support for other ORMs.
|
189
|
-
* Value objects.
|
190
|
-
* Remove dependency on ActiveRecord (optimistic locking? updated_at, created_at support? Data type conversions? TimeZone support?)
|
191
|
-
* More efficient updates (use fewer queries.)
|
202
|
+
* Support for clients to set UUID-based ids.
|
192
203
|
* Nicer DSL for specifying attributes that have different names in the domain model than in the DB.
|
204
|
+
* Aggregate updated_at.
|
205
|
+
* Better support for value objects.
|
193
206
|
|
194
207
|
## FAQ
|
195
208
|
|
@@ -205,11 +218,12 @@ It also does not do some things that you might expect from other ORMs:
|
|
205
218
|
|
206
219
|
**A.** Create a method on a [Repository](http://martinfowler.com/eaaCatalog/repository.html)! They have full access to the DB/ORM so you can use [Arel](https://github.com/rails/arel) and go [crazy](http://asciicasts.com/episodes/239-activerecord-relation-walkthrough) or use direct SQL if you want.
|
207
220
|
|
208
|
-
For example:
|
221
|
+
For example, use the [#query](https://rubydoc.info/github/nulogy/vorpal/main/Vorpal/AggregateMapper#query-instance_method) method on the [AggregateMapper](https://rubydoc.info/github/nulogy/vorpal/main/Vorpal/AggregateMapper) to access the underyling [ActiveRecordRelation](https://api.rubyonrails.org/classes/ActiveRecord/Relation.html):
|
209
222
|
|
210
223
|
```ruby
|
211
|
-
def
|
212
|
-
|
224
|
+
def find_special_ones
|
225
|
+
# use `load_all` or `load_one` to convert from ActiveRecord objects to domain POROs.
|
226
|
+
@mapper.query.where(special: true).load_all
|
213
227
|
end
|
214
228
|
```
|
215
229
|
|
@@ -231,6 +245,10 @@ For example:
|
|
231
245
|
|
232
246
|
**A.** You can use [ActiveModel::Serialization](http://api.rubyonrails.org/classes/ActiveModel/Serialization.html) or [ActiveModel::Serializers](https://github.com/rails-api/active_model_serializers) but they are not heartily recommended. The former is too coupled to the model and the latter is too coupled to Rails controllers. Vorpal uses [SimpleSerializer](https://github.com/nulogy/simple_serializer) for this purpose.
|
233
247
|
|
248
|
+
**Q.** Are `updated_at` and `created_at` supported?
|
249
|
+
|
250
|
+
**A.** Yes. If they exist on your database tables, they will behave exactly as if you were using vanilla ActiveRecord.
|
251
|
+
|
234
252
|
## Contributing
|
235
253
|
|
236
254
|
1. Fork it ( https://github.com/nulogy/vorpal/fork )
|
@@ -239,33 +257,31 @@ For example:
|
|
239
257
|
4. Push to the branch (`git push origin my-new-feature`)
|
240
258
|
5. Create a new Pull Request
|
241
259
|
|
242
|
-
|
243
|
-
|
244
|
-
Using this gem's bin stubs (contained in the `bin` dir) is much easier if [DirEnv](https://github.com/direnv/direnv) is installed.
|
245
|
-
|
246
|
-
On OSX using ZSH DirEnv can be installed like so:
|
247
|
-
|
248
|
-
1. `brew install direnv`
|
249
|
-
2. `echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc`
|
260
|
+
## OSX Environment setup
|
250
261
|
|
251
|
-
|
262
|
+
1. Install [Homebrew](https://brew.sh/)
|
263
|
+
2. Install [rbenv](https://github.com/rbenv/rbenv#installation) ([RVM](https://rvm.io/) can work too)
|
264
|
+
3. Install [DirEnv](https://direnv.net/docs/installation.html) (`brew install direnv`)
|
265
|
+
4. Install Docker Desktop Community Edition (`brew cask install docker`)
|
266
|
+
5. Start Docker Desktop Community Edition (`CMD+space docker ENTER`)
|
267
|
+
6. Install Ruby (`rbenv install 2.7.0`)
|
268
|
+
7. Install PostgreSQL (`brew install postgresql`)
|
269
|
+
8. Clone the repo (`git clone git@github.com:nulogy/vorpal.git`) and `cd` to the project root.
|
270
|
+
8. Copy the contents of `gemfiles/rails_<version>.gemfile.lock` into a `Gemfile.lock` file
|
271
|
+
at the root of the project. (`cp gemfiles/rails_6_0.gemfile.lock gemfile.lock`)
|
272
|
+
9. `bundle`
|
252
273
|
|
253
274
|
### Running Tests
|
254
275
|
|
255
|
-
1. Start a PostgreSQL server
|
256
|
-
|
257
|
-
|
258
|
-
* Modify `spec/helpers/db_helpers.rb`.
|
259
|
-
3. Run `rake` from the terminal.
|
276
|
+
1. Start a PostgreSQL server using `docker-compose up`
|
277
|
+
3. Run `rake` from the terminal to run all specs or `rspec <path to spec file>` to
|
278
|
+
run a single spec.
|
260
279
|
|
261
|
-
### Running Tests for
|
280
|
+
### Running Tests for a specific version of Rails
|
262
281
|
|
263
|
-
1. Start a PostgreSQL server
|
264
|
-
2.
|
265
|
-
|
266
|
-
* Modify `spec/helpers/db_helpers.rb`.
|
267
|
-
3. Run `appraisal <rails version> rake` from the terminal.
|
268
|
-
* Where `<rails version>` is one of the options defined in the `./Appraisal` file.
|
282
|
+
1. Start a PostgreSQL server using `docker-compose up`
|
283
|
+
2. Run `appraisal rails-5-2 rake` from the terminal to run all specs or
|
284
|
+
`appraisal rails-5-2 rspec <path to spec file>` to run a single spec.
|
269
285
|
|
270
286
|
Please see the [Appraisal gem docs](https://github.com/thoughtbot/appraisal) for more information.
|
271
287
|
|
@@ -84,6 +84,17 @@ module Vorpal
|
|
84
84
|
@engine
|
85
85
|
end
|
86
86
|
|
87
|
+
# Returns a 'Vorpal-aware' [ActiveRecord::Relation](https://api.rubyonrails.org/classes/ActiveRecord/Relation.html)
|
88
|
+
# for the ActiveRecord object underlying the domain entity mapped by this mapper.
|
89
|
+
#
|
90
|
+
# This method allows you to easily access the power of ActiveRecord::Relation to do more complex
|
91
|
+
# queries in your repositories.
|
92
|
+
#
|
93
|
+
# The ActiveRecord::Relation is 'Vorpal-aware' because it has the {#load_one} and {#load_many} methods
|
94
|
+
# mixed in so that you can get the POROs from your domain model instead of the ActiveRecord
|
95
|
+
# objects normally returned by ActiveRecord::Relation.
|
96
|
+
#
|
97
|
+
# @return [ActiveRecord::Relation]
|
87
98
|
def query
|
88
99
|
@engine.query(@domain_class)
|
89
100
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'equalizer'
|
2
|
+
|
3
|
+
module Vorpal
|
4
|
+
module Config
|
5
|
+
# @private
|
6
|
+
class ClassConfig
|
7
|
+
include Equalizer.new(:domain_class, :db_class)
|
8
|
+
attr_reader :serializer, :deserializer, :domain_class, :db_class, :primary_key_type, :local_association_configs
|
9
|
+
attr_accessor :has_manys, :belongs_tos, :has_ones
|
10
|
+
|
11
|
+
ALLOWED_PRIMARY_KEY_TYPE_OPTIONS = [:serial, :uuid]
|
12
|
+
|
13
|
+
def initialize(attrs)
|
14
|
+
@has_manys = []
|
15
|
+
@belongs_tos = []
|
16
|
+
@has_ones = []
|
17
|
+
@local_association_configs = []
|
18
|
+
|
19
|
+
@serializer = attrs[:serializer]
|
20
|
+
@deserializer = attrs[:deserializer]
|
21
|
+
@domain_class = attrs[:domain_class]
|
22
|
+
@db_class = attrs[:db_class]
|
23
|
+
@primary_key_type = attrs[:primary_key_type]
|
24
|
+
raise "Invalid primary_key_type: '#{@primary_key_type}'" unless ALLOWED_PRIMARY_KEY_TYPE_OPTIONS.include?(@primary_key_type)
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_db_object(attributes)
|
28
|
+
db_class.new(attributes)
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_db_object_attributes(db_object, attributes)
|
32
|
+
db_object.attributes = attributes
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_db_object_attributes(db_object)
|
36
|
+
symbolize_keys(db_object.attributes)
|
37
|
+
end
|
38
|
+
|
39
|
+
def serialization_required?
|
40
|
+
domain_class.superclass.name != 'ActiveRecord::Base'
|
41
|
+
end
|
42
|
+
|
43
|
+
def serialize(object)
|
44
|
+
serializer.serialize(object)
|
45
|
+
end
|
46
|
+
|
47
|
+
def deserialize(db_object)
|
48
|
+
attributes = get_db_object_attributes(db_object)
|
49
|
+
serialization_required? ? deserializer.deserialize(domain_class.new, attributes) : db_object
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_attribute(db_object, attribute, value)
|
53
|
+
db_object.send("#{attribute}=", value)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_attribute(db_object, attribute)
|
57
|
+
db_object.send(attribute)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def symbolize_keys(hash)
|
63
|
+
result = {}
|
64
|
+
hash.each_key do |key|
|
65
|
+
result[key.to_sym] = hash[key]
|
66
|
+
end
|
67
|
+
result
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/vorpal/configs.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'vorpal/util/hash_initialization'
|
2
2
|
require 'vorpal/exceptions'
|
3
|
+
require 'vorpal/config/class_config'
|
3
4
|
require 'equalizer'
|
4
5
|
|
5
6
|
module Vorpal
|
6
7
|
# @private
|
7
|
-
class
|
8
|
-
def initialize
|
9
|
-
@class_configs =
|
10
|
-
initialize_association_configs
|
8
|
+
class MainConfig
|
9
|
+
def initialize
|
10
|
+
@class_configs = []
|
11
11
|
end
|
12
12
|
|
13
13
|
def config_for(clazz)
|
@@ -20,7 +20,9 @@ module Vorpal
|
|
20
20
|
@class_configs.detect { |conf| conf.db_class == db_object.class }
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
def add_class_config(class_config)
|
24
|
+
@class_configs << class_config
|
25
|
+
end
|
24
26
|
|
25
27
|
def initialize_association_configs
|
26
28
|
association_configs = {}
|
@@ -50,6 +52,8 @@ module Vorpal
|
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
55
|
+
private
|
56
|
+
|
53
57
|
def build_association_config(association_configs, local_config, fk, fk_type)
|
54
58
|
association_config = AssociationConfig.new(local_config, fk, fk_type)
|
55
59
|
if association_configs[association_config]
|
@@ -126,67 +130,6 @@ module Vorpal
|
|
126
130
|
end
|
127
131
|
end
|
128
132
|
|
129
|
-
# @private
|
130
|
-
class ClassConfig
|
131
|
-
include Equalizer.new(:domain_class, :db_class)
|
132
|
-
attr_reader :serializer, :deserializer, :domain_class, :db_class, :local_association_configs
|
133
|
-
attr_accessor :has_manys, :belongs_tos, :has_ones
|
134
|
-
|
135
|
-
def initialize(attrs)
|
136
|
-
@has_manys = []
|
137
|
-
@belongs_tos = []
|
138
|
-
@has_ones = []
|
139
|
-
@local_association_configs = []
|
140
|
-
|
141
|
-
attrs.each do |k,v|
|
142
|
-
instance_variable_set("@#{k}", v)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def build_db_object(attributes)
|
147
|
-
db_class.new(attributes)
|
148
|
-
end
|
149
|
-
|
150
|
-
def set_db_object_attributes(db_object, attributes)
|
151
|
-
db_object.attributes = attributes
|
152
|
-
end
|
153
|
-
|
154
|
-
def get_db_object_attributes(db_object)
|
155
|
-
symbolize_keys(db_object.attributes)
|
156
|
-
end
|
157
|
-
|
158
|
-
def serialization_required?
|
159
|
-
domain_class.superclass.name != 'ActiveRecord::Base'
|
160
|
-
end
|
161
|
-
|
162
|
-
def serialize(object)
|
163
|
-
serializer.serialize(object)
|
164
|
-
end
|
165
|
-
|
166
|
-
def deserialize(db_object)
|
167
|
-
attributes = get_db_object_attributes(db_object)
|
168
|
-
serialization_required? ? deserializer.deserialize(domain_class.new, attributes) : db_object
|
169
|
-
end
|
170
|
-
|
171
|
-
def set_attribute(db_object, attribute, value)
|
172
|
-
db_object.send("#{attribute}=", value)
|
173
|
-
end
|
174
|
-
|
175
|
-
def get_attribute(db_object, attribute)
|
176
|
-
db_object.send(attribute)
|
177
|
-
end
|
178
|
-
|
179
|
-
private
|
180
|
-
|
181
|
-
def symbolize_keys(hash)
|
182
|
-
result = {}
|
183
|
-
hash.each_key do |key|
|
184
|
-
result[key.to_sym] = hash[key]
|
185
|
-
end
|
186
|
-
result
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
133
|
# @private
|
191
134
|
class ForeignKeyInfo
|
192
135
|
include Equalizer.new(:fk_column, :fk_type_column, :fk_type)
|
@@ -9,7 +9,12 @@ module Vorpal
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def insert(db_class, db_objects)
|
12
|
-
if
|
12
|
+
if ActiveRecord::VERSION::MAJOR >= 6
|
13
|
+
return if db_objects.empty?
|
14
|
+
|
15
|
+
update_timestamps_on_create(db_class, db_objects)
|
16
|
+
db_class.insert_all!(db_objects.map(&:attributes))
|
17
|
+
elsif defined? ActiveRecord::Import
|
13
18
|
db_class.import(db_objects, validate: false)
|
14
19
|
else
|
15
20
|
db_objects.each do |db_object|
|
@@ -19,8 +24,15 @@ module Vorpal
|
|
19
24
|
end
|
20
25
|
|
21
26
|
def update(db_class, db_objects)
|
22
|
-
|
23
|
-
|
27
|
+
if ActiveRecord::VERSION::MAJOR >= 6
|
28
|
+
return if db_objects.empty?
|
29
|
+
|
30
|
+
update_timestamps_on_update(db_class, db_objects)
|
31
|
+
db_class.upsert_all(db_objects.map(&:attributes))
|
32
|
+
else
|
33
|
+
db_objects.each do |db_object|
|
34
|
+
db_object.save!(validate: false)
|
35
|
+
end
|
24
36
|
end
|
25
37
|
end
|
26
38
|
|
@@ -100,6 +112,30 @@ module Vorpal
|
|
100
112
|
|
101
113
|
private
|
102
114
|
|
115
|
+
# Adapted from https://github.com/rails/rails/blob/614580270d7789e5275defc3da020ce27b3b2302/activerecord/lib/active_record/timestamp.rb#L99
|
116
|
+
def update_timestamps_on_create(db_class, db_objects)
|
117
|
+
return unless db_class.record_timestamps
|
118
|
+
|
119
|
+
current_time = db_class.current_time_from_proper_timezone
|
120
|
+
db_objects.each do |db_object|
|
121
|
+
db_class.all_timestamp_attributes_in_model.each do |column|
|
122
|
+
db_object.write_attribute(column, current_time) unless db_object.read_attribute(column)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
#Adapted from https://github.com/rails/rails/blob/614580270d7789e5275defc3da020ce27b3b2302/activerecord/lib/active_record/timestamp.rb#L111
|
128
|
+
def update_timestamps_on_update(db_class, db_objects)
|
129
|
+
return unless db_class.record_timestamps
|
130
|
+
|
131
|
+
current_time = db_class.current_time_from_proper_timezone
|
132
|
+
db_objects.each do |db_object|
|
133
|
+
db_class.timestamp_attributes_for_update_in_model.each do |column|
|
134
|
+
db_object.write_attribute(column, current_time)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
103
139
|
def sequence_name(db_class)
|
104
140
|
@sequence_names[db_class] ||= execute(
|
105
141
|
"SELECT substring(column_default from '''(.*)''') FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1 AND column_name = 'id' LIMIT 1",
|