protobuf-activerecord 1.0.5 → 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.
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
3
 
4
4
  desc "Run specs"
@@ -6,3 +6,15 @@ RSpec::Core::RakeTask.new(:spec)
6
6
 
7
7
  desc "Run specs (default)"
8
8
  task :default => :spec
9
+
10
+ desc "Remove protobuf definitions that have been compiled"
11
+ task :clean do
12
+ FileUtils.rm(Dir.glob("spec/support/protobuf/**/*.proto"))
13
+ puts "Cleaned"
14
+ end
15
+
16
+ desc "Compile spec/support protobuf definitions"
17
+ task :compile, [] => :clean do
18
+ cmd = "rprotoc --ruby_out=spec/support/protobuf --proto_path=spec/support/definitions spec/support/definitions/*.proto"
19
+ sh(cmd)
20
+ end
@@ -2,11 +2,13 @@ require 'protoable/convert'
2
2
  require 'protoable/errors'
3
3
  require 'protoable/fields'
4
4
  require 'protoable/persistence'
5
+ require 'protoable/scope'
5
6
  require 'protoable/serialization'
6
7
 
7
8
  module Protoable
8
9
  def self.included(klass)
9
10
  klass.extend Protoable::Fields
11
+ klass.extend Protoable::Scope
10
12
 
11
13
  klass.__send__(:include, Protoable::Convert)
12
14
  klass.__send__(:include, Protoable::Persistence)
@@ -17,10 +17,10 @@ module Protoable
17
17
  convert_int64_to_time(int64).to_datetime
18
18
  end
19
19
 
20
- def _protobuf_convert_columns_to_fields(key, value)
20
+ def _protobuf_convert_attributes_to_fields(key, value)
21
21
  value = case
22
- when _protobuf_column_converters.has_key?(key.to_sym) then
23
- _protobuf_column_converters[key.to_sym].call(value)
22
+ when _protobuf_attribute_converters.has_key?(key.to_sym) then
23
+ _protobuf_attribute_converters[key.to_sym].call(value)
24
24
  when _protobuf_date_column?(key) then
25
25
  value.to_time.to_i
26
26
  when _protobuf_datetime_column?(key) then
@@ -6,18 +6,22 @@ module Protoable
6
6
  class ProtoableError < StandardError
7
7
  end
8
8
 
9
- # Raised by Protoable.protobuf_column_convert when the convert method
10
- # given is not defined, nil, or not callable.
11
- class ColumnConverterError < ProtoableError
9
+ # Raised by Protoable.protoable_attribute when the convert method given is
10
+ # not callable.
11
+ class AttributeConverterError < ProtoableError
12
12
  end
13
13
 
14
- # Raised by Protoable.protobuf_column_transform when the transformer method
15
- # given is not defined, nil, or not callable.
16
- class ColumnTransformerError < ProtoableError
14
+ # Raised by Protoable.attribute_from_proto when the transformer method
15
+ # given is not callable.
16
+ class AttributeTransformerError < ProtoableError
17
17
  end
18
-
19
- # Raised by Protoable.protobuf_field_convert when the convert method
20
- # given is not defined, nil, or not callable.
18
+
19
+ # Raised by Protoable.convert_field when the convert method
20
+ # given not callable.
21
21
  class FieldConverterError < ProtoableError
22
22
  end
23
+
24
+ # Raised by Protoable.field_scope when given scope is not defined.
25
+ class SearchScopeError < ProtoableError
26
+ end
23
27
  end
@@ -9,20 +9,17 @@ module Protoable
9
9
  klass.class_eval do
10
10
  class << self
11
11
  attr_accessor :_protobuf_columns, :_protobuf_column_types,
12
- :_protobuf_column_transformers, :_protobuf_field_converters
13
-
14
- alias_method :convert_field_to_column, :convert_field
15
- alias_method :transform_column_from_proto, :transform_column
12
+ :_protobuf_attribute_transformers, :_protobuf_field_converters
16
13
  end
17
14
 
15
+ @_protobuf_attribute_transformers = {}
18
16
  @_protobuf_columns = {}
19
17
  @_protobuf_column_types = Hash.new { |h,k| h[k] = [] }
20
- @_protobuf_column_transformers = {}
21
18
  @_protobuf_field_converters = {}
22
19
 
23
20
  # NOTE: Make sure each inherited object has the database layout
24
- inheritable_attributes :_protobuf_columns, :_protobuf_column_types,
25
- :_protobuf_field_converters, :_protobuf_column_transformers
21
+ inheritable_attributes :_protobuf_attribute_transformers, :_protobuf_columns,
22
+ :_protobuf_column_types, :_protobuf_field_converters
26
23
  end
27
24
 
