terrestrial 0.3.0 → 0.5.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/.ruby-version +1 -1
- data/Gemfile.lock +44 -53
- data/README.md +3 -6
- data/bin/test +1 -1
- data/features/env.rb +12 -2
- data/features/example.feature +23 -26
- data/lib/terrestrial.rb +31 -0
- data/lib/terrestrial/adapters/abstract_adapter.rb +6 -0
- data/lib/terrestrial/adapters/memory_adapter.rb +82 -6
- data/lib/terrestrial/adapters/sequel_postgres_adapter.rb +191 -0
- data/lib/terrestrial/configurations/conventional_association_configuration.rb +65 -35
- data/lib/terrestrial/configurations/conventional_configuration.rb +280 -124
- data/lib/terrestrial/configurations/mapping_config_options_proxy.rb +97 -0
- data/lib/terrestrial/deleted_record.rb +12 -8
- data/lib/terrestrial/dirty_map.rb +17 -9
- data/lib/terrestrial/functional_pipeline.rb +64 -0
- data/lib/terrestrial/inspection_string.rb +6 -1
- data/lib/terrestrial/lazy_object_proxy.rb +1 -0
- data/lib/terrestrial/many_to_many_association.rb +34 -20
- data/lib/terrestrial/many_to_one_association.rb +11 -3
- data/lib/terrestrial/one_to_many_association.rb +9 -0
- data/lib/terrestrial/public_conveniencies.rb +65 -82
- data/lib/terrestrial/record.rb +106 -0
- data/lib/terrestrial/relation_mapping.rb +43 -12
- data/lib/terrestrial/relational_store.rb +33 -11
- data/lib/terrestrial/upsert_record.rb +54 -0
- data/lib/terrestrial/version.rb +1 -1
- data/spec/automatic_timestamps_spec.rb +339 -0
- data/spec/changes_api_spec.rb +81 -0
- data/spec/config_override_spec.rb +28 -19
- data/spec/custom_serializers_spec.rb +3 -2
- data/spec/database_default_fields_spec.rb +213 -0
- data/spec/database_generated_id_spec.rb +291 -0
- data/spec/database_owned_fields_and_timestamps_spec.rb +200 -0
- data/spec/deletion_spec.rb +1 -1
- data/spec/error_handling/factory_error_handling_spec.rb +1 -4
- data/spec/error_handling/serialization_error_spec.rb +1 -4
- data/spec/error_handling/upsert_error_spec.rb +7 -11
- data/spec/graph_persistence_spec.rb +52 -18
- data/spec/ordered_association_spec.rb +10 -12
- data/spec/predefined_queries_spec.rb +14 -12
- data/spec/readme_examples_spec.rb +1 -1
- data/spec/sequel_query_efficiency_spec.rb +19 -16
- data/spec/spec_helper.rb +6 -1
- data/spec/support/blog_schema.rb +7 -3
- data/spec/support/object_graph_setup.rb +30 -39
- data/spec/support/object_store_setup.rb +16 -196
- data/spec/support/seed_data_setup.rb +15 -149
- data/spec/support/seed_records.rb +141 -0
- data/spec/support/sequel_test_support.rb +46 -13
- data/spec/terrestrial/abstract_record_spec.rb +138 -106
- data/spec/terrestrial/adapters/sequel_postgres_adapter_spec.rb +138 -0
- data/spec/terrestrial/deleted_record_spec.rb +0 -27
- data/spec/terrestrial/dirty_map_spec.rb +52 -77
- data/spec/terrestrial/functional_pipeline_spec.rb +153 -0
- data/spec/terrestrial/inspection_string_spec.rb +61 -0
- data/spec/terrestrial/upsert_record_spec.rb +29 -0
- data/terrestrial.gemspec +7 -8
- metadata +43 -40
- data/MissingFeatures.md +0 -64
- data/lib/terrestrial/abstract_record.rb +0 -99
- data/lib/terrestrial/association_loaders.rb +0 -52
- data/lib/terrestrial/upserted_record.rb +0 -15
- data/spec/terrestrial/public_conveniencies_spec.rb +0 -63
- data/spec/terrestrial/upserted_record_spec.rb +0 -59
@@ -1,4 +1,6 @@
|
|
1
1
|
require "support/object_graph_setup"
|
2
|
+
require "support/seed_records"
|
3
|
+
|
2
4
|
RSpec.shared_context "seed data setup" do
|
3
5
|
include_context "object graph setup"
|
4
6
|
|
@@ -10,158 +12,22 @@ RSpec.shared_context "seed data setup" do
|
|
10
12
|
|
11
13
|
let(:seeded_records) {
|
12
14
|
[
|
13
|
-
[ :users, hansel_record ],
|
14
|
-
[ :users, jasper_record ],
|
15
|
-
[ :users, poppy_record ],
|
16
|
-
[ :posts, biscuits_post_record ],
|
17
|
-
[ :posts, sleep_post_record ],
|
18
|
-
[ :posts, catch_frogs_post_record ],
|
19
|
-
[ :posts, chew_up_boxes_post_record ],
|
20
|
-
[ :comments,
|
21
|
-
[ :categories, cat_biscuits_category_record ],
|
22
|
-
[ :categories, eating_and_sleeping_category_record ],
|
23
|
-
[ :categories, hunting_category_record ],
|
24
|
-
[ :categories, messing_stuff_up_category_record ],
|
25
|
-
*categories_to_posts_records.map { |record|
|
15
|
+
[ :users, SeedRecords.hansel_record ],
|
16
|
+
[ :users, SeedRecords.jasper_record ],
|
17
|
+
[ :users, SeedRecords.poppy_record ],
|
18
|
+
[ :posts, SeedRecords.biscuits_post_record ],
|
19
|
+
[ :posts, SeedRecords.sleep_post_record ],
|
20
|
+
[ :posts, SeedRecords.catch_frogs_post_record ],
|
21
|
+
[ :posts, SeedRecords.chew_up_boxes_post_record ],
|
22
|
+
[ :comments, SeedRecords.biscuits_post_comment_record ],
|
23
|
+
[ :categories, SeedRecords.cat_biscuits_category_record ],
|
24
|
+
[ :categories, SeedRecords.eating_and_sleeping_category_record ],
|
25
|
+
[ :categories, SeedRecords.hunting_category_record ],
|
26
|
+
[ :categories, SeedRecords.messing_stuff_up_category_record ],
|
27
|
+
*SeedRecords.categories_to_posts_records.map { |record|
|
26
28
|
[ :categories_to_posts, record ]
|
27
29
|
},
|
28
30
|
]
|
29
31
|
}
|
30
32
|
|
31
|
-
let(:hansel_record) {
|
32
|
-
{
|
33
|
-
id: "users/1",
|
34
|
-
first_name: "Hansel",
|
35
|
-
last_name: "Trickett",
|
36
|
-
email: "hansel@tricketts.org",
|
37
|
-
}
|
38
|
-
}
|
39
|
-
|
40
|
-
let(:jasper_record) {
|
41
|
-
{
|
42
|
-
id: "users/2",
|
43
|
-
first_name: "Jasper",
|
44
|
-
last_name: "Trickett",
|
45
|
-
email: "jasper@tricketts.org",
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
let(:poppy_record) {
|
50
|
-
{
|
51
|
-
id: "users/3",
|
52
|
-
first_name: "Poppy",
|
53
|
-
last_name: "Herzog",
|
54
|
-
email: "poppy@herzog.info",
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
|
-
let(:biscuits_post_record) {
|
59
|
-
{
|
60
|
-
id: "posts/1",
|
61
|
-
subject: "Biscuits",
|
62
|
-
body: "I like them",
|
63
|
-
author_id: "users/1",
|
64
|
-
created_at: Time.parse("2015-09-05T15:00:00+01:00"),
|
65
|
-
}
|
66
|
-
}
|
67
|
-
|
68
|
-
let(:sleep_post_record) {
|
69
|
-
{
|
70
|
-
id: "posts/2",
|
71
|
-
subject: "Sleeping",
|
72
|
-
body: "I do it three times purrr day",
|
73
|
-
author_id: "users/1",
|
74
|
-
created_at: Time.parse("2015-09-02T15:00:00+01:00"),
|
75
|
-
}
|
76
|
-
}
|
77
|
-
|
78
|
-
let(:catch_frogs_post_record) {
|
79
|
-
{
|
80
|
-
id: "posts/3",
|
81
|
-
subject: "Catching frongs",
|
82
|
-
body: "I love them while at the same time I hate them",
|
83
|
-
author_id: "users/2",
|
84
|
-
created_at: Time.parse("2015-09-03T15:00:00+01:00"),
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
|
-
let(:chew_up_boxes_post_record) {
|
89
|
-
{
|
90
|
-
id: "posts/4",
|
91
|
-
subject: "Chewing up boxes",
|
92
|
-
body: "I love them, and yet I destory them",
|
93
|
-
author_id: "users/2",
|
94
|
-
created_at: Time.parse("2015-09-10T11:00:00+01:00"),
|
95
|
-
}
|
96
|
-
}
|
97
|
-
|
98
|
-
let(:biscuits_post_comment_record) {
|
99
|
-
{
|
100
|
-
id: "comments/1",
|
101
|
-
body: "oh noes",
|
102
|
-
post_id: "posts/1",
|
103
|
-
commenter_id: "users/1",
|
104
|
-
}
|
105
|
-
}
|
106
|
-
|
107
|
-
let(:cat_biscuits_category_record) {
|
108
|
-
{
|
109
|
-
id: "categories/1",
|
110
|
-
name: "Cat biscuits",
|
111
|
-
}
|
112
|
-
}
|
113
|
-
|
114
|
-
let(:eating_and_sleeping_category_record) {
|
115
|
-
{
|
116
|
-
id: "categories/2",
|
117
|
-
name: "Eating and sleeping",
|
118
|
-
}
|
119
|
-
}
|
120
|
-
|
121
|
-
let(:hunting_category_record) {
|
122
|
-
{
|
123
|
-
id: "categories/3",
|
124
|
-
name: "Hunting",
|
125
|
-
}
|
126
|
-
}
|
127
|
-
|
128
|
-
let(:messing_stuff_up_category_record) {
|
129
|
-
{
|
130
|
-
id: "categories/4",
|
131
|
-
name: "Messing stuff up",
|
132
|
-
}
|
133
|
-
}
|
134
|
-
|
135
|
-
let(:categories_to_posts_records) {
|
136
|
-
[
|
137
|
-
{
|
138
|
-
post_id: "posts/1",
|
139
|
-
category_id: "categories/1",
|
140
|
-
},
|
141
|
-
{
|
142
|
-
post_id: "posts/1",
|
143
|
-
category_id: "categories/2",
|
144
|
-
},
|
145
|
-
{
|
146
|
-
post_id: "posts/2",
|
147
|
-
category_id: "categories/2",
|
148
|
-
},
|
149
|
-
{
|
150
|
-
post_id: "posts/3",
|
151
|
-
category_id: "categories/2",
|
152
|
-
},
|
153
|
-
{
|
154
|
-
post_id: "posts/3",
|
155
|
-
category_id: "categories/3",
|
156
|
-
},
|
157
|
-
{
|
158
|
-
post_id: "posts/4",
|
159
|
-
category_id: "categories/3",
|
160
|
-
},
|
161
|
-
{
|
162
|
-
post_id: "posts/4",
|
163
|
-
category_id: "categories/4",
|
164
|
-
},
|
165
|
-
]
|
166
|
-
}
|
167
33
|
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module SeedRecords
|
2
|
+
module_function
|
3
|
+
|
4
|
+
def hansel_record
|
5
|
+
{
|
6
|
+
id: "users/1",
|
7
|
+
first_name: "Hansel",
|
8
|
+
last_name: "Trickett",
|
9
|
+
email: "hansel@tricketts.org",
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def jasper_record
|
14
|
+
{
|
15
|
+
id: "users/2",
|
16
|
+
first_name: "Jasper",
|
17
|
+
last_name: "Trickett",
|
18
|
+
email: "jasper@tricketts.org",
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def poppy_record
|
23
|
+
{
|
24
|
+
id: "users/3",
|
25
|
+
first_name: "Poppy",
|
26
|
+
last_name: "Herzog",
|
27
|
+
email: "poppy@herzog.info",
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def biscuits_post_record
|
32
|
+
{
|
33
|
+
id: "posts/1",
|
34
|
+
subject: "Biscuits",
|
35
|
+
body: "I like them",
|
36
|
+
author_id: "users/1",
|
37
|
+
created_at: Time.parse("2015-09-02T15:00:00+01:00"),
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def sleep_post_record
|
42
|
+
{
|
43
|
+
id: "posts/2",
|
44
|
+
subject: "Sleeping",
|
45
|
+
body: "I do it three times purrr day",
|
46
|
+
author_id: "users/1",
|
47
|
+
created_at: Time.parse("2015-09-03T15:00:00+01:00"),
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def catch_frogs_post_record
|
52
|
+
{
|
53
|
+
id: "posts/3",
|
54
|
+
subject: "Catching frongs",
|
55
|
+
body: "I love them while at the same time I hate them",
|
56
|
+
author_id: "users/2",
|
57
|
+
created_at: Time.parse("2015-09-05T15:00:00+01:00"),
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def chew_up_boxes_post_record
|
62
|
+
{
|
63
|
+
id: "posts/4",
|
64
|
+
subject: "Chewing up boxes",
|
65
|
+
body: "I love them, and yet I destory them",
|
66
|
+
author_id: "users/2",
|
67
|
+
created_at: Time.parse("2015-09-10T11:00:00+01:00"),
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def biscuits_post_comment_record
|
72
|
+
{
|
73
|
+
id: "comments/1",
|
74
|
+
body: "oh noes",
|
75
|
+
post_id: "posts/1",
|
76
|
+
commenter_id: "users/1",
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def cat_biscuits_category_record
|
81
|
+
{
|
82
|
+
id: "categories/1",
|
83
|
+
name: "Cat biscuits",
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def eating_and_sleeping_category_record
|
88
|
+
{
|
89
|
+
id: "categories/2",
|
90
|
+
name: "Eating and sleeping",
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def hunting_category_record
|
95
|
+
{
|
96
|
+
id: "categories/3",
|
97
|
+
name: "Hunting",
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def messing_stuff_up_category_record
|
102
|
+
{
|
103
|
+
id: "categories/4",
|
104
|
+
name: "Messing stuff up",
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def categories_to_posts_records
|
109
|
+
[
|
110
|
+
{
|
111
|
+
post_id: "posts/1",
|
112
|
+
category_id: "categories/1",
|
113
|
+
},
|
114
|
+
{
|
115
|
+
post_id: "posts/1",
|
116
|
+
category_id: "categories/2",
|
117
|
+
},
|
118
|
+
{
|
119
|
+
post_id: "posts/2",
|
120
|
+
category_id: "categories/2",
|
121
|
+
},
|
122
|
+
{
|
123
|
+
post_id: "posts/3",
|
124
|
+
category_id: "categories/2",
|
125
|
+
},
|
126
|
+
{
|
127
|
+
post_id: "posts/3",
|
128
|
+
category_id: "categories/3",
|
129
|
+
},
|
130
|
+
{
|
131
|
+
post_id: "posts/4",
|
132
|
+
category_id: "categories/3",
|
133
|
+
},
|
134
|
+
{
|
135
|
+
post_id: "posts/4",
|
136
|
+
category_id: "categories/4",
|
137
|
+
},
|
138
|
+
]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
@@ -4,8 +4,7 @@ module Terrestrial
|
|
4
4
|
module SequelTestSupport
|
5
5
|
module_function def build_datastore(_schema)
|
6
6
|
db_connection.tap { |db|
|
7
|
-
|
8
|
-
truncate_tables
|
7
|
+
clean_database
|
9
8
|
|
10
9
|
# The query_counter will let us make assertions about how efficiently
|
11
10
|
# the database is being used
|
@@ -21,6 +20,7 @@ module Terrestrial
|
|
21
20
|
module_function def before_suite(schema)
|
22
21
|
drop_tables
|
23
22
|
create_tables(schema.fetch(:tables))
|
23
|
+
add_unique_indexes(schema.fetch(:unique_indexes))
|
24
24
|
add_foreign_keys(schema.fetch(:foreign_keys))
|
25
25
|
end
|
26
26
|
|
@@ -46,18 +46,31 @@ module Terrestrial
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
module_function def
|
50
|
-
tables.
|
51
|
-
|
49
|
+
module_function def clean_database(tables = db_connection.tables)
|
50
|
+
stardard_test_tables = BLOG_SCHEMA.fetch(:tables).keys
|
51
|
+
test_tables_in_deletable_order = stardard_test_tables.reverse
|
52
|
+
|
53
|
+
clean_tables(test_tables_in_deletable_order)
|
54
|
+
end
|
55
|
+
|
56
|
+
module_function def clean_tables(names)
|
57
|
+
names.each do |name|
|
58
|
+
clean_table(name)
|
52
59
|
end
|
53
60
|
end
|
54
61
|
|
62
|
+
module_function def clean_table(name)
|
63
|
+
db_connection[name].delete
|
64
|
+
end
|
65
|
+
|
55
66
|
module_function def db_connection
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
67
|
+
@db_connection ||= begin
|
68
|
+
Sequel.postgres(
|
69
|
+
host: ENV.fetch("PGHOST"),
|
70
|
+
user: ENV.fetch("PGUSER"),
|
71
|
+
database: ENV.fetch("PGDATABASE"),
|
72
|
+
).tap { Sequel.default_timezone = :utc }
|
73
|
+
end
|
61
74
|
end
|
62
75
|
|
63
76
|
module_function def create_tables(tables)
|
@@ -76,10 +89,24 @@ module Terrestrial
|
|
76
89
|
tables.keys
|
77
90
|
end
|
78
91
|
|
92
|
+
module_function def add_unique_indexes(unique_indexes)
|
93
|
+
unique_indexes.each do |(table, *cols)|
|
94
|
+
db_connection.alter_table(table) do
|
95
|
+
add_unique_constraint(cols)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
79
100
|
module_function def add_foreign_keys(foreign_keys)
|
80
|
-
|
101
|
+
default_options = { deferrable: false, on_delete: :set_null }
|
102
|
+
|
103
|
+
foreign_keys.each do |(table, fk_col, foreign_table, key_col, options)|
|
104
|
+
options_with_defaults = default_options
|
105
|
+
.merge(options || {})
|
106
|
+
.merge(key: key_col)
|
107
|
+
|
81
108
|
db_connection.alter_table(table) do
|
82
|
-
add_foreign_key([fk_col], foreign_table,
|
109
|
+
add_foreign_key([fk_col], foreign_table, options_with_defaults)
|
83
110
|
end
|
84
111
|
end
|
85
112
|
end
|
@@ -108,7 +135,7 @@ module Terrestrial
|
|
108
135
|
end
|
109
136
|
|
110
137
|
def write_count
|
111
|
-
|
138
|
+
upserts.count
|
112
139
|
end
|
113
140
|
|
114
141
|
def update_count
|
@@ -119,6 +146,12 @@ module Terrestrial
|
|
119
146
|
inserts.count
|
120
147
|
end
|
121
148
|
|
149
|
+
def upserts
|
150
|
+
@info
|
151
|
+
.map { |query| query.gsub(/\A\([0-9\.]+s\) /, "") }
|
152
|
+
.select { |query| query.start_with?("INSERT") && query.include?("ON CONFLICT") }
|
153
|
+
end
|
154
|
+
|
122
155
|
def updates
|
123
156
|
@info
|
124
157
|
.map { |query| query.gsub(/\A\([0-9\.]+s\) /, "") }
|
@@ -1,31 +1,39 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
require "terrestrial/
|
3
|
+
require "terrestrial/record"
|
4
4
|
|
5
|
-
RSpec.describe Terrestrial::
|
5
|
+
RSpec.describe Terrestrial::Record do
|
6
6
|
subject(:record) {
|
7
|
-
Terrestrial::
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
Terrestrial::Record.new(
|
8
|
+
mapping,
|
9
|
+
attributes,
|
10
|
+
)
|
11
|
+
}
|
12
|
+
|
13
|
+
let(:mapping) {
|
14
|
+
double(
|
15
|
+
:mapping,
|
16
|
+
{
|
17
|
+
namespace: namespace,
|
18
|
+
primary_key: primary_key_fields,
|
19
|
+
database_owned_fields: [],
|
20
|
+
database_default_fields: [],
|
21
|
+
}
|
12
22
|
)
|
13
23
|
}
|
14
24
|
|
15
25
|
let(:namespace) { double(:namespace) }
|
16
|
-
let(:primary_key_fields) { [
|
26
|
+
let(:primary_key_fields) { [:id] }
|
17
27
|
let(:depth) { 0 }
|
18
28
|
|
19
|
-
let(:
|
29
|
+
let(:attributes) {
|
20
30
|
{
|
21
|
-
|
22
|
-
id2: id2,
|
31
|
+
id: id,
|
23
32
|
name: name,
|
24
33
|
}
|
25
34
|
}
|
26
35
|
|
27
|
-
let(:
|
28
|
-
let(:id2) { double(:id2) }
|
36
|
+
let(:id) { double(:id) }
|
29
37
|
let(:name) { double(:name) }
|
30
38
|
|
31
39
|
describe "#namespace" do
|
@@ -37,15 +45,56 @@ RSpec.describe Terrestrial::AbstractRecord do
|
|
37
45
|
describe "#identity" do
|
38
46
|
it "returns the primary key fields" do
|
39
47
|
expect(record.identity).to eq(
|
40
|
-
|
41
|
-
|
48
|
+
id: id,
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#updatable?" do
|
54
|
+
context "when the record has attributes other than its identity attributes" do
|
55
|
+
let(:record) {
|
56
|
+
Terrestrial::Record.new(
|
57
|
+
mapping,
|
58
|
+
{ id: "some-id", name: "some name" },
|
59
|
+
)
|
60
|
+
}
|
61
|
+
|
62
|
+
it "returns true" do
|
63
|
+
expect(record).to be_updatable
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when the record contains only identity attributes" do
|
68
|
+
let(:record) {
|
69
|
+
Terrestrial::Record.new(
|
70
|
+
mapping,
|
71
|
+
{ id: "some-id" },
|
72
|
+
)
|
73
|
+
}
|
74
|
+
|
75
|
+
it "returns false" do
|
76
|
+
expect(record).not_to be_updatable
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#updatable_attributes" do
|
82
|
+
it "filters out idetity attributes" do
|
83
|
+
expect(record.updatable_attributes).not_to include(
|
84
|
+
id: id,
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "returns a hash of only non-identity attributes" do
|
89
|
+
expect(record.updatable_attributes).to eq(
|
90
|
+
name: name,
|
42
91
|
)
|
43
92
|
end
|
44
93
|
end
|
45
94
|
|
46
95
|
describe "#fetch" do
|
47
96
|
it "delegates to the underlying Hash representation" do
|
48
|
-
expect(record.fetch(:
|
97
|
+
expect(record.fetch(:id)).to eq(id)
|
49
98
|
expect(record.fetch(:name)).to eq(name)
|
50
99
|
expect(record.fetch(:not_there, "nope")).to eq("nope")
|
51
100
|
expect(record.fetch(:not_there) { "lord no" }).to eq("lord no")
|
@@ -55,13 +104,61 @@ RSpec.describe Terrestrial::AbstractRecord do
|
|
55
104
|
describe "#to_h" do
|
56
105
|
it "returns a raw_data merged with identity" do
|
57
106
|
expect(record.to_h).to eq(
|
58
|
-
|
59
|
-
id2: id2,
|
107
|
+
id: id,
|
60
108
|
name: name,
|
61
109
|
)
|
62
110
|
end
|
63
111
|
end
|
64
112
|
|
113
|
+
describe "#reject" do
|
114
|
+
it "returns a new record" do
|
115
|
+
expect(record.reject { true }).to be_a(Terrestrial::Record)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "rejects matching non-identity attributes" do
|
119
|
+
filtered = record.reject { |k, _v| k == :name }
|
120
|
+
|
121
|
+
expect(filtered.to_h).not_to include(:name)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "does not yield identity fields for rejection" do
|
125
|
+
captured = []
|
126
|
+
|
127
|
+
record.reject { |k, v| captured << [k, v] }
|
128
|
+
|
129
|
+
expect(captured).not_to include(:id1, :id2)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "cannot reject the identity attributes" do
|
133
|
+
filtered = record.reject { true }
|
134
|
+
|
135
|
+
expect(filtered.to_h).to eq(
|
136
|
+
id: id,
|
137
|
+
)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#empty?" do
|
142
|
+
context "when there are non-identity attributes" do
|
143
|
+
it "returns false" do
|
144
|
+
expect(record).not_to be_empty
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "when there are only identity attributes" do
|
149
|
+
let(:record) {
|
150
|
+
Terrestrial::Record.new(
|
151
|
+
mapping,
|
152
|
+
{ id: "some-id" },
|
153
|
+
)
|
154
|
+
}
|
155
|
+
|
156
|
+
it "returns true" do
|
157
|
+
expect(record).to be_empty
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
65
162
|
describe "#if_upsert" do
|
66
163
|
it "returns self" do
|
67
164
|
expect(
|
@@ -101,8 +198,7 @@ RSpec.describe Terrestrial::AbstractRecord do
|
|
101
198
|
|
102
199
|
it "returns a new record with same identity" do
|
103
200
|
expect(record.merge(extra_data).identity).to eq(
|
104
|
-
|
105
|
-
id2: id2,
|
201
|
+
id: id,
|
106
202
|
)
|
107
203
|
end
|
108
204
|
|
@@ -116,8 +212,7 @@ RSpec.describe Terrestrial::AbstractRecord do
|
|
116
212
|
merged_record = record.merge(extra_data)
|
117
213
|
|
118
214
|
expect(merged_record.to_h).to eq(
|
119
|
-
|
120
|
-
id2: id2,
|
215
|
+
id: id,
|
121
216
|
name: name,
|
122
217
|
location: location,
|
123
218
|
)
|
@@ -145,99 +240,36 @@ RSpec.describe Terrestrial::AbstractRecord do
|
|
145
240
|
end
|
146
241
|
end
|
147
242
|
|
148
|
-
describe "
|
149
|
-
|
150
|
-
Terrestrial::
|
151
|
-
namespace,
|
152
|
-
primary_key_fields,
|
153
|
-
raw_data,
|
154
|
-
_depth = 5,
|
155
|
-
)
|
156
|
-
}
|
157
|
-
|
158
|
-
let(:shallow_record) {
|
159
|
-
Terrestrial::AbstractRecord.new(
|
160
|
-
namespace,
|
161
|
-
primary_key_fields,
|
162
|
-
raw_data,
|
163
|
-
_depth = 1,
|
164
|
-
)
|
165
|
-
}
|
243
|
+
describe "#==" do
|
244
|
+
context "compared to a record with the same attributes and mapping" do
|
245
|
+
let(:other) { Terrestrial::Record.new(mapping, attributes) }
|
166
246
|
|
167
|
-
|
168
|
-
|
169
|
-
expect([shallow_record, record, deep_record].sort).to eq(
|
170
|
-
[record, deep_record, shallow_record]
|
171
|
-
)
|
247
|
+
it "is equal" do
|
248
|
+
expect(record).to eq(other)
|
172
249
|
end
|
173
250
|
end
|
174
|
-
end
|
175
251
|
|
176
|
-
|
177
|
-
|
178
|
-
let(:
|
252
|
+
context "compared to a record with the same mappiung different attributes" do
|
253
|
+
let(:other) { Terrestrial::Record.new(mapping, other_attributes) }
|
254
|
+
let(:other_attributes) { double(:other_attributes) }
|
179
255
|
|
180
|
-
it "
|
181
|
-
record
|
256
|
+
it "is equal" do
|
257
|
+
expect(record).not_to eq(other)
|
182
258
|
end
|
259
|
+
end
|
183
260
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
}
|
188
|
-
|
189
|
-
let(:record_subclass) {
|
190
|
-
Class.new(Terrestrial::AbstractRecord) {
|
191
|
-
protected
|
261
|
+
context "compared to a record with the same attributes and different mapping" do
|
262
|
+
let(:other) { Terrestrial::Record.new(other_mapping, attributes) }
|
263
|
+
let(:other_mapping) { double(:other_mapping) }
|
192
264
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
}
|
265
|
+
it "is not equal" do
|
266
|
+
expect(record).not_to eq(other)
|
267
|
+
end
|
268
|
+
end
|
198
269
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
context "when the operation type is equal" do
|
206
|
-
context "when the combined `raw_data` and `identity` are equal" do
|
207
|
-
let(:comparitor) { record.merge({}) }
|
208
|
-
|
209
|
-
it "is equal" do
|
210
|
-
expect(record.==(comparitor)).to be(true)
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
context "when the combined `raw_data` and `identity` are not equal" do
|
215
|
-
let(:comparitor) { record.merge(something_else: "i'm different") }
|
216
|
-
|
217
|
-
it "is not equal" do
|
218
|
-
expect(record.==(comparitor)).to be(false)
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
|
-
context "when the operation name differs" do
|
224
|
-
let(:comparitor) {
|
225
|
-
record_class_with_different_operation.new(namespace, primary_key_fields, raw_data)
|
226
|
-
}
|
227
|
-
|
228
|
-
let(:record_class_with_different_operation) {
|
229
|
-
Class.new(Terrestrial::AbstractRecord) {
|
230
|
-
protected
|
231
|
-
def operation
|
232
|
-
:do_a_different_thing
|
233
|
-
end
|
234
|
-
}
|
235
|
-
}
|
236
|
-
|
237
|
-
it "is not equal" do
|
238
|
-
expect(record.==(comparitor)).to be(false)
|
239
|
-
end
|
240
|
-
end
|
270
|
+
context "compared to something completely different" do
|
271
|
+
it "is not equal" do
|
272
|
+
expect(record).not_to eq("something completetly different")
|
241
273
|
end
|
242
274
|
end
|
243
275
|
end
|