rpc-mapper 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rpc_mapper/association.rb +187 -19
- data/lib/rpc_mapper/association_preload.rb +26 -7
- data/lib/rpc_mapper/associations/external.rb +11 -4
- data/lib/rpc_mapper/errors.rb +28 -3
- data/lib/rpc_mapper/relation.rb +24 -1
- data/lib/rpc_mapper/relation/query_methods.rb +14 -8
- data/lib/rpc_mapper/version.rb +3 -3
- metadata +7 -7
@@ -2,6 +2,23 @@ module RPCMapper::Association; end
|
|
2
2
|
|
3
3
|
module RPCMapper::Association
|
4
4
|
|
5
|
+
##
|
6
|
+
# Association::Base
|
7
|
+
#
|
8
|
+
# This is the base class for all associations. It defines the basic structure
|
9
|
+
# of an association. The basic nomenclature is as follows:
|
10
|
+
#
|
11
|
+
# TODO: Clean up this nomenclature. Source and Target should be switched. From
|
12
|
+
# the configuration side this is already done but the internal naming is backwards.
|
13
|
+
#
|
14
|
+
# Source: The start point of the association. The source class is the class
|
15
|
+
# on which the association is defined.
|
16
|
+
#
|
17
|
+
# Proxy: On a through association the proxy is the class on which the target
|
18
|
+
# association lives
|
19
|
+
#
|
20
|
+
# Target: The class that will ultimately be returned by the association
|
21
|
+
#
|
5
22
|
class Base
|
6
23
|
attr_accessor :source_klass, :id, :options
|
7
24
|
|
@@ -16,7 +33,10 @@ module RPCMapper::Association
|
|
16
33
|
end
|
17
34
|
|
18
35
|
def polymorphic?
|
19
|
-
raise
|
36
|
+
raise(
|
37
|
+
NotImplementedError,
|
38
|
+
"You must define how your association is polymorphic in subclasses."
|
39
|
+
)
|
20
40
|
end
|
21
41
|
|
22
42
|
def collection?
|
@@ -32,11 +52,33 @@ module RPCMapper::Association
|
|
32
52
|
end
|
33
53
|
|
34
54
|
def target_klass(object=nil)
|
35
|
-
|
36
|
-
|
55
|
+
if options[:polymorphic] && object
|
56
|
+
poly_type = object.is_a?(RPCMapper::Base) ? object.send("#{id}_type") : object
|
57
|
+
end
|
58
|
+
|
59
|
+
klass = if poly_type
|
60
|
+
type_string = [
|
61
|
+
options[:polymorphic_namespace],
|
62
|
+
sanitize_type_attribute(poly_type)
|
63
|
+
].compact.join('::')
|
64
|
+
begin
|
65
|
+
eval(type_string)
|
66
|
+
rescue NameError => err
|
67
|
+
raise(
|
68
|
+
RPCMapper::PolymorphicAssociationTypeError,
|
69
|
+
"No constant defined called #{type_string}"
|
70
|
+
)
|
71
|
+
end
|
37
72
|
else
|
38
|
-
|
39
|
-
|
73
|
+
unless options[:class_name]
|
74
|
+
raise(ArgumentError,
|
75
|
+
":class_name option required for association declaration.")
|
76
|
+
end
|
77
|
+
|
78
|
+
unless options[:class_name] =~ /^::/
|
79
|
+
options[:class_name] = "::#{options[:class_name]}"
|
80
|
+
end
|
81
|
+
|
40
82
|
eval(options[:class_name])
|
41
83
|
end
|
42
84
|
|
@@ -47,9 +89,12 @@ module RPCMapper::Association
|
|
47
89
|
raise NotImplementedError, "You must define scope in subclasses"
|
48
90
|
end
|
49
91
|
|
50
|
-
# TRP: Only eager loadable if association query does not depend on instance
|
92
|
+
# TRP: Only eager loadable if association query does not depend on instance
|
93
|
+
# data
|
51
94
|
def eager_loadable?
|
52
|
-
RPCMapper::Relation::FINDER_OPTIONS.inject(true)
|
95
|
+
RPCMapper::Relation::FINDER_OPTIONS.inject(true) do |condition, key|
|
96
|
+
condition && !options[key].respond_to?(:call)
|
97
|
+
end
|
53
98
|
end
|
54
99
|
|
55
100
|
protected
|
@@ -66,6 +111,11 @@ module RPCMapper::Association
|
|
66
111
|
end
|
67
112
|
end
|
68
113
|
|
114
|
+
# TRP: Make sure the value looks like a variable syntaxtually
|
115
|
+
def sanitize_type_attribute(string)
|
116
|
+
string.gsub(/[^a-zA-Z]\w*/, '')
|
117
|
+
end
|
118
|
+
|
69
119
|
end
|
70
120
|
|
71
121
|
|
@@ -87,16 +137,26 @@ module RPCMapper::Association
|
|
87
137
|
!!options[:polymorphic]
|
88
138
|
end
|
89
139
|
|
140
|
+
def polymorphic_type
|
141
|
+
"#{id}_type".to_sym
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
90
145
|
# Returns a scope on the target containing this association
|
91
146
|
#
|
92
|
-
# Builds conditions on top of the base_scope generated from any finder
|
147
|
+
# Builds conditions on top of the base_scope generated from any finder
|
148
|
+
# options set with the association
|
93
149
|
#
|
94
150
|
# belongs_to :foo, :foreign_key => :foo_id
|
95
151
|
#
|
96
|
-
# In addition to any finder options included with the association options
|
152
|
+
# In addition to any finder options included with the association options
|
153
|
+
# the following scope will be added:
|
97
154
|
# where(:id => source[:foo_id])
|
155
|
+
#
|
98
156
|
def scope(object)
|
99
|
-
|
157
|
+
if object[self.foreign_key]
|
158
|
+
base_scope(object).where(self.primary_key => object[self.foreign_key])
|
159
|
+
end
|
100
160
|
end
|
101
161
|
|
102
162
|
end
|
@@ -105,27 +165,46 @@ module RPCMapper::Association
|
|
105
165
|
class Has < Base
|
106
166
|
|
107
167
|
def foreign_key
|
108
|
-
super ||
|
168
|
+
super || if self.polymorphic?
|
169
|
+
"#{options[:as]}_id"
|
170
|
+
else
|
171
|
+
"#{RPCMapper::Base.base_class_name(source_klass).downcase}_id"
|
172
|
+
end.to_sym
|
109
173
|
end
|
110
174
|
|
111
175
|
|
176
|
+
##
|
112
177
|
# Returns a scope on the target containing this association
|
113
178
|
#
|
114
|
-
# Builds conditions on top of the base_scope generated from any finder
|
179
|
+
# Builds conditions on top of the base_scope generated from any finder
|
180
|
+
# options set with the association
|
181
|
+
#
|
182
|
+
# has_many :widgets, :class_name => "Widget", :foreign_key => :widget_id
|
183
|
+
# has_many :comments, :as => :parent
|
184
|
+
#
|
185
|
+
# In addition to any finder options included with the association options
|
186
|
+
# the following will be added:
|
115
187
|
#
|
116
|
-
#
|
117
|
-
# has_many :comments, :as => :parent
|
188
|
+
# where(widget_id => source[:id])
|
118
189
|
#
|
119
|
-
# In addition to any finder options included with the association options the following will be added:
|
120
|
-
# where(widget_id => source[:id])
|
121
190
|
# Or for the polymorphic :comments association:
|
122
|
-
#
|
191
|
+
#
|
192
|
+
# where(:parent_id => source[:id], :parent_type => source.class)
|
193
|
+
#
|
123
194
|
def scope(object)
|
124
|
-
|
125
|
-
s =
|
195
|
+
return nil unless object[self.primary_key]
|
196
|
+
s = base_scope(object).where(self.foreign_key => object[self.primary_key])
|
197
|
+
if polymorphic?
|
198
|
+
s = s.where(
|
199
|
+
polymorphic_type => RPCMapper::Base.base_class_name(object.class) )
|
200
|
+
end
|
126
201
|
s
|
127
202
|
end
|
128
203
|
|
204
|
+
def polymorphic_type
|
205
|
+
:"#{options[:as]}_type"
|
206
|
+
end
|
207
|
+
|
129
208
|
def polymorphic?
|
130
209
|
!!options[:as]
|
131
210
|
end
|
@@ -159,4 +238,93 @@ module RPCMapper::Association
|
|
159
238
|
end
|
160
239
|
|
161
240
|
|
241
|
+
class HasManyThrough < HasMany
|
242
|
+
attr_accessor :proxy_association
|
243
|
+
attr_accessor :target_association
|
244
|
+
|
245
|
+
def proxy_association
|
246
|
+
@proxy_association ||= source_klass.defined_associations[options[:through]] ||
|
247
|
+
raise(
|
248
|
+
RPCMapper::AssociationNotFound,
|
249
|
+
":has_many_through: '#{options[:through]}' is not an association " +
|
250
|
+
"on #{source_klass}"
|
251
|
+
)
|
252
|
+
end
|
253
|
+
|
254
|
+
def target_association
|
255
|
+
return @target_association if @target_association
|
256
|
+
|
257
|
+
klass = proxy_association.target_klass
|
258
|
+
@target_association = klass.defined_associations[self.id] ||
|
259
|
+
klass.defined_associations[self.options[:source]] ||
|
260
|
+
raise(RPCMapper::AssociationNotFound,
|
261
|
+
":has_many_through: '#{options[:source] || self.id}' is not an " +
|
262
|
+
"association on #{klass}"
|
263
|
+
)
|
264
|
+
end
|
265
|
+
|
266
|
+
def scope(object)
|
267
|
+
# Which attribute's values should be used on the proxy
|
268
|
+
key = if target_is_has?
|
269
|
+
target_association.primary_key.to_sym
|
270
|
+
else
|
271
|
+
target_association.foreign_key.to_sym
|
272
|
+
end
|
273
|
+
|
274
|
+
# Fetch the ids of all records on the proxy using the correct key
|
275
|
+
proxy_ids = proc do
|
276
|
+
proxy_association.scope(object).select(key).collect(&key)
|
277
|
+
end
|
278
|
+
|
279
|
+
# Use these ids to build a scope on the target object
|
280
|
+
relation = target_klass.scoped
|
281
|
+
|
282
|
+
if target_is_has?
|
283
|
+
relation = relation.where(
|
284
|
+
proc do
|
285
|
+
{ target_association.foreign_key => proxy_ids.call }
|
286
|
+
end
|
287
|
+
)
|
288
|
+
else
|
289
|
+
relation = relation.where(
|
290
|
+
proc do
|
291
|
+
{ target_association.primary_key => proxy_ids.call }
|
292
|
+
end
|
293
|
+
)
|
294
|
+
end
|
295
|
+
|
296
|
+
# Add polymorphic type condition if target is polymorphic and has
|
297
|
+
if target_association.polymorphic? && target_is_has?
|
298
|
+
relation = relation.where(
|
299
|
+
target_association.polymorphic_type =>
|
300
|
+
RPCMapper::Base.base_class_name(proxy_association.target_klass(object))
|
301
|
+
)
|
302
|
+
end
|
303
|
+
|
304
|
+
relation
|
305
|
+
end
|
306
|
+
|
307
|
+
def target_klass
|
308
|
+
target_association.target_klass(options[:source_type])
|
309
|
+
end
|
310
|
+
|
311
|
+
def target_type
|
312
|
+
target_association.type
|
313
|
+
end
|
314
|
+
|
315
|
+
def target_is_has?
|
316
|
+
target_association.is_a?(Has)
|
317
|
+
end
|
318
|
+
|
319
|
+
def type
|
320
|
+
:has_many_through
|
321
|
+
end
|
322
|
+
|
323
|
+
def polymorphic?
|
324
|
+
false
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
|
162
330
|
end
|
@@ -4,30 +4,49 @@ module RPCMapper::AssociationPreload
|
|
4
4
|
def eager_load_associations(original_results, relation)
|
5
5
|
relation.includes_values.each do |association_id|
|
6
6
|
association = self.defined_associations[association_id.to_sym]
|
7
|
-
|
7
|
+
force_fresh = relation.fresh_value
|
8
|
+
|
9
|
+
unless association
|
10
|
+
raise(
|
11
|
+
RPCMapper::AssociationNotFound,
|
12
|
+
"no such association (#{association_id})"
|
13
|
+
)
|
14
|
+
end
|
8
15
|
|
9
16
|
unless association.eager_loadable?
|
10
|
-
raise
|
11
|
-
|
17
|
+
raise(
|
18
|
+
RPCMapper::AssociationPreloadNotSupported,
|
19
|
+
"delayed execution options (block options) cannot be used for eager loaded associations"
|
20
|
+
)
|
12
21
|
end
|
13
22
|
|
23
|
+
options = association.options
|
24
|
+
|
14
25
|
case association.type
|
15
26
|
when :has_many, :has_one
|
16
27
|
fks = original_results.collect { |record| record.send(association.primary_key) }.compact
|
17
28
|
|
18
|
-
pre_records = association.target_klass.where(association.foreign_key => fks).all
|
29
|
+
pre_records = association.target_klass.where(association.foreign_key => fks).all(:fresh => force_fresh)
|
19
30
|
|
20
31
|
original_results.each do |record|
|
21
32
|
pk = record.send(association.primary_key)
|
22
33
|
relevant_records = pre_records.select { |r| r.send(association.foreign_key) == pk }
|
23
|
-
|
34
|
+
|
35
|
+
relevant_records = if association.collection?
|
36
|
+
scope = association.scope(record).where(association.foreign_key => pk)
|
37
|
+
scope.records = relevant_records
|
38
|
+
scope
|
39
|
+
else
|
40
|
+
relevant_records.first
|
41
|
+
end
|
42
|
+
|
24
43
|
record.send("#{association.id}=", relevant_records)
|
25
44
|
end
|
26
45
|
|
27
46
|
when :belongs_to
|
28
47
|
fks = original_results.collect { |record| record.send(association.foreign_key) }.compact
|
29
48
|
|
30
|
-
pre_records = association.target_klass.where(association.primary_key => fks).all
|
49
|
+
pre_records = association.target_klass.where(association.primary_key => fks).all(:fresh => force_fresh)
|
31
50
|
|
32
51
|
original_results.each do |record|
|
33
52
|
fk = record.send(association.foreign_key)
|
@@ -47,4 +66,4 @@ module RPCMapper::AssociationPreload
|
|
47
66
|
receiver.extend ClassMethods
|
48
67
|
receiver.send :include, InstanceMethods
|
49
68
|
end
|
50
|
-
end
|
69
|
+
end
|
@@ -15,7 +15,12 @@ module RPCMapper::Associations
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def has_many(id, options={})
|
18
|
-
|
18
|
+
klass = if options.include?(:through)
|
19
|
+
RPCMapper::Association::HasManyThrough
|
20
|
+
else
|
21
|
+
RPCMapper::Association::HasMany
|
22
|
+
end
|
23
|
+
create_external_association klass.new(self, id, options)
|
19
24
|
end
|
20
25
|
|
21
26
|
protected
|
@@ -27,11 +32,13 @@ module RPCMapper::Associations
|
|
27
32
|
define_method(association.id) do
|
28
33
|
cached_value = instance_variable_get(cache_ivar)
|
29
34
|
|
30
|
-
# TRP: Logic for actually pulling setting the value
|
35
|
+
# TRP: Logic for actually pulling & setting the value
|
31
36
|
unless cached_value
|
32
37
|
scoped = association.scope(self)
|
33
38
|
|
34
|
-
|
39
|
+
if scoped && !scoped.where_values.empty?
|
40
|
+
cached_value = association.collection? ? scoped : scoped.first
|
41
|
+
end
|
35
42
|
|
36
43
|
instance_variable_set(cache_ivar, cached_value)
|
37
44
|
end
|
@@ -57,4 +64,4 @@ module RPCMapper::Associations
|
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
60
|
-
end
|
67
|
+
end
|
data/lib/rpc_mapper/errors.rb
CHANGED
@@ -1,24 +1,49 @@
|
|
1
1
|
module RPCMapper
|
2
2
|
|
3
3
|
# Generic RPCMapper error
|
4
|
+
#
|
4
5
|
class RPCMapperError < StandardError
|
5
6
|
end
|
6
7
|
|
7
8
|
# Raised when RPCMapper cannot find records from a given id or set of ids
|
9
|
+
#
|
8
10
|
class RecordNotFound < RPCMapperError
|
9
11
|
end
|
10
12
|
|
11
|
-
# Raised when RPCMapper cannot save a record through the write_adapter
|
13
|
+
# Raised when RPCMapper cannot save a record through the write_adapter
|
14
|
+
# and save! or update_attributes! was used
|
15
|
+
#
|
12
16
|
class RecordNotSaved < RPCMapperError
|
13
17
|
end
|
14
18
|
|
15
|
-
#
|
19
|
+
# Used for all association related errors
|
20
|
+
#
|
21
|
+
class AssociationError < RPCMapperError
|
22
|
+
end
|
23
|
+
|
24
|
+
# Raised when trying to eager load an association that relies on instance
|
25
|
+
# level data.
|
16
26
|
# class Article < RPCMapper::Base
|
17
27
|
# has_many :comments, :conditions => lambda { |article| ... }
|
18
28
|
# end
|
19
29
|
#
|
20
30
|
# Article.recent.includes(:comments) # Raises AssociationPreloadNotSupported
|
21
|
-
|
31
|
+
#
|
32
|
+
class AssociationPreloadNotSupported < AssociationError
|
33
|
+
end
|
34
|
+
|
35
|
+
# Raised when trying to eager load an association that does not exist or
|
36
|
+
# trying to create a :has_many_through association with a nonexistant
|
37
|
+
# association as the source
|
38
|
+
#
|
39
|
+
class AssociationNotFound < AssociationError
|
40
|
+
end
|
41
|
+
|
42
|
+
# Raised when a polymorphic association type is not present in the specified
|
43
|
+
# scope. Always be sure that any values set for the type attribute on any
|
44
|
+
# polymorphic association are real constants defined in :polymorphic_namespace
|
45
|
+
#
|
46
|
+
class PolymorphicAssociationTypeError < AssociationError
|
22
47
|
end
|
23
48
|
|
24
49
|
end
|
data/lib/rpc_mapper/relation.rb
CHANGED
@@ -6,6 +6,8 @@ require 'rpc_mapper/relation/finder_methods'
|
|
6
6
|
# Used to achieve the chainability of scopes -- methods are delegated back and forth from BM::Base and BM::Relation
|
7
7
|
class RPCMapper::Relation
|
8
8
|
attr_reader :klass
|
9
|
+
attr_accessor :records
|
10
|
+
|
9
11
|
SINGLE_VALUE_METHODS = [:limit, :offset, :from, :fresh]
|
10
12
|
MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :includes, :where, :having]
|
11
13
|
|
@@ -44,23 +46,31 @@ class RPCMapper::Relation
|
|
44
46
|
else
|
45
47
|
hash = SINGLE_VALUE_METHODS.inject({}) do |h, option|
|
46
48
|
value = self.send("#{option}_value")
|
49
|
+
value = call_procs(value)
|
47
50
|
value ? h.merge(option => value) : h
|
48
51
|
end
|
49
52
|
|
50
53
|
hash.merge!((MULTI_VALUE_METHODS - [:select]).inject({}) do |h, option|
|
51
54
|
value = self.send("#{option}_values")
|
55
|
+
value = call_procs(value)
|
52
56
|
value && !value.empty? ? h.merge(option => value.uniq) : h
|
53
57
|
end)
|
54
58
|
|
55
59
|
# TRP: If one of the select options contains a * than select options are ignored
|
56
60
|
if select_values && !select_values.empty? && !select_values.any? { |val| val.to_s.match(/\*$/) }
|
57
|
-
|
61
|
+
value = call_procs(select_values)
|
62
|
+
hash.merge!(:select => value.uniq)
|
58
63
|
end
|
59
64
|
|
60
65
|
hash
|
61
66
|
end
|
62
67
|
end
|
63
68
|
|
69
|
+
def reset_queries
|
70
|
+
@hash = nil
|
71
|
+
@records = nil
|
72
|
+
end
|
73
|
+
|
64
74
|
def to_a
|
65
75
|
@records ||= fetch_records
|
66
76
|
end
|
@@ -127,4 +137,17 @@ class RPCMapper::Relation
|
|
127
137
|
@klass.send(:fetch_records, self)
|
128
138
|
end
|
129
139
|
|
140
|
+
private
|
141
|
+
|
142
|
+
def call_procs(values)
|
143
|
+
case values
|
144
|
+
when Array:
|
145
|
+
values.collect { |v| v.is_a?(Proc) ? v.call : v }
|
146
|
+
when Proc:
|
147
|
+
values.call
|
148
|
+
else
|
149
|
+
values
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
130
153
|
end
|
@@ -8,33 +8,33 @@ module RPCMapper::QueryMethods
|
|
8
8
|
if block_given?
|
9
9
|
to_a.select {|*block_args| yield(*block_args) }
|
10
10
|
else
|
11
|
-
clone.tap { |r| r.select_values += args if
|
11
|
+
clone.tap { |r| r.select_values += args if args_valid? args }
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
def group(*args)
|
16
|
-
clone.tap { |r| r.group_values += args if
|
16
|
+
clone.tap { |r| r.group_values += args if args_valid? args }
|
17
17
|
end
|
18
18
|
|
19
19
|
def order(*args)
|
20
|
-
clone.tap { |r| r.order_values += args if
|
20
|
+
clone.tap { |r| r.order_values += args if args_valid? args }
|
21
21
|
end
|
22
22
|
|
23
23
|
def joins(*args)
|
24
|
-
clone.tap { |r| r.joins_values += args if args
|
24
|
+
clone.tap { |r| r.joins_values += args if args_valid?(args) }
|
25
25
|
end
|
26
26
|
|
27
27
|
def includes(*args)
|
28
28
|
args.reject! { |a| a.nil? }
|
29
|
-
clone.tap { |r| r.includes_values += (r.includes_values + args).flatten.uniq if
|
29
|
+
clone.tap { |r| r.includes_values += (r.includes_values + args).flatten.uniq if args_valid? args }
|
30
30
|
end
|
31
31
|
|
32
32
|
def where(*args)
|
33
|
-
clone.tap { |r| r.where_values += args.compact.
|
33
|
+
clone.tap { |r| r.where_values += args.compact.select { |i| args_valid? i } if args_valid? args }
|
34
34
|
end
|
35
35
|
|
36
36
|
def having(*args)
|
37
|
-
clone.tap { |r| r.having_values += args if
|
37
|
+
clone.tap { |r| r.having_values += args if args_valid? args }
|
38
38
|
end
|
39
39
|
|
40
40
|
def limit(value = true)
|
@@ -52,7 +52,7 @@ module RPCMapper::QueryMethods
|
|
52
52
|
def fresh(val=true)
|
53
53
|
clone.tap do |r|
|
54
54
|
r.fresh_value = val
|
55
|
-
r.
|
55
|
+
r.reset_queries if r.fresh_value
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
@@ -60,4 +60,10 @@ module RPCMapper::QueryMethods
|
|
60
60
|
clone.tap { |r| r.raw_sql_value = raw_sql }
|
61
61
|
end
|
62
62
|
|
63
|
+
protected
|
64
|
+
|
65
|
+
def args_valid?(args)
|
66
|
+
args.respond_to?(:empty?) ? !args.empty? : !!args
|
67
|
+
end
|
68
|
+
|
63
69
|
end
|
data/lib/rpc_mapper/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rpc-mapper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Travis Petticrew
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-02-28 00:00:00 -06:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -179,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
179
|
requirements: []
|
180
180
|
|
181
181
|
rubyforge_project:
|
182
|
-
rubygems_version: 1.
|
182
|
+
rubygems_version: 1.4.2
|
183
183
|
signing_key:
|
184
184
|
specification_version: 3
|
185
185
|
summary: Ruby library for querying and mapping data over RPC
|