terrestrial 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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