28
25
  _protobuf_map_columns(klass)
@@ -39,39 +36,33 @@ module Protoable
39
36
  end
40
37
 
41
38
  module ClassMethods
42
- # Define a field conversion from protobuf to db. Accepts a callable,
43
- # Symbol, or Hash.
39
+ # Define a field conversion from protobuf to db. Accepts a Symbol,
40
+ # Hash, callable or block.
44
41
  #
45
- # When given a callable, it is directly used to convert the field.
42
+ # When given a callable or block, it is directly used to convert the field.
46
43
  #
47
44
  # When a Hash is given, :from and :to keys are expected and expand
48
45
  # to extracting a class method in the format of
49
46
  # "convert_#{from}_to_#{to}".
50
47
  #
51
- # When a symbol is given, it extracts the method with the same name,
52
- # if any. When method is not available it is assumed as the "from"
53
- # data type, and the "to" value is extracted based on the
54
- # name of the column.
48
+ # When a symbol is given, it extracts the method with the same name.
55
49
  #
56
50
  # Examples:
57
- # convert_field :created_at, :int64
58
51
  # convert_field :public_key, :extract_public_key_from_proto
59
- # convert_field :status, lambda { |proto_field| ... }
60
52
  # convert_field :symmetric_key, :from => :base64, :to => :encoded_string
53
+ # convert_field :status, lambda { |proto_field| # Do some stuff... }
54
+ # convert_field :status do |proto_field|
55
+ # # Do some blocky stuff...
56
+ # end
61
57
  #
62
- def convert_field(field, transformer = nil, &blk)
63
- transformer ||= blk
64
- transformer = :"convert_#{transformer[:from]}_to_#{transformer[:to]}" if transformer.is_a?(Hash)
58
+ def convert_field(field, converter = nil, &blk)
59
+ converter ||= blk
60
+ converter = :"convert_#{converter[:from]}_to_#{converter[:to]}" if converter.is_a?(Hash)
65
61
 
66
- if transformer.is_a?(Symbol)
67
- unless self.respond_to?(transformer, true)
68
- column = _protobuf_columns[field.to_sym]
69
- transformer = :"convert_#{transformer}_to_#{column.try(:type)}"
70
- end
71
-
72
- callable = lambda { |value| self.__send__(transformer, value) }
62
+ if converter.is_a?(Symbol)
63
+ callable = lambda { |value| self.__send__(converter, value) }
73
64
  else
74
- callable = transformer
65
+ callable = converter
75
66
  end
76
67
 
77
68
  unless callable.respond_to?(:call)
@@ -81,10 +72,10 @@ module Protoable
81
72
  _protobuf_field_converters[field.to_sym] = callable
82
73
  end
83
74
 
84
- # Define a column transformation from protobuf to db. Accepts a callable,
85
- # or Symbol.
75
+ # Define an attribute transformation from protobuf. Accepts a Symbol,
76
+ # callable, or block.
86
77
  #
87
- # When given a callable, it is directly used to convert the field.
78
+ # When given a callable or block, it is directly used to convert the field.
88
79
  #
89
80
  # When a symbol is given, it extracts the method with the same name.
90
81
  #
@@ -92,10 +83,13 @@ module Protoable
92
83
  # proto message.
93
84
  #
94
85
  # Examples:
95
- # transform_column :public_key, :extract_public_key_from_proto
96
- # transform_column :status, lambda { |proto_field| ... }
86
+ # attribute_from_proto :public_key, :extract_public_key_from_proto
87
+ # attribute_from_proto :status, lambda { |proto_field| # Do some stuff... }
88
+ # attribute_from_proto :status do |proto_field|
89
+ # # Do some blocky stuff...
90
+ # end
97
91
  #
98
- def transform_column(field, transformer = nil, &blk)
92
+ def attribute_from_proto(field, transformer = nil, &blk)
99
93
  transformer ||= blk
100
94
 
101
95
  if transformer.is_a?(Symbol)
@@ -105,10 +99,15 @@ module Protoable
105
99
  end
106
100
 
107
101
  unless callable.respond_to?(:call)
108
- raise ColumnTransformerError, 'Protoable casting needs a callable or block!'
102
+ raise AttributeTransformerError, 'Attribute transformers need a callable or block!'
109
103
  end
110
104
 
111
- _protobuf_column_transformers[field.to_sym] = callable
105
+ _protobuf_attribute_transformers[field.to_sym] = callable
106
+ end
107
+
108
+ def transform_column(field, transformer = nil, &blk)
109
+ warn "[DEPRECATION] `transform_column` is deprecated and will be removed in v1.2. Please use `attribute_from_proto` instead."
110
+ attribute_from_proto(field, transformer, &blk)
112
111
  end
113
112
  end
