protobuf-activerecord 1.0.5 → 1.1.0

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