protobuf-activerecord 2.1.0 → 3.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/Gemfile +0 -4
- data/lib/protobuf-activerecord.rb +26 -3
- data/lib/protobuf/active_record/attribute_methods.rb +35 -0
- data/lib/protobuf/active_record/columns.rb +74 -0
- data/lib/protobuf/active_record/config.rb +11 -0
- data/lib/protobuf/active_record/errors.rb +23 -0
- data/lib/protobuf/active_record/mass_assignment_security.rb +16 -0
- data/lib/protobuf/active_record/mass_assignment_security/persistence.rb +57 -0
- data/lib/protobuf/active_record/mass_assignment_security/transformation.rb +27 -0
- data/lib/protobuf/active_record/model.rb +32 -0
- data/lib/protobuf/active_record/persistence.rb +55 -0
- data/lib/protobuf/active_record/railtie.rb +11 -0
- data/lib/protobuf/active_record/scope.rb +121 -0
- data/lib/protobuf/active_record/serialization.rb +194 -0
- data/lib/protobuf/active_record/transformation.rb +155 -0
- data/lib/protobuf/active_record/validations.rb +43 -0
- data/lib/protobuf/{activerecord → active_record}/version.rb +1 -1
- data/protobuf-activerecord.gemspec +4 -5
- data/spec/{protoable → protobuf/active_record}/columns_spec.rb +2 -2
- data/spec/{protoable → protobuf/active_record}/persistence_spec.rb +1 -1
- data/spec/{protoable → protobuf/active_record}/scope_spec.rb +1 -8
- data/spec/{protoable → protobuf/active_record}/serialization_spec.rb +2 -2
- data/spec/{protoable → protobuf/active_record}/transformation_spec.rb +12 -12
- data/spec/support/models/user.rb +1 -1
- metadata +59 -68
- data/lib/protobuf/activerecord/protoable.rb +0 -19
- data/lib/protobuf/activerecord/protoable/columns.rb +0 -56
- data/lib/protobuf/activerecord/protoable/errors.rb +0 -22
- data/lib/protobuf/activerecord/protoable/persistence.rb +0 -56
- data/lib/protobuf/activerecord/protoable/scope.rb +0 -121
- data/lib/protobuf/activerecord/protoable/serialization.rb +0 -190
- data/lib/protobuf/activerecord/protoable/transformation.rb +0 -158
- data/lib/protobuf/activerecord/protoable/validations.rb +0 -39
- data/lib/protobuf/activerecord/protobuf_ext/message.rb +0 -16
@@ -1,22 +0,0 @@
|
|
1
|
-
module Protoable
|
2
|
-
|
3
|
-
# = Protoable errors
|
4
|
-
#
|
5
|
-
# Generic Protoable exception class
|
6
|
-
class ProtoableError < StandardError
|
7
|
-
end
|
8
|
-
|
9
|
-
# Raised by Protoable.attribute_from_proto when the transformer method
|
10
|
-
# given is not callable.
|
11
|
-
class AttributeTransformerError < ProtoableError
|
12
|
-
end
|
13
|
-
|
14
|
-
# Raised by Protoable.field_from_record when the convert method
|
15
|
-
# given not callable.
|
16
|
-
class FieldTransformerError < ProtoableError
|
17
|
-
end
|
18
|
-
|
19
|
-
# Raised by Protoable.field_scope when given scope is not defined.
|
20
|
-
class SearchScopeError < ProtoableError
|
21
|
-
end
|
22
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
module Protoable
|
2
|
-
module Persistence
|
3
|
-
def self.included(klass)
|
4
|
-
klass.extend Protoable::Persistence::ClassMethods
|
5
|
-
|
6
|
-
klass.class_eval do
|
7
|
-
# Override Active Record's initialize method so it can accept a protobuf
|
8
|
-
# message as it's attributes. Need to do it in class_eval block since initialize
|
9
|
-
# is defined in ActiveRecord::Base.
|
10
|
-
# :noapi:
|
11
|
-
def initialize(*args)
|
12
|
-
args[0] = attributes_from_proto(args.first) if args.first.is_a?(::Protobuf::Message)
|
13
|
-
|
14
|
-
super(*args)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module ClassMethods
|
20
|
-
# :nodoc:
|
21
|
-
def create(attributes, options = {}, &block)
|
22
|
-
attributes = attributes_from_proto(attributes) if attributes.is_a?(::Protobuf::Message)
|
23
|
-
|
24
|
-
super(attributes, options)
|
25
|
-
end
|
26
|
-
|
27
|
-
# :nodoc:
|
28
|
-
def create!(attributes, options = {}, &block)
|
29
|
-
attributes = attributes_from_proto(attributes) if attributes.is_a?(::Protobuf::Message)
|
30
|
-
|
31
|
-
super(attributes, options)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# :nodoc:
|
36
|
-
def assign_attributes(attributes, options = {})
|
37
|
-
attributes = attributes_from_proto(attributes) if attributes.is_a?(::Protobuf::Message)
|
38
|
-
|
39
|
-
super(attributes, options)
|
40
|
-
end
|
41
|
-
|
42
|
-
# :nodoc:
|
43
|
-
def update_attributes(attributes, options = {})
|
44
|
-
attributes = attributes_from_proto(attributes) if attributes.is_a?(::Protobuf::Message)
|
45
|
-
|
46
|
-
super(attributes, options)
|
47
|
-
end
|
48
|
-
|
49
|
-
# :nodoc:
|
50
|
-
def update_attributes!(attributes, options = {})
|
51
|
-
attributes = attributes_from_proto(attributes) if attributes.is_a?(::Protobuf::Message)
|
52
|
-
|
53
|
-
super(attributes, options)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,121 +0,0 @@
|
|
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 :scope_from_proto, :search_scope
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
# Define fields that should be searchable via `search_scope`. Accepts a
|
13
|
-
# protobuf field and an already defined scope. If no scope is specified,
|
14
|
-
# the scope will be the field name, prefixed with `by_` (e.g. when the
|
15
|
-
# field is :guid, the scope will be :by_guid).
|
16
|
-
#
|
17
|
-
# Optionally, a parser can be provided that will be called, passing the
|
18
|
-
# field value as an argument. This allows custom data parsers to be used
|
19
|
-
# so that they don't have to be handled by scopes. Parsers can be procs,
|
20
|
-
# lambdas, or symbolized method names and must accept the value of the
|
21
|
-
# field as a parameter.
|
22
|
-
#
|
23
|
-
# > **Deprecated usage**: Previous versions required the scope to be passed
|
24
|
-
# as a second argument. This has been replaced with the new hash-style
|
25
|
-
# options or simply relying on the defaults. While it will still work
|
26
|
-
# until v3.0 is released, it has been deprecated.
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# Examples:
|
30
|
-
#
|
31
|
-
# class User < ActiveRecord::Base
|
32
|
-
# scope :by_guid, lambda { |*guids| where(:guid => guids) }
|
33
|
-
# scope :custom_guid_scope, lambda { |*guids| where(:guid => guids) }
|
34
|
-
#
|
35
|
-
# # Equivalent to `field_scope :guid, :by_guid`
|
36
|
-
# field_scope :guid
|
37
|
-
#
|
38
|
-
# # With a custom scope
|
39
|
-
# field_scope :guid, :scope => :custom_guid_scope
|
40
|
-
#
|
41
|
-
# # With a custom parser that converts the value to an integer
|
42
|
-
# field_scope :guid, :scope => :custom_guid_scope, :parser => lambda { |value| value.to_i }
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
def field_scope(field, *args)
|
46
|
-
# TODO: For backwards compatibility. Remove this in the next major release.
|
47
|
-
options = args.extract_options!
|
48
|
-
|
49
|
-
scope_name = case
|
50
|
-
when args.present? then
|
51
|
-
warn "WARNING: Passing scopes directly to field_scope is deprecated and will be removed in 3.0. Use :scope => xxx instead."
|
52
|
-
args.first
|
53
|
-
when options.include?(:scope) then
|
54
|
-
options[:scope]
|
55
|
-
else
|
56
|
-
# When no scope is defined, assume the scope is the field, prefixed with `by_`
|
57
|
-
:"by_#{field}"
|
58
|
-
end
|
59
|
-
searchable_fields[field] = scope_name
|
60
|
-
|
61
|
-
searchable_field_parsers[field] = options[:parser] if options[:parser]
|
62
|
-
end
|
63
|
-
|
64
|
-
# :noapi:
|
65
|
-
def parse_search_values(proto, field)
|
66
|
-
value = proto.__send__(field)
|
67
|
-
|
68
|
-
if searchable_field_parsers[field]
|
69
|
-
parser = searchable_field_parsers[field]
|
70
|
-
|
71
|
-
if parser.respond_to?(:to_sym)
|
72
|
-
value = self.__send__(parser.to_sym, value)
|
73
|
-
else
|
74
|
-
value = parser.call(value)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
values = [ value ].flatten
|
79
|
-
values.map!(&:to_i) if proto.get_field_by_name(field).enum?
|
80
|
-
values
|
81
|
-
end
|
82
|
-
|
83
|
-
# Builds and returns a Arel relation based on the fields that are present
|
84
|
-
# in the given protobuf message using the searchable fields to determine
|
85
|
-
# what scopes to use. Provides several aliases for variety.
|
86
|
-
#
|
87
|
-
# Examples:
|
88
|
-
#
|
89
|
-
# # Search starting with the default scope and searchable fields
|
90
|
-
# User.search_scope(request)
|
91
|
-
# User.by_fields(request)
|
92
|
-
# User.scope_from_proto(request)
|
93
|
-
#
|
94
|
-
def search_scope(proto)
|
95
|
-
relation = scoped # Get an ARel relation to build off of
|
96
|
-
|
97
|
-
searchable_fields.each do |field, scope_name|
|
98
|
-
next unless proto.respond_to_and_has_and_present?(field)
|
99
|
-
|
100
|
-
unless self.respond_to?(scope_name)
|
101
|
-
raise Protoable::SearchScopeError, "Undefined scope :#{scope_name}."
|
102
|
-
end
|
103
|
-
|
104
|
-
search_values = parse_search_values(proto, field)
|
105
|
-
relation = relation.__send__(scope_name, *search_values)
|
106
|
-
end
|
107
|
-
|
108
|
-
return relation
|
109
|
-
end
|
110
|
-
|
111
|
-
# :noapi:
|
112
|
-
def searchable_fields
|
113
|
-
@_searchable_fields ||= {}
|
114
|
-
end
|
115
|
-
|
116
|
-
# :noapi:
|
117
|
-
def searchable_field_parsers
|
118
|
-
@_searchable_field_parsers ||= {}
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
@@ -1,190 +0,0 @@
|
|
1
|
-
require 'heredity/inheritable_class_instance_variables'
|
2
|
-
|
3
|
-
module Protoable
|
4
|
-
module Serialization
|
5
|
-
def self.included(klass)
|
6
|
-
klass.extend Protoable::Serialization::ClassMethods
|
7
|
-
klass.__send__(:include, ::Heredity::InheritableClassInstanceVariables)
|
8
|
-
|
9
|
-
klass.class_eval do
|
10
|
-
class << self
|
11
|
-
attr_accessor :_protobuf_field_transformers, :_protobuf_field_options
|
12
|
-
end
|
13
|
-
|
14
|
-
@_protobuf_field_transformers = {}
|
15
|
-
@_protobuf_field_options = {}
|
16
|
-
|
17
|
-
inheritable_attributes :_protobuf_field_transformers, :_protobuf_field_options,
|
18
|
-
:protobuf_message
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
module ClassMethods
|
23
|
-
# :nodoc:
|
24
|
-
def _protobuf_convert_attributes_to_fields(key, value)
|
25
|
-
return value if value.nil?
|
26
|
-
|
27
|
-
value = case
|
28
|
-
when _protobuf_date_column?(key) then
|
29
|
-
value.to_time.to_i
|
30
|
-
when _protobuf_datetime_column?(key) then
|
31
|
-
value.to_i
|
32
|
-
when _protobuf_time_column?(key) then
|
33
|
-
value.to_i
|
34
|
-
when _protobuf_timestamp_column?(key) then
|
35
|
-
value.to_i
|
36
|
-
else
|
37
|
-
value
|
38
|
-
end
|
39
|
-
|
40
|
-
return value
|
41
|
-
end
|
42
|
-
|
43
|
-
# Define a field transformation from a record. Accepts a Symbol,
|
44
|
-
# callable, or block that is called with the record being serialized.
|
45
|
-
#
|
46
|
-
# When given a callable or block, it is directly used to convert the field.
|
47
|
-
#
|
48
|
-
# When a symbol is given, it extracts the method with the same name.
|
49
|
-
#
|
50
|
-
# The callable or method must accept a single parameter, which is the
|
51
|
-
# proto message.
|
52
|
-
#
|
53
|
-
# Examples:
|
54
|
-
# field_from_record :public_key, :convert_public_key_to_proto
|
55
|
-
# field_from_record :status, lambda { |record| # Do some stuff... }
|
56
|
-
# field_from_record :status do |record|
|
57
|
-
# # Do some blocky stuff...
|
58
|
-
# end
|
59
|
-
#
|
60
|
-
def field_from_record(field, transformer = nil, &block)
|
61
|
-
transformer ||= block
|
62
|
-
|
63
|
-
if transformer.is_a?(Symbol)
|
64
|
-
callable = lambda { |value| self.__send__(transformer, value) }
|
65
|
-
else
|
66
|
-
callable = transformer
|
67
|
-
end
|
68
|
-
|
69
|
-
unless callable.respond_to?(:call)
|
70
|
-
raise FieldTransformerError, 'Attribute transformers need a callable or block!'
|
71
|
-
end
|
72
|
-
|
73
|
-
_protobuf_field_transformers[field.to_sym] = callable
|
74
|
-
end
|
75
|
-
|
76
|
-
# Define the protobuf message class that should be used to serialize the
|
77
|
-
# object to protobuf. Accepts a string or symbol and an options hash.
|
78
|
-
#
|
79
|
-
# When protobuf_message is declared, Protoable automatically extracts the
|
80
|
-
# fields from the message and automatically adds a to_proto method that
|
81
|
-
# serializes the object to protobuf.
|
82
|
-
#
|
83
|
-
# The fields that will be automatically serialized can be configured by
|
84
|
-
# passing :only or :except in the options hash. If :only is specified, only
|
85
|
-
# the specified fields will be serialized. If :except is specified, all
|
86
|
-
# field except the specified fields will be serialized.
|
87
|
-
#
|
88
|
-
# By default, deprecated fields will be serialized. To exclude deprecated
|
89
|
-
# fields, pass :deprecated => false in the options hash.
|
90
|
-
#
|
91
|
-
# Examples:
|
92
|
-
# protobuf_message :user_message
|
93
|
-
# protobuf_message "UserMessage"
|
94
|
-
# protobuf_message "Namespaced::UserMessage"
|
95
|
-
# protobuf_message :user_message, :only => [ :guid, :name ]
|
96
|
-
# protobuf_message :user_message, :except => :email_domain
|
97
|
-
# protobuf_message :user_message, :except => :email_domain, :deprecated => false
|
98
|
-
#
|
99
|
-
def protobuf_message(message = nil, options = {})
|
100
|
-
unless message.nil?
|
101
|
-
@protobuf_message = message.to_s.classify.constantize
|
102
|
-
|
103
|
-
self._protobuf_field_options = options
|
104
|
-
|
105
|
-
define_method(:to_proto) do |options = {}|
|
106
|
-
self.class.protobuf_message.new(self.fields_from_record(options))
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
@protobuf_message
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# :nodoc:
|
115
|
-
def _filter_field_attributes(options = {})
|
116
|
-
options = _normalize_options(options)
|
117
|
-
|
118
|
-
fields = _filtered_fields(options)
|
119
|
-
fields &= [ options[:only] ].flatten if options[:only].present?
|
120
|
-
fields -= [ options[:except] ].flatten if options[:except].present?
|
121
|
-
|
122
|
-
fields
|
123
|
-
end
|
124
|
-
|
125
|
-
# :nodoc:
|
126
|
-
def _filtered_fields(options = {})
|
127
|
-
exclude_deprecated = ! options.fetch(:deprecated, true)
|
128
|
-
|
129
|
-
fields = self.class.protobuf_message.all_fields.map do |field|
|
130
|
-
next if field.nil?
|
131
|
-
next if exclude_deprecated && field.deprecated?
|
132
|
-
field.name.to_sym
|
133
|
-
end
|
134
|
-
fields.compact!
|
135
|
-
|
136
|
-
fields
|
137
|
-
end
|
138
|
-
|
139
|
-
# :nodoc:
|
140
|
-
def _normalize_options(options)
|
141
|
-
options ||= {}
|
142
|
-
options[:only] ||= [] if options.fetch(:except, false)
|
143
|
-
options[:except] ||= [] if options.fetch(:only, false)
|
144
|
-
|
145
|
-
self.class._protobuf_field_options.merge(options)
|
146
|
-
end
|
147
|
-
|
148
|
-
# Extracts attributes that correspond to fields on the specified protobuf
|
149
|
-
# message, performing any necessary column conversions on them. Accepts a
|
150
|
-
# hash of options for specifying which fields should be serialized.
|
151
|
-
#
|
152
|
-
# Examples:
|
153
|
-
# fields_from_record(:only => [ :guid, :name ])
|
154
|
-
# fields_from_record(:except => :email_domain)
|
155
|
-
# fields_from_record(:include => :email_domain)
|
156
|
-
# fields_from_record(:except => :email_domain, :deprecated => false)
|
157
|
-
#
|
158
|
-
def fields_from_record(options = {})
|
159
|
-
field_attributes = _filter_field_attributes(options)
|
160
|
-
field_attributes += [ options.fetch(:include, []) ]
|
161
|
-
field_attributes.flatten!
|
162
|
-
field_attributes.compact!
|
163
|
-
field_attributes.uniq!
|
164
|
-
|
165
|
-
field_attributes = field_attributes.inject({}) do |hash, field|
|
166
|
-
if _protobuf_field_transformers.has_key?(field)
|
167
|
-
hash[field] = _protobuf_field_transformers[field].call(self)
|
168
|
-
else
|
169
|
-
value = respond_to?(field) ? __send__(field) : nil
|
170
|
-
hash[field] = _protobuf_convert_attributes_to_fields(field, value)
|
171
|
-
end
|
172
|
-
hash
|
173
|
-
end
|
174
|
-
|
175
|
-
field_attributes
|
176
|
-
end
|
177
|
-
|
178
|
-
private
|
179
|
-
|
180
|
-
# :nodoc:
|
181
|
-
def _protobuf_convert_attributes_to_fields(field, value)
|
182
|
-
self.class._protobuf_convert_attributes_to_fields(field, value)
|
183
|
-
end
|
184
|
-
|
185
|
-
# :nodoc:
|
186
|
-
def _protobuf_field_transformers
|
187
|
-
self.class._protobuf_field_transformers
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
@@ -1,158 +0,0 @@
|
|
1
|
-
require 'heredity/inheritable_class_instance_variables'
|
2
|
-
|
3
|
-
module Protoable
|
4
|
-
module Transformation
|
5
|
-
def self.included(klass)
|
6
|
-
klass.extend Protoable::Transformation::ClassMethods
|
7
|
-
klass.__send__(:include, ::Heredity::InheritableClassInstanceVariables)
|
8
|
-
|
9
|
-
klass.class_eval do
|
10
|
-
class << self
|
11
|
-
attr_accessor :_protobuf_attribute_transformers
|
12
|
-
end
|
13
|
-
|
14
|
-
@_protobuf_attribute_transformers = {}
|
15
|
-
|
16
|
-
inheritable_attributes :_protobuf_attribute_transformers
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
module ClassMethods
|
21
|
-
# Filters accessible attributes that exist in the given protobuf message's
|
22
|
-
# fields or have attribute transformers defined for them.
|
23
|
-
#
|
24
|
-
# Returns a hash of attribute fields with their respective values.
|
25
|
-
#
|
26
|
-
# :nodoc:
|
27
|
-
def _filter_attribute_fields(proto)
|
28
|
-
fields = proto.to_hash
|
29
|
-
fields.select! do |key, value|
|
30
|
-
field = proto.get_field_by_name(key) || proto.get_ext_field_by_name(key)
|
31
|
-
proto.has_field?(key) && !field.repeated?
|
32
|
-
end
|
33
|
-
|
34
|
-
attribute_fields = _filtered_attributes.inject({}) do |hash, column_name|
|
35
|
-
symbolized_column = column_name.to_sym
|
36
|
-
|
37
|
-
if fields.has_key?(symbolized_column) ||
|
38
|
-
_protobuf_attribute_transformers.has_key?(symbolized_column)
|
39
|
-
hash[symbolized_column] = fields[symbolized_column]
|
40
|
-
end
|
41
|
-
|
42
|
-
hash
|
43
|
-
end
|
44
|
-
|
45
|
-
attribute_fields
|
46
|
-
end
|
47
|
-
|
48
|
-
# Filters protected attributes from the available attributes list. When
|
49
|
-
# set through accessible attributes, returns the accessible attributes.
|
50
|
-
# When set through protected attributes, returns the attributes minus any
|
51
|
-
# protected attributes.
|
52
|
-
#
|
53
|
-
# :nodoc:
|
54
|
-
def _filtered_attributes
|
55
|
-
return accessible_attributes.to_a if accessible_attributes.present?
|
56
|
-
|
57
|
-
return self.attribute_names - protected_attributes.to_a
|
58
|
-
end
|
59
|
-
|
60
|
-
# :nodoc:
|
61
|
-
def _protobuf_convert_fields_to_columns(key, value)
|
62
|
-
return value if value.nil?
|
63
|
-
|
64
|
-
value = case
|
65
|
-
when _protobuf_date_column?(key) then
|
66
|
-
convert_int64_to_date(value)
|
67
|
-
when _protobuf_datetime_column?(key) then
|
68
|
-
convert_int64_to_datetime(value)
|
69
|
-
when _protobuf_time_column?(key) then
|
70
|
-
convert_int64_to_time(value)
|
71
|
-
when _protobuf_timestamp_column?(key) then
|
72
|
-
convert_int64_to_time(value)
|
73
|
-
else
|
74
|
-
value
|
75
|
-
end
|
76
|
-
|
77
|
-
return value
|
78
|
-
end
|
79
|
-
|
80
|
-
# Define an attribute transformation from protobuf. Accepts a Symbol,
|
81
|
-
# callable, or block.
|
82
|
-
#
|
83
|
-
# When given a callable or block, it is directly used to convert the field.
|
84
|
-
#
|
85
|
-
# When a symbol is given, it extracts the method with the same name.
|
86
|
-
#
|
87
|
-
# The callable or method must accept a single parameter, which is the
|
88
|
-
# proto message.
|
89
|
-
#
|
90
|
-
# Examples:
|
91
|
-
# attribute_from_proto :public_key, :extract_public_key_from_proto
|
92
|
-
# attribute_from_proto :status, lambda { |proto| # Do some stuff... }
|
93
|
-
# attribute_from_proto :status do |proto|
|
94
|
-
# # Do some blocky stuff...
|
95
|
-
# end
|
96
|
-
#
|
97
|
-
def attribute_from_proto(attribute, transformer = nil, &block)
|
98
|
-
transformer ||= block
|
99
|
-
|
100
|
-
if transformer.is_a?(Symbol)
|
101
|
-
callable = lambda { |value| self.__send__(transformer, value) }
|
102
|
-
else
|
103
|
-
callable = transformer
|
104
|
-
end
|
105
|
-
|
106
|
-
unless callable.respond_to?(:call)
|
107
|
-
raise AttributeTransformerError, 'Attribute transformers need a callable or block!'
|
108
|
-
end
|
109
|
-
|
110
|
-
_protobuf_attribute_transformers[attribute.to_sym] = callable
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
# Creates a hash of attributes from a given protobuf message.
|
115
|
-
#
|
116
|
-
# It converts and transforms field values using the field converters and
|
117
|
-
# attribute transformers, ignoring repeated and nil fields.
|
118
|
-
#
|
119
|
-
def attributes_from_proto(proto)
|
120
|
-
attribute_fields = _filter_attribute_fields(proto)
|
121
|
-
|
122
|
-
attributes = attribute_fields.inject({}) do |hash, (key, value)|
|
123
|
-
if _protobuf_attribute_transformers.has_key?(key)
|
124
|
-
attribute = _protobuf_attribute_transformers[key].call(proto)
|
125
|
-
hash[key] = attribute unless attribute.nil?
|
126
|
-
else
|
127
|
-
hash[key] = _protobuf_convert_fields_to_columns(key, value)
|
128
|
-
end
|
129
|
-
|
130
|
-
hash
|
131
|
-
end
|
132
|
-
|
133
|
-
attributes
|
134
|
-
end
|
135
|
-
|
136
|
-
# :nodoc:
|
137
|
-
def convert_int64_to_time(int64)
|
138
|
-
Time.at(int64.to_i)
|
139
|
-
end
|
140
|
-
|
141
|
-
# :nodoc:
|
142
|
-
def convert_int64_to_date(int64)
|
143
|
-
convert_int64_to_time(int64).utc.to_date
|
144
|
-
end
|
145
|
-
|
146
|
-
# :nodoc:
|
147
|
-
def convert_int64_to_datetime(int64)
|
148
|
-
convert_int64_to_time(int64).to_datetime
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Calls up to the class version of the method.
|
153
|
-
#
|
154
|
-
def attributes_from_proto(proto)
|
155
|
-
self.class.attributes_from_proto(proto)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|