114
113
  end
@@ -6,7 +6,7 @@ module Protoable
6
6
 
7
7
  module ClassMethods
8
8
  # Filters accessible attributes that exist in the given protobuf message's
9
- # fields or have column transformers defined for them.
9
+ # fields or have attribute transformers defined for them.
10
10
  #
11
11
  # Returns a hash of attribute fields with their respective values.
12
12
  #
@@ -20,7 +20,7 @@ module Protoable
20
20
  symbolized_column = column_name.to_sym
21
21
 
22
22
  if fields.has_key?(symbolized_column) ||
23
- _protobuf_column_transformers.has_key?(symbolized_column)
23
+ _protobuf_attribute_transformers.has_key?(symbolized_column)
24
24
  hash[symbolized_column] = fields[symbolized_column]
25
25
  end
26
26
 
@@ -33,14 +33,14 @@ module Protoable
33
33
  # Creates a hash of attributes from a given protobuf message.
34
34
  #
35
35
  # It converts and transforms field values using the field converters and
36
- # column transformers, ignoring repeated and nil fields.
36
+ # attribute transformers, ignoring repeated and nil fields.
37
37
  #
38
38
  def attributes_from_proto(proto)
39
39
  attribute_fields = _filter_attribute_fields(proto)
40
40
 
41
41
  attributes = attribute_fields.inject({}) do |hash, (key, value)|
42
- if _protobuf_column_transformers.has_key?(key)
43
- hash[key] = _protobuf_column_transformers[key].call(proto)
42
+ if _protobuf_attribute_transformers.has_key?(key)
43
+ hash[key] = _protobuf_attribute_transformers[key].call(proto)
44
44
  else
45
45
  hash[key] = _protobuf_convert_fields_to_columns(key, value)
46
46
  end
@@ -0,0 +1,64 @@
1
+ module Protoable
2
+ module Scope
3
+ def self.extended(klass)
4
+ klass.class_eval do
5
+ class << self
6
+ alias_method :by_fields, :search_scope
7
+ alias_method :from_proto, :search_scope
8
+ alias_method :scope_from_proto, :search_scope
9
+ end
10
+ end
11
+ end
12
+
13
+ # Define fields that should be searchable via `search_scope`. Accepts a
14
+ # protobuf field and an already defined scope.
15
+ #
16
+ # Examples:
17
+ #
18
+ # class User < ActiveRecord::Base
19
+ # scope :by_guid, lambda { |*guids| where(:guid => guids) }
20
+ #
21
+ # field_scope :guid, :by_guid
22
+ # end
23
+ #
24
+ def field_scope(field, scope_name)
25
+ unless self.respond_to?(scope_name)
26
+ raise Protoable::SearchScopeError, "Undefined scope :#{scope_name}. :#{scope_name} must be defined before it can be used as a field scope."
27
+ end
28
+
29
+ searchable_fields[field] = scope_name
30
+ end
31
+
32
+ # Builds and returns a Arel relation based on the fields that are present
33
+ # in the given protobuf message using the searchable fields to determine
34
+ # what scopes to use. Provides several aliases for variety.
35
+ #
36
+ # Examples:
37
+ #
38
+ # # Search starting with the default scope and searchable fields
39
+ # User.search_scope(request)
40
+ # User.by_fields(request)
41
+ # User.from_proto(request)
42
+ # User.scope_from_proto(request)
43
+ #
44
+ def search_scope(proto)
45
+ relation = scoped # Get an ARel relation to build off of
46
+
47
+ searchable_fields.each do |field, scope_name|
48
+ next unless proto.respond_to_and_has_and_present?(field)
49
+
50
+ values = [ proto.__send__(field) ].flatten
51
+ values.map!(&:to_i) if proto.get_field_by_name(field).enum?
52
+
53
+ relation = relation.__send__(scope_name, *values)
54
+ end
55
+
56
+ return relation
57
+ end
58
+
59
+ # :noapi:
60
+ def searchable_fields
61
+ @_searchable_fields ||= {}
62
+ end
63
+ end
64
+ end
@@ -8,60 +8,56 @@ module Protoable
8
8
 
9
9
  klass.class_eval do
10
10
  class << self
11
- attr_accessor :_protobuf_column_converters, :protobuf_fields
12
-
13
- alias_method :convert_column_to_field, :convert_column
11
+ attr_accessor :_protobuf_attribute_converters, :protobuf_fields
14
12
  end
15
13
 
16
- @_protobuf_column_converters = {}
14
+ @_protobuf_attribute_converters = {}
17
15
  @protobuf_fields = []
18
16
 
19
- inheritable_attributes :_protobuf_column_converters, :protobuf_fields, :protobuf_message
17
+ inheritable_attributes :_protobuf_attribute_converters, :protobuf_fields, :protobuf_message
20
18
  end
