protobuf-activerecord 7.0.0 → 7.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.
@@ -1,47 +0,0 @@
1
- lib = File.expand_path("../lib", __FILE__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require "protobuf/active_record/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "protobuf-activerecord"
7
- spec.version = Protobuf::ActiveRecord::VERSION
8
- spec.authors = ["Adam Hutchison"]
9
- spec.email = ["liveh2o@gmail.com"]
10
- spec.homepage = "http://github.com/liveh2o/protobuf-activerecord"
11
- spec.summary = "Google Protocol Buffers integration for Active Record"
12
- spec.description = "Provides the ability to create Active Record objects from Protocol Buffer messages and vice versa."
13
- spec.license = "MIT"
14
-
15
- spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
16
- spec.executables = spec.files.grep(%r{^bin/}).map { |f| File.basename(f) }
17
- spec.require_paths = ["lib"]
18
-
19
- ##
20
- # Dependencies
21
- #
22
- spec.add_dependency "activerecord", "~> 7.0.0"
23
- spec.add_dependency "activesupport", "~> 7.0.0"
24
- spec.add_dependency "concurrent-ruby"
25
- spec.add_dependency "heredity", ">= 0.1.1"
26
- spec.add_dependency "protobuf", ">= 3.0"
27
-
28
- ##
29
- # Development dependencies
30
- #
31
- spec.add_development_dependency "benchmark-ips"
32
- spec.add_development_dependency "rake"
33
- spec.add_development_dependency "rspec", ">= 3.3.0"
34
- spec.add_development_dependency "rspec-pride", ">= 3.1.0"
35
- spec.add_development_dependency "pry-nav"
36
- spec.add_development_dependency "simplecov"
37
- spec.add_development_dependency "standard"
38
-
39
- if ENV["PLATFORM"] == "java" || ::RUBY_PLATFORM == "java"
40
- spec.platform = "java"
41
- spec.add_development_dependency "activerecord-jdbcsqlite3-adapter"
42
- else
43
- spec.add_development_dependency "sqlite3", ">= 1.4"
44
- end
45
-
46
- spec.add_development_dependency "timecop"
47
- end
@@ -1,98 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Protobuf::ActiveRecord::Columns do
4
- describe "._protobuf_map_columns" do
5
- context "when the class has a table" do
6
- let(:expected_column_names) {
7
- User.columns.each_with_object({}) do |column, hash|
8
- hash[column.name.to_sym] = column
9
- end
10
- }
11
-
12
- let(:expected_column_types) {
13
- User.columns.each_with_object({}) do |column, hash|
14
- hash[column.type.to_sym] ||= ::Set.new
15
- hash[column.type.to_sym] << column.name.to_sym
16
- end
17
- }
18
-
19
- it "maps columns by name" do
20
- expect(User._protobuf_columns).to eq expected_column_names
21
- end
22
-
23
- it "maps column names by column type" do
24
- expected_column_types.each do |expected_column_type, value|
25
- expect(User._protobuf_column_types).to include expected_column_type => value
26
- end
27
- end
28
- end
29
- end
30
-
31
- context "column type predicates" do
32
- before { allow(User).to receive(:_protobuf_column_types).and_return({}) }
33
-
34
- describe "._protobuf_date_column?" do
35
- before { User._protobuf_column_types[:date] = [:foo_date] }
36
-
37
- context "when the column type is :date" do
38
- it "is true" do
39
- expect(User._protobuf_date_column?(:foo_date)).to be true
40
- end
41
- end
42
-
43
- context "when the column type is not :date" do
44
- it "is false" do
45
- expect(User._protobuf_date_column?(:bar_date)).to be false
46
- end
47
- end
48
- end
49
-
50
- describe "._protobuf_datetime_column?" do
51
- before { User._protobuf_column_types[:datetime] = [:foo_datetime] }
52
-
53
- context "when the column type is :datetime" do
54
- it "is true" do
55
- expect(User._protobuf_datetime_column?(:foo_datetime)).to be true
56
- end
57
- end
58
-
59
- context "when the column type is not :datetime" do
60
- it "is false" do
61
- expect(User._protobuf_datetime_column?(:bar_datetime)).to be false
62
- end
63
- end
64
- end
65
-
66
- describe "._protobuf_time_column?" do
67
- before { User._protobuf_column_types[:time] = [:foo_time] }
68
-
69
- context "when the column type is :time" do
70
- it "is true" do
71
- expect(User._protobuf_time_column?(:foo_time)).to be true
72
- end
73
- end
74
-
75
- context "when the column type is not :time" do
76
- it "is false" do
77
- expect(User._protobuf_time_column?(:bar_time)).to be false
78
- end
79
- end
80
- end
81
-
82
- describe "._protobuf_timestamp_column?" do
83
- before { User._protobuf_column_types[:timestamp] = [:foo_timestamp] }
84
-
85
- context "when the column type is :timestamp" do
86
- it "is true" do
87
- expect(User._protobuf_timestamp_column?(:foo_timestamp)).to be true
88
- end
89
- end
90
-
91
- context "when the column type is not :timestamp" do
92
- it "is false" do
93
- expect(User._protobuf_timestamp_column?(:bar_timestamp)).to be false
94
- end
95
- end
96
- end
97
- end
98
- end
@@ -1,28 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Protobuf::ActiveRecord::NestedAttributes do
4
- let(:user_message) {
5
- UserMessage.new(name: "foo bar", email: "foo@test.co", photos: [{url: "https://test.co/test.png"}])
6
- }
7
-
8
- describe "._filter_attribute_fields", aggregate_failures: true do
9
- it "includes nested attributes" do
10
- attribute_fields = User._filter_attribute_fields(user_message)
11
- expect(attribute_fields[:photos_attributes]).to eq(user_message.photos)
12
- end
13
-
14
- context "when" do
15
- end
16
- end
17
-
18
- describe ".new" do
19
- context "when a model accepts nested attributes" do
20
- it "transforms nested attributes", aggregate_failures: true do
21
- user_message.photos.each do |photo_message|
22
- expect(Photo).to receive(:attributes_from_proto).with(photo_message).and_call_original
23
- end
24
- User.new(user_message)
25
- end
26
- end
27
- end
28
- end
@@ -1,70 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Protobuf::ActiveRecord::Persistence do
4
- let(:user) { User.new(user_attributes) }
5
- let(:user_attributes) { {first_name: "foo", last_name: "bar", email: "foo@test.co"} }
6
- let(:proto_hash) { {name: "foo bar", email: "foo@test.co"} }
7
- let(:proto) { UserMessage.new(proto_hash) }
8
-
9
- describe ".create" do
10
- it "accepts a protobuf message" do
11
- expect_any_instance_of(User).to receive(:save)
12
- User.create(proto)
13
- end
14
-
15
- it "accepts a hash" do
16
- expect_any_instance_of(User).to receive(:save)
17
- User.create(user_attributes)
18
- end
19
- end
20
-
21
- describe ".create!" do
22
- it "accepts a protobuf message" do
23
- expect_any_instance_of(User).to receive(:save!)
24
- User.create!(proto)
25
- end
26
-
27
- it "accepts a hash" do
28
- expect_any_instance_of(User).to receive(:save!)
29
- User.create!(user_attributes)
30
- end
31
- end
32
-
33
- describe "#assign_attributes" do
34
- let(:user) { ::User.new }
35
-
36
- it "accepts a protobuf message" do
37
- user.assign_attributes(proto)
38
- expect(user.changed?).to be true
39
- end
40
-
41
- it "accepts a hash" do
42
- user.assign_attributes(user_attributes)
43
- expect(user.changed?).to be true
44
- end
45
- end
46
-
47
- describe "#update" do
48
- it "accepts a protobuf message" do
49
- expect_any_instance_of(User).to receive(:save)
50
- user.update(proto)
51
- end
52
-
53
- it "accepts a hash" do
54
- expect_any_instance_of(User).to receive(:save)
55
- user.update(user_attributes)
56
- end
57
- end
58
-
59
- describe "#update!" do
60
- it "accepts a protobuf message" do
61
- expect_any_instance_of(User).to receive(:save!)
62
- user.update!(proto)
63
- end
64
-
65
- it "accepts a hash" do
66
- expect_any_instance_of(User).to receive(:save!)
67
- user.update!(user_attributes)
68
- end
69
- end
70
- end
@@ -1,201 +0,0 @@
1
- require "spec_helper"
2
-
3
- class TheEnum < ::Protobuf::Enum
4
- define :VALUE, 1
5
- end
6
-
7
- class TheMessage < ::Protobuf::Message
8
- optional TheEnum, :the_enum_value, 1
9
- end
10
-
11
- describe Protobuf::ActiveRecord::Scope do
12
- before do
13
- @field_parsers = User.instance_variable_get(:@_searchable_field_parsers)
14
- @fields = User.instance_variable_get(:@_searchable_fields)
15
- end
16
-
17
- after do
18
- User.instance_variable_set(:@_searchable_field_parsers, @field_parsers)
19
- User.instance_variable_set(:@_searchable_fields, @fields)
20
- User.instance_variable_set(:@_upsert_keys, [])
21
- end
22
-
23
- describe ".search_scope" do
24
- let(:request) { UserSearchMessage.new(guid: ["foo"], email: ["foo@test.co"]) }
25
-
26
- before {
27
- allow(User).to receive(:searchable_field_parsers).and_return(email: proc { |val| val })
28
- }
29
-
30
- it "builds scopes for searchable fields" do
31
- allow(User).to receive(:searchable_fields).and_return(email: :by_email)
32
- expect(User.search_scope(request)).to eq User.by_email("foo@test.co")
33
- end
34
-
35
- it "is chainable" do
36
- expect(User.limit(1).search_scope(request).order(:email)).to eq User.limit(1).order(:email)
37
- end
38
-
39
- context "when a searchable field does not have a value" do
40
- let(:request) { UserSearchMessage.new(email: ["foo@test.co"]) }
41
-
42
- it "doesn't build a scope from that field" do
43
- allow(User).to receive(:searchable_fields).and_return(email: :by_email)
44
- expect(User.search_scope(request)).to eq User.by_email("foo@test.co")
45
- end
46
- end
47
-
48
- context "when a searchable field uses a non-existant scope" do
49
- let(:request) { UserSearchMessage.new(email: ["foo@test.co"]) }
50
-
51
- it "raises an exception" do
52
- allow(User).to receive(:searchable_fields).and_return(email: :by_hullabaloo)
53
- expect { User.search_scope(request) }.to raise_exception(/undefined method .*by_hullabaloo/i)
54
- end
55
- end
56
- end
57
-
58
- describe ".field_scope" do
59
- context "when :scope is not defined" do
60
- it "defines the given field as searchable using the `by_[:field]` as the scope" do
61
- User.field_scope :guid
62
- expect(User.searchable_fields[:guid]).to eq :by_guid
63
- end
64
- end
65
-
66
- context "when :scope is defined" do
67
- it "defines the given field as searchable using the given :scope" do
68
- User.field_scope :guid, scope: :custom_scope
69
- expect(User.searchable_fields[:guid]).to eq :custom_scope
70
- end
71
- end
72
-
73
- context "when :parser is not defined" do
74
- it "doesn't define the given field as parseable" do
75
- User.field_scope :guid
76
- expect(User.searchable_field_parsers[:guid]).to eq nil
77
- end
78
- end
79
-
80
- context "when :parser is defined" do
81
- it "defines the given field as parseable using the given :parser" do
82
- User.field_scope :guid, parser: :parser
83
- expect(User.searchable_field_parsers[:guid]).to eq :parser
84
- end
85
- end
86
- end
87
-
88
- describe ".parse_search_values" do
89
- it "converts single values to collections" do
90
- proto = UserMessage.new(email: "the.email@test.in")
91
-
92
- User.field_scope :email
93
- expect(User.parse_search_values(proto, :email)).to eq ["the.email@test.in"]
94
- end
95
-
96
- context "when a field parser is defined" do
97
- before { User.field_scope :guid, parser: parser }
98
-
99
- let(:proto) { UserSearchMessage.new(guid: ["foo"]) }
100
-
101
- context "and the parser does not respond to :to_sym" do
102
- let(:parser) { double("parser") }
103
-
104
- it "passes the value to the parser" do
105
- expect(parser).to receive(:call).with(["foo"])
106
- User.parse_search_values(proto, :guid)
107
- end
108
- end
109
- end
110
-
111
- context "when the field is an enum" do
112
- it "maps values to integers" do
113
- proto = TheMessage.new(the_enum_value: TheEnum::VALUE)
114
- expect(User.parse_search_values(proto, :the_enum_value)[0]).to be 1
115
- end
116
- end
117
- end
118
-
119
- describe ".upsert_key" do
120
- it "adds the fields to the upsert_keys" do
121
- ::User.field_scope(:guid)
122
- ::User.upsert_key(:guid)
123
- expect(::User.upsert_keys).to eq([[:guid]])
124
- end
125
-
126
- context "no field_scope defined" do
127
- it "raises an error" do
128
- expect { ::User.upsert_key(:foobar) }.to raise_error(::Protobuf::ActiveRecord::UpsertScopeError)
129
- end
130
- end
131
- end
132
-
133
- describe ".for_upsert" do
134
- let(:guid) { "USR-1" }
135
- let(:proto) { ::UserMessage.new(guid: guid) }
136
-
137
- before do
138
- ::User.delete_all
139
- ::User.field_scope(:guid)
140
- ::User.upsert_key(:guid)
141
- end
142
-
143
- context "no matching upsert keys" do
144
- let(:proto) { ::UserMessage.new }
145
-
146
- it "raises an error" do
147
- expect { ::User.for_upsert(proto) }.to raise_error(::Protobuf::ActiveRecord::UpsertNotFoundError)
148
- end
149
- end
150
-
151
- context "no existing records" do
152
- it "returns a new record" do
153
- record = ::User.for_upsert(proto)
154
- expect(record.new_record?).to be true
155
- end
156
- end
157
-
158
- context "existing record" do
159
- before { ::User.create(guid: guid) }
160
- after { ::User.delete_all }
161
-
162
- it "returns the existing record" do
163
- record = ::User.for_upsert(proto)
164
- expect(record.new_record?).to be false
165
- end
166
- end
167
- end
168
-
169
- describe ".upsert" do
170
- let(:guid) { "USR-1" }
171
- let(:proto) { ::UserMessage.new(guid: guid, email: "bar") }
172
-
173
- before do
174
- ::User.delete_all
175
- ::User.field_scope(:guid)
176
- ::User.upsert_key(:guid)
177
- end
178
-
179
- context "no existing records" do
180
- it "creates a new record" do
181
- ::User.upsert(proto)
182
- expect(::User.count).to eq(1)
183
- end
184
- end
185
-
186
- context "existing record" do
187
- before { ::User.create(guid: guid, email: "foo") }
188
- after { ::User.delete_all }
189
-
190
- it "updates the existing record" do
191
- ::User.upsert(proto)
192
- expect(::User.first.email).to eq("bar")
193
- end
194
-
195
- it "returns a user" do
196
- result = ::User.upsert(proto)
197
- expect(result).to be_a(::User)
198
- end
199
- end
200
- end
201
- end
@@ -1,208 +0,0 @@
1
- require "spec_helper"
2
-
3
- # Used to test calling #to_proto when no protobuf message is configured.
4
- class UnconfiguredUser
5
- include Protobuf::ActiveRecord::Model
6
- end
7
-
8
- describe Protobuf::ActiveRecord::Serialization do
9
- let(:protobuf_message) { UserMessage }
10
-
11
- describe ".field_from_record" do
12
- context "when the given transformer is a symbol" do
13
- before { User.field_from_record :first_name, :extract_first_name }
14
-
15
- it "creates a symbol transformer from the converter" do
16
- expect(User._protobuf_field_symbol_transformers[:first_name]).to eq :extract_first_name
17
- end
18
- end
19
-
20
- context "when the given transformer is not callable" do
21
- it "raises an exception" do
22
- expect { User.field_from_record :name, nil }.to raise_exception(Protobuf::ActiveRecord::FieldTransformerError)
23
- end
24
- end
25
-
26
- context "when the given transformer is callable" do
27
- let(:callable) { lambda { |_proto| } }
28
-
29
- before {
30
- allow(User).to receive(:_protobuf_field_transformers).and_return({})
31
- User.field_from_record :account_id, callable
32
- }
33
-
34
- it "adds the given converter to the list of protobuf field transformers" do
35
- expect(User._protobuf_field_transformers[:account_id]).to eq(callable)
36
- end
37
- end
38
- end
39
-
40
- describe ".protobuf_message" do
41
- let(:options) { {only: []} }
42
-
43
- before { User.protobuf_message(protobuf_message, options) }
44
- after { User.protobuf_message(protobuf_message, {}) }
45
-
46
- context "given a value" do
47
- it "defines #to_proto" do
48
- expect(User.allocate).to respond_to :to_proto
49
- end
50
- end
51
-
52
- context "given options" do
53
- it "merges them with protobuf field options" do
54
- expect(User._protobuf_field_options).to eq options
55
- end
56
- end
57
-
58
- it "returns the protobuf message for this object" do
59
- expect(User.protobuf_message).to eq protobuf_message
60
- end
61
- end
62
-
63
- context "when protobuf_message is defined" do
64
- let(:attributes) { {} }
65
- let(:user) { User.new(attributes) }
66
-
67
- before { User.protobuf_message(protobuf_message) }
68
-
69
- describe "#_filter_field_attributes" do
70
- context "when options has :only" do
71
- it "only returns the given field(s)" do
72
- fields = user._filter_field_attributes(only: :name)
73
- expect(fields).to eq [:name]
74
- end
75
- end
76
-
77
- context "when options has :except" do
78
- it "returns all except the given field(s)" do
79
- fields = user._filter_field_attributes(except: :name)
80
- expect(fields).to match_array(
81
- [:guid, :email, :email_domain, :password, :nullify, :photos, :created_at, :updated_at]
82
- )
83
- end
84
- end
85
- end
86
-
87
- describe "#_filtered_fields" do
88
- it "returns protobuf fields" do
89
- expect(user._filtered_fields).to match_array(
90
- [:guid, :name, :email, :email_domain, :password, :nullify, :photos, :created_at, :updated_at]
91
- )
92
- end
93
-
94
- context "given :deprecated => false" do
95
- it "filters all deprecated fields" do
96
- fields = user._filtered_fields(deprecated: false)
97
- expect(fields).to match_array(
98
- [:guid, :name, :email, :password, :nullify, :photos, :created_at, :updated_at]
99
- )
100
- end
101
-
102
- context "and :include => :email_domain" do
103
- it "includes deprecated fields that have been explicitly specified" do
104
- fields = user._filtered_fields(deprecated: false, include: :email_domain)
105
- expect(fields).to match_array(
106
- [:guid, :name, :email, :email_domain, :password, :nullify, :photos, :created_at, :updated_at]
107
- )
108
- end
109
- end
110
- end
111
- end
112
-
113
- describe "#_normalize_options" do
114
- let(:options) { {only: [:name]} }
115
-
116
- context "given empty options" do
117
- before { User.protobuf_message(protobuf_message, options) }
118
-
119
- it "returns the class's protobuf field options" do
120
- expect(User.allocate._normalize_options({})).to eq options
121
- end
122
- end
123
-
124
- context "given options" do
125
- before { User.protobuf_message(protobuf_message, {}) }
126
-
127
- it "merges them with the class's protobuf field options" do
128
- normalized_options = User.allocate._normalize_options(options)
129
- expect(normalized_options[:only]).to eq options[:only]
130
- end
131
- end
132
-
133
- context "given options with :only" do
134
- before { User.protobuf_message(protobuf_message, {}) }
135
-
136
- it "ensures that :except exists" do
137
- normalized_options = User.allocate._normalize_options(options)
138
- expect(normalized_options[:except]).to eq []
139
- end
140
- end
141
-
142
- context "given options with :except" do
143
- let(:options) { {except: [:name]} }
144
-
145
- before { User.protobuf_message(protobuf_message, {}) }
146
-
147
- it "ensures that :only exists" do
148
- normalized_options = User.allocate._normalize_options(options)
149
- expect(normalized_options[:only]).to eq []
150
- end
151
- end
152
- end
153
-
154
- describe "#fields_from_record" do
155
- let(:attributes) {
156
- {
157
- guid: "foo",
158
- first_name: "bar",
159
- last_name: "baz",
160
- email: "foo@test.co"
161
- }
162
- }
163
-
164
- context "when a transformer is defined for the field" do
165
- it "gets the field from the transformer" do
166
- expect(user.fields_from_record[:email_domain]).to eq("test.co")
167
- end
168
- end
169
-
170
- context "given options with :include" do
171
- it "adds the given field to the list of serialized fields" do
172
- fields = user.fields_from_record(include: :token)
173
- expect(fields).to include(:token)
174
- end
175
- end
176
-
177
- context "when a field is a collection association" do
178
- let(:user) { User.create(attributes) }
179
-
180
- it "terminates the association proxy" do
181
- fields = user.fields_from_record(include: :photos)
182
- expect(fields[:photos]).to be_an(Array)
183
- end
184
- end
185
- end
186
-
187
- describe "#to_proto" do
188
- context "when a protobuf message is configured" do
189
- let(:proto) { protobuf_message.new(proto_hash) }
190
- let(:proto_hash) { {name: "foo"} }
191
-
192
- before { allow(user).to receive(:fields_from_record).and_return(proto_hash) }
193
-
194
- it "intializes a new protobuf message with attributes from #to_proto_hash" do
195
- expect(user.to_proto).to eq proto
196
- end
197
- end
198
-
199
- context "when a protobuf message is not configured" do
200
- let(:user) { UnconfiguredUser.new }
201
-
202
- it "raises an exception" do
203
- expect { user.to_proto }.to raise_exception(Protobuf::ActiveRecord::MessageNotDefined)
204
- end
205
- end
206
- end
207
- end
208
- end