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.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -1
  3. data/Gemfile.lock +44 -53
  4. data/README.md +3 -6
  5. data/bin/test +1 -1
  6. data/features/env.rb +12 -2
  7. data/features/example.feature +23 -26
  8. data/lib/terrestrial.rb +31 -0
  9. data/lib/terrestrial/adapters/abstract_adapter.rb +6 -0
  10. data/lib/terrestrial/adapters/memory_adapter.rb +82 -6
  11. data/lib/terrestrial/adapters/sequel_postgres_adapter.rb +191 -0
  12. data/lib/terrestrial/configurations/conventional_association_configuration.rb +65 -35
  13. data/lib/terrestrial/configurations/conventional_configuration.rb +280 -124
  14. data/lib/terrestrial/configurations/mapping_config_options_proxy.rb +97 -0
  15. data/lib/terrestrial/deleted_record.rb +12 -8
  16. data/lib/terrestrial/dirty_map.rb +17 -9
  17. data/lib/terrestrial/functional_pipeline.rb +64 -0
  18. data/lib/terrestrial/inspection_string.rb +6 -1
  19. data/lib/terrestrial/lazy_object_proxy.rb +1 -0
  20. data/lib/terrestrial/many_to_many_association.rb +34 -20
  21. data/lib/terrestrial/many_to_one_association.rb +11 -3
  22. data/lib/terrestrial/one_to_many_association.rb +9 -0
  23. data/lib/terrestrial/public_conveniencies.rb +65 -82
  24. data/lib/terrestrial/record.rb +106 -0
  25. data/lib/terrestrial/relation_mapping.rb +43 -12
  26. data/lib/terrestrial/relational_store.rb +33 -11
  27. data/lib/terrestrial/upsert_record.rb +54 -0
  28. data/lib/terrestrial/version.rb +1 -1
  29. data/spec/automatic_timestamps_spec.rb +339 -0
  30. data/spec/changes_api_spec.rb +81 -0
  31. data/spec/config_override_spec.rb +28 -19
  32. data/spec/custom_serializers_spec.rb +3 -2
  33. data/spec/database_default_fields_spec.rb +213 -0
  34. data/spec/database_generated_id_spec.rb +291 -0
  35. data/spec/database_owned_fields_and_timestamps_spec.rb +200 -0
  36. data/spec/deletion_spec.rb +1 -1
  37. data/spec/error_handling/factory_error_handling_spec.rb +1 -4
  38. data/spec/error_handling/serialization_error_spec.rb +1 -4
  39. data/spec/error_handling/upsert_error_spec.rb +7 -11
  40. data/spec/graph_persistence_spec.rb +52 -18
  41. data/spec/ordered_association_spec.rb +10 -12
  42. data/spec/predefined_queries_spec.rb +14 -12
  43. data/spec/readme_examples_spec.rb +1 -1
  44. data/spec/sequel_query_efficiency_spec.rb +19 -16
  45. data/spec/spec_helper.rb +6 -1
  46. data/spec/support/blog_schema.rb +7 -3
  47. data/spec/support/object_graph_setup.rb +30 -39
  48. data/spec/support/object_store_setup.rb +16 -196
  49. data/spec/support/seed_data_setup.rb +15 -149
  50. data/spec/support/seed_records.rb +141 -0
  51. data/spec/support/sequel_test_support.rb +46 -13
  52. data/spec/terrestrial/abstract_record_spec.rb +138 -106
  53. data/spec/terrestrial/adapters/sequel_postgres_adapter_spec.rb +138 -0
  54. data/spec/terrestrial/deleted_record_spec.rb +0 -27
  55. data/spec/terrestrial/dirty_map_spec.rb +52 -77
  56. data/spec/terrestrial/functional_pipeline_spec.rb +153 -0
  57. data/spec/terrestrial/inspection_string_spec.rb +61 -0
  58. data/spec/terrestrial/upsert_record_spec.rb +29 -0
  59. data/terrestrial.gemspec +7 -8
  60. metadata +43 -40
  61. data/MissingFeatures.md +0 -64
  62. data/lib/terrestrial/abstract_record.rb +0 -99
  63. data/lib/terrestrial/association_loaders.rb +0 -52
  64. data/lib/terrestrial/upserted_record.rb +0 -15
  65. data/spec/terrestrial/public_conveniencies_spec.rb +0 -63
  66. 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, biscuits_post_comment_record ],
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
- # This test is using the database so we better clean it out first
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 truncate_tables(tables = db_connection.tables)
50
- tables.each do |table_name|
51
- db_connection[table_name].truncate(cascade: true)
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
- @@db_connection ||= Sequel.postgres(
57
- host: ENV.fetch("PGHOST"),
58
- user: ENV.fetch("PGUSER"),
59
- database: ENV.fetch("PGDATABASE"),
60
- ).tap { Sequel.default_timezone = :utc }
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
- foreign_keys.each do |(table, fk_col, foreign_table, key_col)|
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, key: key_col, deferrable: false, on_delete: :set_null)
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
- insert_count + update_count
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/abstract_record"
3
+ require "terrestrial/record"
4
4
 
5
- RSpec.describe Terrestrial::AbstractRecord do
5
+ RSpec.describe Terrestrial::Record do
6
6
  subject(:record) {
7
- Terrestrial::AbstractRecord.new(
8
- namespace,
9
- primary_key_fields,
10
- raw_data,
11
- depth,
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) { [ :id1, :id2 ] }
26
+ let(:primary_key_fields) { [:id] }
17
27
  let(:depth) { 0 }
18
28
 
19
- let(:raw_data) {
29
+ let(:attributes) {
20
30
  {
21
- id1: id1,
22
- id2: id2,
31
+ id: id,
23
32
  name: name,
24
33
  }
25
34
  }
26
35
 
27
- let(:id1) { double(:id1) }
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
- id1: id1,
41
- id2: id2,
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(:id1)).to eq(id1)
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
- id1: id1,
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
- id1: id1,
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
- id1: id1,
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 "#<=>" do
149
- let(:deep_record) {
150
- Terrestrial::AbstractRecord.new(
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
- context "when other record has deeper depth" do
168
- it "is sortable by depth" do
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
- describe "#==" do
177
- context "super class contract" do
178
- let(:comparitor) { record.merge({}) }
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 "compares" do
181
- record == comparitor
256
+ it "is equal" do
257
+ expect(record).not_to eq(other)
182
258
  end
259
+ end
183
260
 
184
- context "when subclassed" do
185
- subject(:record) {
186
- record_subclass.new(namespace, primary_key_fields, raw_data)
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
- def operation
194
- :do_a_thing
195
- end
196
- }
197
- }
265
+ it "is not equal" do
266
+ expect(record).not_to eq(other)
267
+ end
268
+ end
198
269
 
199
- context "when comparitor is of the wrong type" do
200
- it "is not equal" do
201
- expect(record.==(Object.new)).to be(false)
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