21
19
  end
22
20
 
23
21
  module ClassMethods
24
- # Define a column conversion from db to protobuf. Accepts a callable,
25
- # Symbol, or Hash.
22
+ def convert_column(field, converter = nil, &blk)
23
+ warn "[DEPRECATION] `convert_column` is deprecated and will be removed in v1.2. Please use `protoable_attribute` instead."
24
+ protoable_attribute(field, converter, &blk)
25
+ end
26
+
27
+ # Define a custom attribute conversion for serialization to protobuf.
28
+ # Accepts a Symbol, Hash, callable or block.
26
29
  #
27
- # When given a callable, it is directly used to convert the field.
30
+ # When given a callable or block, it is directly used to convert the field.
28
31
  #
29
32
  # When a Hash is given, :from and :to keys are expected and expand
30
33
  # to extracting a class method in the format of
31
34
  # "convert_#{from}_to_#{to}".
32
35
  #
33
- # When a symbol is given, it extracts the method with the same name,
34
- # if any. When method is not available it is assumed as the "from"
35
- # data type, and the "to" value is extracted based on the
36
- # name of the column.
36
+ # When a symbol is given, it extracts the method with the same name.
37
37
  #
38
38
  # Examples:
39
- # convert_column :created_at, :int64
40
- # convert_column :public_key, :extract_public_key_from_proto
41
- # convert_column :public_key, method(:extract_public_key_from_proto)
42
- # convert_column :status, lambda { |proto_field| ... }
43
- # convert_column :symmetric_key, :from => :base64, :to => :raw_string
39
+ # protoable_attribute :public_key, :extract_public_key_from_proto
40
+ # protoable_attribute :symmetric_key, :from => :base64, :to => :raw_string
41
+ # protoable_attribute :status, lambda { |proto_field| # Do stuff... }
42
+ # protoable_attribute :status do |proto_field|
43
+ # # Do some blocky stuff...
44
+ # end
44
45
  #
45
- def convert_column(field, converter = nil, &blk)
46
+ def protoable_attribute(field, converter = nil, &blk)
46
47
  converter ||= blk
47
48
  converter = :"convert_#{converter[:from]}_to_#{converter[:to]}" if converter.is_a?(Hash)
48
49
 
49
50
  if converter.is_a?(Symbol)
50
- unless self.respond_to?(converter, true)
51
- column = _protobuf_columns[field.to_sym]
52
- converter = :"convert_#{converter}_to_#{column.try(:type)}"
53
- end
54
-
55
51
  callable = lambda { |value| __send__(converter, value) }
56
52
  else
57
53
  callable = converter
58
54
  end
59
55
 
60
56
  unless callable.respond_to?(:call)
61
- raise ColumnConverterError, 'Column converters must be a callable or block!'
57
+ raise AttributeConverterError, 'Attribute converters must be a callable or block!'
62
58
  end
63
59
 
64
- _protobuf_column_converters[field.to_sym] = callable
60
+ _protobuf_attribute_converters[field.to_sym] = callable
65
61
  end
66
62
 
67
63
  # Define the protobuf message class that should be used to serialize the
@@ -103,7 +99,7 @@ module Protoable
103
99
  def protoable_attributes
104
100
  protoable_attributes = protobuf_fields.inject({}) do |hash, field|
105
101
  value = respond_to?(field) ? __send__(field) : nil
106
- hash[field] = _protobuf_convert_columns_to_fields(field, value)
102
+ hash[field] = _protobuf_convert_attributes_to_fields(field, value)
107
103
  hash
108
104
  end
109
105
 
@@ -112,8 +108,8 @@ module Protoable
112
108
 
113
109
  private
114
110
 
115
- def _protobuf_convert_columns_to_fields(field, value)
116
- self.class._protobuf_convert_columns_to_fields(field, value)
111
+ def _protobuf_convert_attributes_to_fields(field, value)
112
+ self.class._protobuf_convert_attributes_to_fields(field, value)
117
113
  end
118
114
 
119
115
  def protobuf_fields
@@ -1,3 +1,5 @@
1
+ require 'protobuf_ext/message'
2
+
1
3
  require 'protoable'
2
4
 
3
5
  require 'protobuf/activerecord/version'
@@ -1,5 +1,5 @@
1
1
  module Protobuf
2
2
  module ActiveRecord
3
- VERSION = "1.0.5"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
@@ -0,0 +1,12 @@
1
+ require 'protobuf'
2
+
3
+ class Protobuf::Message
4
+ def respond_to_and_has?(key)
5
+ self.respond_to?(key) && self.has_field?(key)
6
+ end
7
+
8
+ def respond_to_and_has_and_present?(key)
9
+ self.respond_to_and_has?(key) &&
10
+ (self.__send__(key).present? || [true, false].include?(self.__send__(key)))
11
+ end
12
+ end
@@ -34,24 +34,24 @@ describe Protoable::Convert do
34
34
  end
