rpc-mapper 0.2.1 → 0.3.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/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
|