vorpal 0.1.0.rc3 → 1.1.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 +5 -5
- data/README.md +50 -33
- data/lib/vorpal/aggregate_mapper.rb +11 -0
- data/lib/vorpal/db_loader.rb +3 -4
- data/lib/vorpal/driver/postgresql.rb +181 -0
- data/lib/vorpal/dsl/config_builder.rb +37 -22
- data/lib/vorpal/dsl/configuration.rb +5 -4
- data/lib/vorpal/dsl/defaults_generator.rb +14 -4
- data/lib/vorpal/engine.rb +7 -8
- data/lib/vorpal/identity_map.rb +15 -14
- data/lib/vorpal/version.rb +1 -1
- data/vorpal.gemspec +11 -7
- metadata +57 -65
- data/.editorconfig +0 -13
- data/.gitignore +0 -16
- data/.rspec +0 -1
- data/.ruby-version +0 -1
- data/.yardopts +0 -1
- data/Gemfile +0 -4
- data/Rakefile +0 -10
- data/lib/vorpal/db_driver.rb +0 -143
- data/spec/helpers/db_helpers.rb +0 -51
- 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/db_driver_spec.rb +0 -42
- data/spec/vorpal/performance/performance_spec.rb +0 -195
- 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 -19
- 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
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c866f3e83daac73f28e5c9a53875b73d7f8db95cbf437200f0b0c69cce81d317
|
4
|
+
data.tar.gz: e1e91f848552e1f3710849370978d4a1e322e65b12c65bebcd995b76960ba44e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5bcd5518f56b4c43f4180d3f63c75c6186c1bd89bee939b8410e656f3df411920238eaf51da4a590367f929507c3bdb1ada98cf972d6ebf6785432417018b9a2
|
7
|
+
data.tar.gz: 85e53e6946ea0d0f06e14d8913ac3eba45668b3d08bfc0036a789620ea05af587cb5a97991467eadfea1f76d5dd3fccaf71c96aeec2345327d9538e714e0e720
|
data/README.md
CHANGED
@@ -1,16 +1,13 @@
|
|
1
|
-
# Vorpal [](https://travis-ci.com/nulogy/vorpal) [](https://codeclimate.com/github/nulogy/vorpal) [](https://codecov.io/gh/nulogy/vorpal/branch/master)
|
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).
|
@@ -45,8 +42,6 @@ Or install it yourself as:
|
|
45
42
|
|
46
43
|
## Usage
|
47
44
|
|
48
|
-
**Warning! API still in flux! Expect it to change with every release until 0.1.0. After this point, semantic versioning will be used.**
|
49
|
-
|
50
45
|
Start with a domain model of POROs and AR::Base objects that form an aggregate:
|
51
46
|
|
52
47
|
```ruby
|
@@ -145,7 +140,8 @@ module TreeRepository
|
|
145
140
|
end
|
146
141
|
```
|
147
142
|
|
148
|
-
Here we've used the `owned` flag on the `belongs_to` from the Tree to the Gardener to show
|
143
|
+
Here we've used the `owned: false` flag on the `belongs_to` from the Tree to the Gardener to show
|
144
|
+
that the Gardener is on the aggregate boundary.
|
149
145
|
|
150
146
|
And use it:
|
151
147
|
|
@@ -176,7 +172,7 @@ It also does not do some things that you might expect from other ORMs:
|
|
176
172
|
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.
|
177
173
|
1. No managing of transactions. It is the strong opinion of the authors that managing transactions is an application-level concern.
|
178
174
|
1. No support for validations. Validations are not a persistence concern.
|
179
|
-
1. No AR-style callbacks. Use Infrastructure, Application, or Domain
|
175
|
+
1. No AR-style callbacks. Use [Infrastructure, Application, or Domain services](http://martinfowler.com/bliki/EvansClassification.html) instead.
|
180
176
|
1. No has-many-through associations. Use two has-many associations to a join entity instead.
|
181
177
|
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.
|
182
178
|
|
@@ -185,13 +181,11 @@ It also does not do some things that you might expect from other ORMs:
|
|
185
181
|
1. Only supports PostgreSQL.
|
186
182
|
|
187
183
|
## Future Enhancements
|
188
|
-
*
|
189
|
-
* Support for other DBMSs (no MySQL support until ids can be generated without inserting into a table!)
|
190
|
-
* Support for other ORMs.
|
191
|
-
* Value objects.
|
192
|
-
* Remove dependency on ActiveRecord (optimistic locking? updated_at, created_at support? Data type conversions? TimeZone support?)
|
193
|
-
* More efficient updates (use fewer queries.)
|
184
|
+
* Support for UUID primary keys.
|
194
185
|
* Nicer DSL for specifying attributes that have different names in the domain model than in the DB.
|
186
|
+
* Show how to implement POROs without using Virtus (it is unsupported and can be crazy slow)
|
187
|
+
* Aggregate updated_at.
|
188
|
+
* Better support for value objects.
|
195
189
|
|
196
190
|
## FAQ
|
197
191
|
|
@@ -207,11 +201,12 @@ It also does not do some things that you might expect from other ORMs:
|
|
207
201
|
|
208
202
|
**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.
|
209
203
|
|
210
|
-
For example:
|
204
|
+
For example, use the [#query](https://rubydoc.info/github/nulogy/vorpal/master/Vorpal/AggregateMapper#query-instance_method) method on the [AggregateMapper](https://rubydoc.info/github/nulogy/vorpal/master/Vorpal/AggregateMapper) to access the underyling [ActiveRecordRelation](https://api.rubyonrails.org/classes/ActiveRecord/Relation.html):
|
211
205
|
|
212
206
|
```ruby
|
213
|
-
def
|
214
|
-
|
207
|
+
def find_special_ones
|
208
|
+
# use `load_all` or `load_one` to convert from ActiveRecord objects to domain POROs.
|
209
|
+
@mapper.query.where(special: true).load_all
|
215
210
|
end
|
216
211
|
```
|
217
212
|
|
@@ -233,19 +228,9 @@ For example:
|
|
233
228
|
|
234
229
|
**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.
|
235
230
|
|
236
|
-
|
237
|
-
|
238
|
-
1. Start a PostgreSQL server.
|
239
|
-
2. Either:
|
240
|
-
* Create a DB user called `vorpal` with password `pass`. OR:
|
241
|
-
* Modify `spec/helpers/db_helpers.rb`.
|
242
|
-
3. Run `rake` from the terminal.
|
243
|
-
|
244
|
-
## Contributors
|
231
|
+
**Q.** Are `updated_at` and `created_at` supported?
|
245
232
|
|
246
|
-
|
247
|
-
* [Paul Sobocinski](https://github.com/psobocinski)
|
248
|
-
* [Jason Cheong-Kee-You](https://github.com/jchunky)
|
233
|
+
**A.** Yes. If they exist on your database tables, they will behave exactly as if you were using vanilla ActiveRecord.
|
249
234
|
|
250
235
|
## Contributing
|
251
236
|
|
@@ -254,3 +239,35 @@ For example:
|
|
254
239
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
255
240
|
4. Push to the branch (`git push origin my-new-feature`)
|
256
241
|
5. Create a new Pull Request
|
242
|
+
|
243
|
+
## OSX Environment setup
|
244
|
+
|
245
|
+
1. Install [Homebrew](https://brew.sh/)
|
246
|
+
2. Install [rbenv](https://github.com/rbenv/rbenv#installation) ([RVM](https://rvm.io/) can work too)
|
247
|
+
3. Install [DirEnv](https://direnv.net/docs/installation.html) (`brew install direnv`)
|
248
|
+
4. Install Docker Desktop Community Edition (`brew cask install docker`)
|
249
|
+
5. Start Docker Desktop Community Edition (`CMD+space docker ENTER`)
|
250
|
+
6. Install Ruby (`rbenv install 2.7.0`)
|
251
|
+
7. Install PostgreSQL (`brew install postgresql`)
|
252
|
+
8. Clone the repo (`git clone git@github.com:nulogy/vorpal.git`) and `cd` to the project root.
|
253
|
+
8. Copy the contents of `gemfiles/rails_<version>.gemfile.lock` into a `Gemfile.lock` file
|
254
|
+
at the root of the project. (`cp gemfiles/rails_6_0.gemfile.lock gemfile.lock`)
|
255
|
+
9. `bundle`
|
256
|
+
|
257
|
+
### Running Tests
|
258
|
+
|
259
|
+
1. Start a PostgreSQL server using `docker-compose up`
|
260
|
+
3. Run `rake` from the terminal to run all specs or `rspec <path to spec file>` to
|
261
|
+
run a single spec.
|
262
|
+
|
263
|
+
### Running Tests for a specific version of Rails
|
264
|
+
|
265
|
+
1. Start a PostgreSQL server using `docker-compose up`
|
266
|
+
2. Run `appraisal rails-5-2 rake` from the terminal to run all specs or
|
267
|
+
`appraisal rails-5-2 rspec <path to spec file>` to run a single spec.
|
268
|
+
|
269
|
+
Please see the [Appraisal gem docs](https://github.com/thoughtbot/appraisal) for more information.
|
270
|
+
|
271
|
+
## Contributors
|
272
|
+
|
273
|
+
See who's [contributed](https://github.com/nulogy/vorpal/graphs/contributors)!
|
@@ -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
|
data/lib/vorpal/db_loader.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'vorpal/loaded_objects'
|
2
2
|
require 'vorpal/util/array_hash'
|
3
|
-
require 'vorpal/db_driver'
|
4
3
|
|
5
4
|
module Vorpal
|
6
5
|
# Handles loading of objects from the database.
|
@@ -13,7 +12,7 @@ module Vorpal
|
|
13
12
|
end
|
14
13
|
|
15
14
|
def load_from_db(ids, config)
|
16
|
-
db_roots = @db_driver.load_by_id(config, ids)
|
15
|
+
db_roots = @db_driver.load_by_id(config.db_class, ids)
|
17
16
|
load_from_db_objects(db_roots, config)
|
18
17
|
end
|
19
18
|
|
@@ -123,7 +122,7 @@ module Vorpal
|
|
123
122
|
|
124
123
|
def load_all(db_driver)
|
125
124
|
return [] if @ids.empty?
|
126
|
-
db_driver.load_by_id(@config, @ids)
|
125
|
+
db_driver.load_by_id(@config.db_class, @ids)
|
127
126
|
end
|
128
127
|
end
|
129
128
|
|
@@ -138,7 +137,7 @@ module Vorpal
|
|
138
137
|
|
139
138
|
def load_all(db_driver)
|
140
139
|
return [] if @fk_values.empty?
|
141
|
-
db_driver.load_by_foreign_key(@config, @fk_values, @fk_info)
|
140
|
+
db_driver.load_by_foreign_key(@config.db_class, @fk_values, @fk_info)
|
142
141
|
end
|
143
142
|
end
|
144
143
|
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'vorpal/util/string_utils.rb'
|
2
|
+
|
3
|
+
module Vorpal
|
4
|
+
module Driver
|
5
|
+
# Interface between the database and Vorpal for PostgreSQL using ActiveRecord.
|
6
|
+
class Postgresql
|
7
|
+
def initialize
|
8
|
+
@sequence_names = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def insert(db_class, db_objects)
|
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
|
18
|
+
db_class.import(db_objects, validate: false)
|
19
|
+
else
|
20
|
+
db_objects.each do |db_object|
|
21
|
+
db_object.save!(validate: false)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def update(db_class, db_objects)
|
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
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def destroy(db_class, ids)
|
40
|
+
db_class.where(id: ids).delete_all
|
41
|
+
end
|
42
|
+
|
43
|
+
# Loads instances of the given class by primary key.
|
44
|
+
#
|
45
|
+
# @param db_class [Class] A subclass of ActiveRecord::Base
|
46
|
+
# @return [[Object]] An array of entities.
|
47
|
+
def load_by_id(db_class, ids)
|
48
|
+
db_class.where(id: ids).to_a
|
49
|
+
end
|
50
|
+
|
51
|
+
# Loads instances of the given class whose foreign key has the given value.
|
52
|
+
#
|
53
|
+
# @param db_class [Class] A subclass of ActiveRecord::Base
|
54
|
+
# @param id [Integer] The value of the foreign key to find by. (Can also be an array of ids.)
|
55
|
+
# @param foreign_key_info [ForeignKeyInfo] Meta data for the foreign key.
|
56
|
+
# @return [[Object]] An array of entities.
|
57
|
+
def load_by_foreign_key(db_class, id, foreign_key_info)
|
58
|
+
arel = db_class.where(foreign_key_info.fk_column => id)
|
59
|
+
arel = arel.where(foreign_key_info.fk_type_column => foreign_key_info.fk_type) if foreign_key_info.polymorphic?
|
60
|
+
arel.to_a
|
61
|
+
end
|
62
|
+
|
63
|
+
# Fetches primary key values to be used for new entities.
|
64
|
+
#
|
65
|
+
# @param db_class [Class] A subclass of ActiveRecord::Base
|
66
|
+
# @return [[Integer]] An array of unused primary keys.
|
67
|
+
def get_primary_keys(db_class, count)
|
68
|
+
result = execute("select nextval($1) from generate_series(1,$2);", [sequence_name(db_class), count])
|
69
|
+
result.rows.map(&:first).map(&:to_i)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Builds an ORM Class for accessing data in the given DB table.
|
73
|
+
#
|
74
|
+
# @param model_class [Class] The PORO class that we are creating a DB interface class for.
|
75
|
+
# @param table_name [String] Name of the DB table the DB class should interface with.
|
76
|
+
# @return [Class] ActiveRecord::Base Class
|
77
|
+
def build_db_class(model_class, table_name)
|
78
|
+
db_class = Class.new(ActiveRecord::Base) do
|
79
|
+
class << self
|
80
|
+
# This is overridden for two reasons:
|
81
|
+
# 1) For anonymous classes, #name normally returns nil. Class names in Ruby come from the
|
82
|
+
# name of the constant they are assigned to.
|
83
|
+
# 2) Because the default implementation for Class#name for anonymous classes is very, very
|
84
|
+
# slow. https://bugs.ruby-lang.org/issues/11119
|
85
|
+
# Remove this override once #2 has been fixed!
|
86
|
+
def name
|
87
|
+
@name ||= "Vorpal_generated_ActiveRecord__Base_class_for_#{vorpal_model_class_name}"
|
88
|
+
end
|
89
|
+
|
90
|
+
# Overridden because, like #name, the default implementation for anonymous classes is very,
|
91
|
+
# very slow.
|
92
|
+
def to_s
|
93
|
+
name
|
94
|
+
end
|
95
|
+
|
96
|
+
attr_accessor :vorpal_model_class_name
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
db_class.vorpal_model_class_name = Util::StringUtils.escape_class_name(model_class.name)
|
101
|
+
db_class.table_name = table_name
|
102
|
+
db_class
|
103
|
+
end
|
104
|
+
|
105
|
+
# Builds a composable query object (e.g. ActiveRecord::Relation) with Vorpal methods mixed in
|
106
|
+
# for querying for instances of the given AR::Base class.
|
107
|
+
#
|
108
|
+
# @param db_class [Class] A subclass of ActiveRecord::Base
|
109
|
+
def query(db_class, aggregate_mapper)
|
110
|
+
db_class.unscoped.extending(ArelQueryMethods.new(aggregate_mapper))
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
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
|
+
|
139
|
+
def sequence_name(db_class)
|
140
|
+
@sequence_names[db_class] ||= execute(
|
141
|
+
"SELECT substring(column_default from '''(.*)''') FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1 AND column_name = 'id' LIMIT 1",
|
142
|
+
[db_class.table_name]
|
143
|
+
).rows.first.first
|
144
|
+
end
|
145
|
+
|
146
|
+
def execute(sql, binds)
|
147
|
+
binds = binds.map { |row| [nil, row] }
|
148
|
+
ActiveRecord::Base.connection.exec_query(sql, 'SQL', binds)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class ArelQueryMethods < Module
|
153
|
+
def initialize(mapper)
|
154
|
+
@mapper = mapper
|
155
|
+
end
|
156
|
+
|
157
|
+
def extended(descendant)
|
158
|
+
super
|
159
|
+
descendant.extend(Methods)
|
160
|
+
descendant.vorpal_aggregate_mapper = @mapper
|
161
|
+
end
|
162
|
+
|
163
|
+
# Methods in this module will appear on any composable
|
164
|
+
module Methods
|
165
|
+
attr_writer :vorpal_aggregate_mapper
|
166
|
+
|
167
|
+
# See {AggregateMapper#load_many}.
|
168
|
+
def load_many
|
169
|
+
db_roots = self.to_a
|
170
|
+
@vorpal_aggregate_mapper.load_many(db_roots)
|
171
|
+
end
|
172
|
+
|
173
|
+
# See {AggregateMapper#load_one}.
|
174
|
+
def load_one
|
175
|
+
db_root = self.first
|
176
|
+
@vorpal_aggregate_mapper.load_one(db_root)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -22,43 +22,58 @@ module Vorpal
|
|
22
22
|
@attributes.concat(attributes)
|
23
23
|
end
|
24
24
|
|
25
|
-
# Defines a one-to-many association
|
25
|
+
# Defines a one-to-many association to another type where the foreign key is stored on the child.
|
26
26
|
#
|
27
|
-
#
|
27
|
+
# In Object-Oriented programming, associations are *directed*. This means that they can only be
|
28
|
+
# traversed in one direction: from the type that defines the association (the one with the
|
29
|
+
# getter) to the type that is associated. They end that defines the association is called the
|
30
|
+
# 'Parent' and the end that is associated is called the 'Child'.
|
31
|
+
#
|
32
|
+
# @param name [String] Name of the association getter.
|
28
33
|
# @param options [Hash]
|
29
|
-
# @option options [Boolean] :owned
|
30
|
-
# @option options [String] :fk
|
31
|
-
# @option options [String] :fk_type
|
32
|
-
# @option options [Class] :child_class
|
34
|
+
# @option options [Boolean] :owned (True) True if the child type belongs to the aggregate. Changes to any object belonging to the aggregate will be persisted when the aggregate is persisted.
|
35
|
+
# @option options [String] :fk (Parent class name converted to snakecase and appended with a '_id') The name of the DB column on the child that contains the foreign key reference to the parent.
|
36
|
+
# @option options [String] :fk_type The name of the DB column on the child that contains the parent class name. Only needed when there is an association from the child side that is polymorphic.
|
37
|
+
# @option options [Class] :child_class (name converted to a Class) The child class.
|
33
38
|
def has_many(name, options={})
|
34
39
|
@has_manys << {name: name}.merge(options)
|
35
40
|
end
|
36
41
|
|
37
|
-
# Defines a one-to-one association
|
38
|
-
# is stored on the
|
42
|
+
# Defines a one-to-one association to another type where the foreign key
|
43
|
+
# is stored on the child.
|
44
|
+
#
|
45
|
+
# In Object-Oriented programming, associations are *directed*. This means that they can only be
|
46
|
+
# traversed in one direction: from the type that defines the association (the one with the
|
47
|
+
# getter) to the type that is associated. They end that defines the association is called the
|
48
|
+
# 'Parent' and the end that is associated is called the 'Child'.
|
39
49
|
#
|
40
|
-
# @param name [String] Name of the
|
50
|
+
# @param name [String] Name of the association getter.
|
41
51
|
# @param options [Hash]
|
42
|
-
# @option options [Boolean] :owned
|
43
|
-
# @option options [String] :fk
|
44
|
-
# @option options [String] :fk_type
|
45
|
-
# @option options [Class] :child_class
|
52
|
+
# @option options [Boolean] :owned (True) True if the child type belongs to the aggregate. Changes to any object belonging to the aggregate will be persisted when the aggregate is persisted.
|
53
|
+
# @option options [String] :fk (Parent class name converted to snakecase and appended with a '_id') The name of the DB column on the child that contains the foreign key reference to the parent.
|
54
|
+
# @option options [String] :fk_type The name of the DB column on the child that contains the parent class name. Only needed when there is an association from the child side that is polymorphic.
|
55
|
+
# @option options [Class] :child_class (name converted to a Class) The child class.
|
46
56
|
def has_one(name, options={})
|
47
57
|
@has_ones << {name: name}.merge(options)
|
48
58
|
end
|
49
59
|
|
50
|
-
# Defines a one-to-one association with another
|
51
|
-
# is stored on
|
60
|
+
# Defines a one-to-one association with another type where the foreign key
|
61
|
+
# is stored on the parent.
|
62
|
+
#
|
63
|
+
# This association can be polymorphic. I.E. children can be of different types.
|
52
64
|
#
|
53
|
-
# This
|
65
|
+
# In Object-Oriented programming, associations are *directed*. This means that they can only be
|
66
|
+
# traversed in one direction: from the type that defines the association (the one with the
|
67
|
+
# getter) to the type that is associated. They end that defines the association is called the
|
68
|
+
# 'Parent' and the end that is associated is called the 'Child'.
|
54
69
|
#
|
55
|
-
# @param name [String] Name of the
|
70
|
+
# @param name [String] Name of the association getter.
|
56
71
|
# @param options [Hash]
|
57
|
-
# @option options [Boolean] :owned
|
58
|
-
# @option options [String] :fk
|
59
|
-
# @option options [String] :fk_type
|
60
|
-
# @option options [Class] :child_class
|
61
|
-
# @option options [[Class]] :child_classes
|
72
|
+
# @option options [Boolean] :owned (True) True if the child type belongs to the aggregate. Changes to any object belonging to the aggregate will be persisted when the aggregate is persisted.
|
73
|
+
# @option options [String] :fk (Child class name converted to snakecase and appended with a '_id') The name of the DB column on the parent that contains the foreign key reference to the child.
|
74
|
+
# @option options [String] :fk_type The name of the DB column on the parent that contains the child class name. Only needed when the association is polymorphic.
|
75
|
+
# @option options [Class] :child_class (name converted to a Class) The child class.
|
76
|
+
# @option options [[Class]] :child_classes The list of possible classes that can be children. This is for polymorphic associations. Takes precedence over `:child_class`.
|
62
77
|
def belongs_to(name, options={})
|
63
78
|
@belongs_tos << {name: name}.merge(options)
|
64
79
|
end
|