35
35
  end
36
36
 
37
- describe "._protobuf_convert_columns_to_fields" do
38
- context "when there is a column converter for the field" do
37
+ describe "._protobuf_convert_attributes_to_fields" do
38
+ context "when there is a attribute converter for the field" do
39
39
  let(:email_value) { "foo@test.co" }
40
40
  let(:email_converter) { User.method(:convert_email_to_lowercase) }
41
41
 
42
- before { User.stub(:_protobuf_column_converters).and_return({ :email => email_converter }) }
42
+ before { User.stub(:_protobuf_attribute_converters).and_return({ :email => email_converter }) }
43
43
 
44
- it "calls the column converter with the given value" do
44
+ it "calls the attribute converter with the given value" do
45
45
  email_converter.should_receive(:call).with(email_value)
46
- User._protobuf_convert_columns_to_fields(:email, email_value)
46
+ User._protobuf_convert_attributes_to_fields(:email, email_value)
47
47
  end
48
48
 
49
49
  context "and it's corresponding column type has a default converter" do
50
50
  before { User.stub(:_protobuf_date_column?).and_return(true) }
51
51
 
52
- it "calls the column converter with the given value" do
52
+ it "calls the attribute converter with the given value" do
53
53
  email_converter.should_receive(:call).with(email_value)
54
- User._protobuf_convert_columns_to_fields(:email, email_value)
54
+ User._protobuf_convert_attributes_to_fields(:email, email_value)
55
55
  end
56
56
  end
57
57
  end
@@ -63,7 +63,7 @@ describe Protoable::Convert do
63
63
  before { User.stub(:_protobuf_date_column?).and_return(true) }
64
64
 
65
65
  it "converts the given value to an integer" do
66
- User._protobuf_convert_columns_to_fields(:foo_date, date).should eq integer
66
+ User._protobuf_convert_attributes_to_fields(:foo_date, date).should eq integer
67
67
  end
68
68
  end
69
69
 
@@ -74,7 +74,7 @@ describe Protoable::Convert do
74
74
  before { User.stub(:_protobuf_datetime_column?).and_return(true) }
75
75
 
76
76
  it "converts the given value to an integer" do
77
- User._protobuf_convert_columns_to_fields(:foo_datetime, datetime).should eq integer
77
+ User._protobuf_convert_attributes_to_fields(:foo_datetime, datetime).should eq integer
78
78
  end
79
79
  end
80
80
 
@@ -85,7 +85,7 @@ describe Protoable::Convert do
85
85
  before { User.stub(:_protobuf_time_column?).and_return(true) }
86
86
 
87
87
  it "converts the given value to an integer" do
88
- User._protobuf_convert_columns_to_fields(:foo_time, time).should eq integer
88
+ User._protobuf_convert_attributes_to_fields(:foo_time, time).should eq integer
89
89
  end
90
90
  end
91
91
 
@@ -96,7 +96,7 @@ describe Protoable::Convert do
96
96
  before { User.stub(:_protobuf_timestamp_column?).and_return(true) }
97
97
 
98
98
  it "converts the given value to an integer" do
99
- User._protobuf_convert_columns_to_fields(:foo_timestamp, timestamp).should eq integer
99
+ User._protobuf_convert_attributes_to_fields(:foo_timestamp, timestamp).should eq integer
100
100
  end
101
101
  end
102
102
 
@@ -104,7 +104,7 @@ describe Protoable::Convert do
104
104
  let(:value) { "Foo" }
105
105
 
106
106
  it "returns the given value" do
107
- User._protobuf_convert_columns_to_fields(:foo, value).should eq value
107
+ User._protobuf_convert_attributes_to_fields(:foo, value).should eq value
108
108
  end
109
109
  end
110
110
  end
@@ -68,21 +68,21 @@ describe Protoable::Fields do
68
68
  end
69
69
  end
70
70
 
71
- describe ".transform_column" do
71
+ describe ".attribute_from_proto" do
72
72
  context "when the given converter is a symbol" do
73
73
  let(:callable) { lambda { |value| User.__send__(:extract_first_name) } }
74
74
 
75
- before { User.transform_column :first_name, :extract_first_name }
75
+ before { User.attribute_from_proto :first_name, :extract_first_name }
76
76
 
77
77
  it "creates a callable method object from the converter" do
78
78
  User.should_receive(:extract_first_name)
79
- User._protobuf_column_transformers[:first_name].call(1)
79
+ User._protobuf_attribute_transformers[:first_name].call(1)
80
80
  end
81
81
  end
82
82
 
83
83
  context "when the given converter is not callable" do
84
84
  it "raises an exception" do
85
- expect { User.transform_column :name, nil }.to raise_exception(Protoable::ColumnTransformerError)
85
+ expect { User.attribute_from_proto :name, nil }.to raise_exception(Protoable::AttributeTransformerError)
86
86
  end
87
87
  end
88
88
 
@@ -90,12 +90,12 @@ describe Protoable::Fields do
90
90
  let(:callable) { lambda { |proto| nil } }
91
91
 
92
92
  before {
93
- User.stub(:_protobuf_column_transformers).and_return(Hash.new)
94
- User.transform_column :account_id, callable
93
+ User.stub(:_protobuf_attribute_transformers).and_return(Hash.new)
94
+ User.attribute_from_proto :account_id, callable
95
95
  }
96
96
 
97
97
  it "adds the given converter to the list of protobuf field transformers" do
98
- User._protobuf_column_transformers[:account_id] = callable
98
+ User._protobuf_attribute_transformers[:account_id] = callable
99
99
  end
100
100
  end
101
101
  end
@@ -4,7 +4,7 @@ describe Protoable::Persistence do
4
4
  let(:user) { User.new(user_attributes) }
5
5
  let(:user_attributes) { { :first_name => 'foo', :last_name => 'bar', :email => 'foo@test.co' } }
6
6
  let(:proto_hash) { { :name => 'foo bar', :email => 'foo@test.co' } }
7
- let(:proto) { Proto::User.new(proto_hash) }
7
+ let(:proto) { UserMessage.new(proto_hash) }
8
8
 
9
9
  describe "._filter_attribute_fields" do
10
10
  it "includes fields that have values" do
@@ -23,22 +23,22 @@ describe Protoable::Persistence do
23
23
  attribute_fields.has_key?(:email).should be_false
24
24
  end
25
25
 
26
- it "includes attributes that aren't fields, but have column transformers" do
27
- User.stub(:_protobuf_column_transformers).and_return({ :account_id => :fetch_account_id })
26
+ it "includes attributes that aren't fields, but have attribute transformers" do
27
+ User.stub(:_protobuf_attribute_transformers).and_return({ :account_id => :fetch_account_id })
28
28
  attribute_fields = User._filter_attribute_fields(proto)
29
29
  attribute_fields.has_key?(:account_id).should be_true
30
30
  end
31
31
  end
32
32
 
33
33
  describe ".attributes_from_proto" do
34
- context "when a column transformer is defined for the field" do
34
+ context "when a attribute transformer is defined for the field" do
35
35
  it "transforms the field value" do
36
36
  attribute_fields = User.attributes_from_proto(proto)
37
37
  attribute_fields[:first_name].should eq user_attributes[:first_name]
38
38
  end
39
39
  end
40
40
 
41
- context "when column transformer is not defined for the field" do
41
+ context "when attribute transformer is not defined for the field" do
42
42
  before {
43
43
  User.stub(:_protobuf_convert_fields_to_columns) do |key, value|
44
44
  value
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Protoable::Scope do
4
+ describe ".search_scope" do
5
+ let(:request) { UserSearchMessage.new(:guid => ["foo"], :email => ["foo@test.co"]) }
6
+
7
+ it "builds scopes for searchable fields" do
8
+ User.stub(:searchable_fields).and_return({ :email => :by_email })
9
+ User.search_scope(request).should eq User.by_email("foo@test.co")
10
+ end
11
+
12
+ it "is chainable" do
13
+ User.limit(1).search_scope(request).order(:email).should eq User.limit(1).order(:email)
14
+ end
15
+
16
+ context "when a searchable field does not have a value" do
17
+ let(:request) { UserSearchMessage.new(:email => ["foo@test.co"]) }
18
+
19
+ it "doesn't build a scope from that field" do
20
+ User.stub(:searchable_fields).and_return({ :email => :by_email })
21
+ User.search_scope(request).should eq User.by_email("foo@test.co")
22
+ end
23
+ end
24
+ end
25
+
26
+ describe ".field_scope" do
27
+ before { @fields = User.instance_variable_get("@_searchable_fields") }
28
+ after { User.instance_variable_set("@_searchable_fields", @fields) }
29
+
30
+ context "when given a search scope that is not defined" do
31
+ it "raises an exception" do
32
+ expect { User.field_scope :name, :foo }.to raise_exception(/Undefined scope :foo/)
33
+ end
34
+ end
35
+
36
+ it "stores the search scope in the searchable fields hash using the field as the key" do
37
+ User.field_scope :guid, :by_guid
38
+ User.searchable_fields[:guid].should eq :by_guid
39
+ end
40
+ end
41
+ end
@@ -1,44 +1,44 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Protoable::Serialization do
4
- let(:protobuf_message) { ::Proto::User }
4
+ let(:protobuf_message) { UserMessage }
5
5
 
6
- describe ".convert_column" do
6
+ describe ".protoable_attribute" do
7
7
  context "when the given converter is a hash" do
8
8
  let(:method) { lambda { |value| User.__send__(:convert_base64_to_string, value) } }
9
9
 
10
- before { User.convert_column :public_key, :from => :base64, :to => :string }
10
+ before { User.protoable_attribute :public_key, :from => :base64, :to => :string }
11
11
 
12
12
  it "determines the method using the hash's :to and :from keys" do
13
13
  User.should_receive(:convert_base64_to_string)
14
- User._protobuf_column_converters[:public_key].call(1)
14
+ User._protobuf_attribute_converters[:public_key].call(1)
15
15
  end
16
16
  end
17
17
 
18
18
  context "when the given converter is a symbol" do
19
19
  let(:callable) { lambda { |value| User.__send__(:convert_email_to_lowercase, value) } }
20
20
 
21
- before { User.convert_column :email, :convert_email_to_lowercase }
21
+ before { User.protoable_attribute :email, :convert_email_to_lowercase }
22
22
 
23
23
  it "creates a callable method object from the converter" do
24
24
  User.should_receive(:convert_email_to_lowercase)
25
- User._protobuf_column_converters[:email].call(1)
25
+ User._protobuf_attribute_converters[:email].call(1)
26
26
  end
27
27
  end
28
28
 
29
29
  context "when the given converter is not callable" do
30
30
  it "raises an exception" do
31
- expect { User.convert_column :email, nil }.to raise_exception(Protoable::ColumnConverterError)
31
+ expect { User.protoable_attribute :email, nil }.to raise_exception(Protoable::AttributeConverterError)
32
32
  end
33
33
  end
34
34
 
35
35
  context "when the given converter is callable" do
36
36
  let(:callable) { lambda { |value| value } }
37
37
 
38
- before { User.convert_column :email, callable }
38
+ before { User.protoable_attribute :email, callable }
39
39
 
40
- it "adds the given converter to list of column converters" do
41
- User._protobuf_column_converters[:email].should eq callable
40
+ it "adds the given converter to list of attribute converters" do
41
+ User._protobuf_attribute_converters[:email].should eq callable
42
42
  end
43
43
  end
44
44
  end
@@ -47,7 +47,7 @@ describe Protoable::Serialization do
47
47
  before { User.protobuf_message(protobuf_message) }
48
48
 
49
49
  context "given a value" do
50
- let(:protobuf_fields) { [ :name, :email, :tags ] }
50
+ let(:protobuf_fields) { [ :guid, :name, :email ] }
51
51
 
52
52
  it "sets .protobuf_fields" do
53
53
  User.protobuf_fields.should =~ protobuf_fields
@@ -76,19 +76,20 @@ describe Protoable::Serialization do
76
76
  describe "#protoable_attributes" do
77
77
  let(:attributes) {
78
78
  {
79
- :first_name => "foo",
80
- :last_name => "bar",
79
+ :guid => "foo",
80
+ :first_name => "bar",
81
+ :last_name => "baz",
81
82
  :email => "foo@test.co"
82
83
  }
83
84
  }
84
- let(:protoable_attributes) { { :name => user.name, :email => user.email, :tags => nil } }
85
+ let(:protoable_attributes) { { :guid => user.guid, :name => user.name, :email => user.email } }
85
86
 
86
87
  it "returns a hash of protobuf fields that this object has getters for" do
87
88
  user.protoable_attributes.should eq protoable_attributes
88
89
  end
89
90
 
90
91
  it "converts attributes values for protobuf messages" do
91
- user.should_receive(:_protobuf_convert_columns_to_fields).any_number_of_times
92
+ user.should_receive(:_protobuf_convert_attributes_to_fields).any_number_of_times
92
93
  user.protoable_attributes
93
94
  end
94
95
  end
@@ -11,6 +11,7 @@ end
11
11
 
12
12
  ActiveRecord::Schema.define(:version => 1) do
13
13
  create_table :users do |t|
14
+ t.string :guid
14
15
  t.string :first_name
15
16
  t.string :last_name
16
17
  t.string :email
@@ -0,0 +1,10 @@
1
+ message UserMessage {
2
+ optional string guid = 1;
3
+ optional string name = 2;
4
+ optional string email = 3;
5
+ }
6
+
7
+ message UserSearchMessage {
8
+ repeated string guid = 1;
9
+ repeated string email = 2;
10
+ }
@@ -1,6 +1,12 @@
1
1
  class User < ActiveRecord::Base
2
2
  include Protoable
3
3
 
4
+ scope :by_guid, lambda { |*guids| where(:guid => guids) }
5
+ scope :by_email, lambda { |*emails| where(:email => emails) }
6
+
7
+ attribute_from_proto :first_name, :extract_first_name
8
+ attribute_from_proto :last_name, :extract_last_name
9
+
4
10
  def self.convert_base64_to_string(value)
5
11
  value
6
12
  end
@@ -17,7 +23,6 @@ class User < ActiveRecord::Base
17
23
 
18
24
  first_name
19
25
  end
20
- transform_column :first_name, :extract_first_name
21
26
 
22
27
  def self.extract_last_name(proto)
23
28
  if proto.has_field?(:name)
@@ -28,7 +33,6 @@ class User < ActiveRecord::Base
28
33
 
29
34
  last_name
30
35
  end
31
- transform_column :last_name, :extract_last_name
32
36
 
33
37
  def name
34
38
  "#{first_name} #{last_name}"
@@ -1,9 +1,26 @@
1
+ ##
2
+ # This file is auto-generated. DO NOT EDIT!
3
+ #
1
4
  require 'protobuf/message'
2
5
 
3
- module Proto
4
- class User < ::Protobuf::Message
5
- optional ::Protobuf::Field::StringField, :name, 1
6
- optional ::Protobuf::Field::StringField, :email, 2
7
- repeated ::Protobuf::Field::StringField, :tags, 3
8
- end
6
+
7
+ ##
8
+ # Message Classes
9
+ #
10
+ class UserMessage < ::Protobuf::Message; end
11
+ class UserSearchMessage < ::Protobuf::Message; end
12
+
13
+ ##
14
+ # Message Fields
15
+ #
16
+ class UserMessage
17
+ optional ::Protobuf::Field::StringField, :guid, 1
18
+ optional ::Protobuf::Field::StringField, :name, 2
19
+ optional ::Protobuf::Field::StringField, :email, 3
9
20
  end
21
+
22
+ class UserSearchMessage
23
+ repeated ::Protobuf::Field::StringField, :guid, 1
24
+ repeated ::Protobuf::Field::StringField, :email, 2
25
+ end
26
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protobuf-activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-09 00:00:00.000000000 Z
12
+ date: 2012-11-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -207,17 +207,21 @@ files:
207
207
  - lib/protoable/errors.rb
208
208
  - lib/protoable/fields.rb
209
209
  - lib/protoable/persistence.rb
210
+ - lib/protoable/scope.rb
210
211
  - lib/protoable/serialization.rb
211
212
  - lib/protobuf-activerecord.rb
212
213
  - lib/protobuf/activerecord/version.rb
214
+ - lib/protobuf_ext/message.rb
213
215
  - protobuf-activerecord.gemspec
214
216
  - spec/protoable/convert_spec.rb
215
217
  - spec/protoable/fields_spec.rb
216
218
  - spec/protoable/persistence_spec.rb
219
+ - spec/protoable/scope_spec.rb
217
220
  - spec/protoable/serialization_spec.rb
218
221
  - spec/spec_helper.rb
219
222
  - spec/support/db.rb
220
223
  - spec/support/db/setup.rb
224
+ - spec/support/definitions/user.proto
221
225
  - spec/support/models.rb
222
226
  - spec/support/models/user.rb
223
227
  - spec/support/protobuf.rb
@@ -236,7 +240,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
236
240
  version: '0'
237
241
  segments:
238
242
  - 0
239
- hash: -2949215824244847758
243
+ hash: -185204863767589185
240
244
  required_rubygems_version: !ruby/object:Gem::Requirement
241
245
  none: false
242
246
  requirements:
@@ -245,7 +249,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
245
249
  version: '0'
246
250
  segments:
247
251
  - 0
248
- hash: -2949215824244847758
252
+ hash: -185204863767589185
249
253
  requirements: []
250
254
  rubyforge_project:
251
255
  rubygems_version: 1.8.24
@@ -256,10 +260,12 @@ test_files:
256
260
  - spec/protoable/convert_spec.rb
257
261
  - spec/protoable/fields_spec.rb
258
262
  - spec/protoable/persistence_spec.rb
263
+ - spec/protoable/scope_spec.rb
259
264
  - spec/protoable/serialization_spec.rb
260
265
  - spec/spec_helper.rb
261
266
  - spec/support/db.rb
262
267
  - spec/support/db/setup.rb
268
+ - spec/support/definitions/user.proto
263
269
  - spec/support/models.rb
264
270
  - spec/support/models/user.rb
265
271
  - spec/support/protobuf.rb