arrest 0.0.16 → 0.0.17
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/arrest.rb +4 -0
- data/lib/arrest/abstract_resource.rb +18 -10
- data/lib/arrest/attributes/belongs_to.rb +7 -4
- data/lib/arrest/attributes/belongs_to_attribute.rb +13 -0
- data/lib/arrest/attributes/has_many_attribute.rb +16 -0
- data/lib/arrest/class_utils.rb +11 -0
- data/lib/arrest/helper/has_many_collection.rb +4 -4
- data/lib/arrest/mem_source.rb +130 -9
- data/lib/arrest/root_resource.rb +1 -1
- data/lib/arrest/string_utils.rb +60 -58
- data/lib/arrest/version.rb +1 -1
- data/test/models.rb +10 -0
- data/test/unit.rb +98 -0
- metadata +160 -97
data/lib/arrest.rb
CHANGED
@@ -8,7 +8,11 @@ require "arrest/attributes/has_attributes"
|
|
8
8
|
require "arrest/attributes/attribute"
|
9
9
|
require "arrest/attributes/nested_attribute"
|
10
10
|
require "arrest/attributes/polymorphic_attribute"
|
11
|
+
require "arrest/attributes/belongs_to_attribute"
|
12
|
+
require "arrest/attributes/has_many_attribute"
|
11
13
|
require "arrest/attributes/converter"
|
14
|
+
require "arrest/class_utils.rb"
|
15
|
+
require "arrest/string_utils.rb"
|
12
16
|
require "arrest/handler"
|
13
17
|
require "arrest/source"
|
14
18
|
require "arrest/helper/filter"
|
@@ -50,23 +50,31 @@ module Arrest
|
|
50
50
|
|
51
51
|
def has_many(*args)
|
52
52
|
method_name, options = args
|
53
|
-
|
53
|
+
ids_field_name = (StringUtils.singular(method_name.to_s) + '_ids').to_sym
|
54
54
|
method_name = method_name.to_sym
|
55
|
-
|
56
|
-
|
55
|
+
clazz_name = StringUtils.singular(method_name.to_s)
|
56
|
+
foreign_key = clazz_name + "_id"
|
57
57
|
if options
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
58
|
+
clazz_name = options[:class_name].to_s unless options[:class_name] == nil
|
59
|
+
foreign_key = "#{StringUtils.underscore(clazz_name)}_id"
|
60
|
+
foreign_key = options[:foreign_key].to_s unless options[:foreign_key] == nil
|
62
61
|
end
|
63
|
-
|
62
|
+
|
63
|
+
url_part = method_name.to_s
|
64
|
+
|
65
|
+
hm_attr = HasManyAttribute.new(ids_field_name,
|
66
|
+
method_name,
|
67
|
+
clazz_name,
|
68
|
+
url_part,
|
69
|
+
foreign_key)
|
70
|
+
add_attribute(hm_attr)
|
71
|
+
|
64
72
|
send :define_method, method_name do
|
65
73
|
if @has_many_collections == nil
|
66
74
|
@has_many_collections = {}
|
67
75
|
end
|
68
76
|
if @has_many_collections[method_name] == nil
|
69
|
-
@has_many_collections[method_name] = HasManyCollection.new(self,
|
77
|
+
@has_many_collections[method_name] = HasManyCollection.new(self, hm_attr)
|
70
78
|
end
|
71
79
|
|
72
80
|
@has_many_collections[method_name]
|
@@ -144,7 +152,7 @@ module Arrest
|
|
144
152
|
true
|
145
153
|
end
|
146
154
|
#
|
147
|
-
#
|
155
|
+
# convenience method printing curl command
|
148
156
|
def curl
|
149
157
|
hs = ""
|
150
158
|
Arrest::Source.header_decorator.headers.each_pair do |k,v|
|
@@ -17,28 +17,31 @@ module Arrest
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def create_and_add_attribute(field_name, polymorphic, read_only)
|
20
|
+
def create_and_add_attribute(field_name, polymorphic, read_only, foreign_key, class_name)
|
21
21
|
if polymorphic
|
22
22
|
add_attribute(PolymorphicAttribute.new(field_name.to_sym, read_only))
|
23
23
|
else
|
24
|
-
add_attribute(
|
24
|
+
add_attribute(BelongsToAttribute.new(field_name.to_sym, read_only, String, foreign_key, class_name))
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
def belongs_to(*args)
|
29
29
|
arg = args[0]
|
30
30
|
name = arg.to_s.downcase
|
31
|
-
class_name = StringUtils.classify
|
31
|
+
class_name = StringUtils.classify(name)
|
32
|
+
foreign_key = "#{StringUtils.underscore(ClassUtils.simple_name(self))}_id"
|
32
33
|
params = args[1] unless args.length < 2
|
34
|
+
|
33
35
|
if params
|
34
36
|
read_only = params[:read_only] == true
|
35
37
|
polymorphic = params[:polymorphic] unless params[:polymorphic] == nil
|
36
38
|
class_name = params[:class_name].to_s unless params[:class_name] == nil
|
39
|
+
foreign_key = params[:foreign_key].to_s unless params[:foreign_key] == nil
|
37
40
|
end
|
38
41
|
|
39
42
|
field_name = create_field_name(name, params, polymorphic)
|
40
43
|
|
41
|
-
create_and_add_attribute(field_name, polymorphic, read_only)
|
44
|
+
create_and_add_attribute(field_name, polymorphic, read_only, foreign_key, class_name)
|
42
45
|
|
43
46
|
send :define_method, name do
|
44
47
|
val = self.send(field_name)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Arrest
|
2
|
+
class BelongsToAttribute < Attribute
|
3
|
+
attr_accessor :foreign_key
|
4
|
+
def initialize(name, read_only, field_class, foreign_key, target_class_name)
|
5
|
+
super(name, read_only, field_class)
|
6
|
+
@foreign_key = foreign_key
|
7
|
+
@target_class_name = target_class_name
|
8
|
+
end
|
9
|
+
def target_class
|
10
|
+
Arrest::Source.mod.const_get(@target_class_name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Arrest
|
2
|
+
class HasManyAttribute < Attribute
|
3
|
+
attr_reader :method_name, :clazz_name, :url_part, :foreign_key
|
4
|
+
def initialize(ids_field_name,
|
5
|
+
method_name,
|
6
|
+
clazz_name,
|
7
|
+
url_part,
|
8
|
+
foreign_key)
|
9
|
+
super(ids_field_name, false, Array)
|
10
|
+
@method_name = method_name.to_sym
|
11
|
+
@clazz_name = clazz_name.to_sym
|
12
|
+
@url_part = url_part.to_sym
|
13
|
+
@foreign_key = foreign_key.to_sym
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Arrest
|
2
|
+
class ClassUtils
|
3
|
+
class << self
|
4
|
+
# Returns the simple class name without any preceding modules or namespaces
|
5
|
+
# (removes everything up to the last '::' inclusively from class.name)
|
6
|
+
def simple_name(clazz)
|
7
|
+
clazz.name.gsub(/.*::/,"")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
module Arrest
|
2
2
|
class HasManyCollection #< BasicObject
|
3
|
-
def initialize parent,
|
3
|
+
def initialize parent, has_many_attribute
|
4
4
|
@parent = parent
|
5
|
-
@clazz_name = clazz_name
|
5
|
+
@clazz_name = (StringUtils.classify(has_many_attribute.clazz_name.to_s))
|
6
|
+
@url_part = has_many_attribute.url_part
|
6
7
|
@children = nil
|
7
8
|
@foreign_key_name = (StringUtils.underscore(@parent.class.name).gsub(/^.*\//, '') + '_id').to_sym
|
8
9
|
define_filters
|
@@ -23,10 +24,9 @@ module Arrest
|
|
23
24
|
|
24
25
|
private
|
25
26
|
|
26
|
-
|
27
27
|
def children
|
28
28
|
if @children == nil
|
29
|
-
url = @parent.resource_location + '/' +
|
29
|
+
url = @parent.resource_location + '/' + @url_part.to_s
|
30
30
|
@children = resolved_class.by_url(url)
|
31
31
|
end
|
32
32
|
@children
|
data/lib/arrest/mem_source.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
module Arrest
|
2
|
+
|
3
|
+
Edge = Struct.new(:foreign_key, :name, :id, :tail)
|
4
|
+
|
2
5
|
class MemSource
|
3
6
|
|
4
7
|
attr_accessor :data
|
@@ -8,7 +11,9 @@ module Arrest
|
|
8
11
|
# each having a unique id
|
9
12
|
|
10
13
|
@@collections = {} # maps urls to collections of ids of objects
|
11
|
-
|
14
|
+
|
15
|
+
# For every has_many relation
|
16
|
+
@@edge_matrix = {} # matrix of edges based on node ids for has_many and belongs_to relations
|
12
17
|
|
13
18
|
@@data = {}
|
14
19
|
|
@@ -24,12 +29,25 @@ module Arrest
|
|
24
29
|
@@data
|
25
30
|
end
|
26
31
|
|
32
|
+
def edge_matrix
|
33
|
+
@@edge_matrix
|
34
|
+
end
|
35
|
+
|
36
|
+
def edge_count
|
37
|
+
@@edge_matrix.values.inject(0){|sum, edges| sum + edges.length }
|
38
|
+
end
|
39
|
+
|
40
|
+
def node_count
|
41
|
+
@@edge_matrix.length
|
42
|
+
end
|
43
|
+
|
27
44
|
def initialize
|
28
45
|
@@all_objects = {} # holds all objects of all types,
|
29
46
|
|
30
47
|
@@collections = {} # maps urls to collections of ids of objects
|
31
48
|
@@random = Random.new(42)
|
32
49
|
|
50
|
+
@@edge_matrix = {}
|
33
51
|
end
|
34
52
|
|
35
53
|
|
@@ -61,17 +79,38 @@ module Arrest
|
|
61
79
|
end
|
62
80
|
end
|
63
81
|
|
64
|
-
def
|
82
|
+
def parse_for_has_many_relations(resource_path)
|
83
|
+
matcher = /^.+\/([^\/]+)\/([^\/]+)$/.match(resource_path)
|
84
|
+
return [] unless matcher
|
85
|
+
object_id = matcher[1]
|
86
|
+
relation = matcher[2]
|
87
|
+
|
88
|
+
if (object_id && relation && @@edge_matrix[object_id])
|
89
|
+
result = []
|
90
|
+
@@edge_matrix[object_id].each do |edge|
|
91
|
+
if (edge.name.to_s == relation)
|
92
|
+
result << edge.id
|
93
|
+
end
|
94
|
+
end
|
95
|
+
return result
|
96
|
+
end
|
97
|
+
[]
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_many(sub, filters = {})
|
65
101
|
Arrest::debug sub + (hash_to_query filters)
|
66
102
|
# filters are ignored by mem impl so far
|
103
|
+
|
104
|
+
id_list = parse_for_has_many_relations(sub)
|
105
|
+
if id_list.empty?
|
106
|
+
id_list = @@collections[sub] || []
|
107
|
+
end
|
67
108
|
|
68
|
-
id_list = @@collections[sub] || []
|
69
109
|
objects = id_list.map do |id|
|
70
110
|
@@all_objects[id]
|
71
111
|
end
|
72
112
|
|
73
113
|
wrap collection_json(objects), id_list.length
|
74
|
-
|
75
114
|
end
|
76
115
|
|
77
116
|
def get_one sub, filters = {}
|
@@ -95,6 +134,7 @@ module Arrest
|
|
95
134
|
v.reject!{ |id| id == base_id }
|
96
135
|
end
|
97
136
|
@@all_objects[base_id].delete
|
137
|
+
remove_edges(@@edge_matrix, base_id)
|
98
138
|
end
|
99
139
|
end
|
100
140
|
|
@@ -118,26 +158,104 @@ module Arrest
|
|
118
158
|
end
|
119
159
|
|
120
160
|
|
121
|
-
def delete
|
161
|
+
def delete(rest_resource)
|
122
162
|
raise "To change an object it must have an id" unless rest_resource.respond_to?(:id) && rest_resource.id != nil
|
123
163
|
@@all_objects.delete(rest_resource.id)
|
124
164
|
@@collections.each_pair do |k,v|
|
125
165
|
v.reject!{ |id| id == rest_resource.id }
|
126
166
|
end
|
167
|
+
remove_edges(@@edge_matrix, rest_resource.id)
|
127
168
|
rest_resource
|
128
169
|
end
|
129
170
|
|
130
|
-
def
|
171
|
+
def remove_outgoing_edges(edge_matrix, id)
|
172
|
+
if (edge_matrix[id])
|
173
|
+
out_edges = edge_matrix[id].find_all{|edge| edge.tail}
|
174
|
+
in_edges_to_delete = out_edges.map do |out_edge|
|
175
|
+
foreign_edges = edge_matrix[out_edge.id] # the edge set of the foreign node that this node points to
|
176
|
+
has_many_back_edges = foreign_edges.find_all do |for_edge|
|
177
|
+
for_edge.id == id && for_edge.foreign_key == out_edge.foreign_key
|
178
|
+
end
|
179
|
+
[has_many_back_edges.first, out_edge.id] # first element may be nil
|
180
|
+
end
|
181
|
+
|
182
|
+
in_edges_to_delete.each do |tupel|
|
183
|
+
if tupel[0]
|
184
|
+
edge_matrix[tupel[1]].delete_if{|e| e.id == tupel[0].id && e.foreign_key == tupel[0].foreign_key}
|
185
|
+
end
|
186
|
+
end
|
187
|
+
edge_matrix[id] = Set.new()
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def remove_edges(edge_matrix, node_id)
|
192
|
+
if (edge_matrix[node_id])
|
193
|
+
edge_matrix[node_id].each do |edge|
|
194
|
+
to_nodes = edge_matrix[edge.id]
|
195
|
+
to_nodes.delete_if{|e| e.id == node_id}
|
196
|
+
end
|
197
|
+
edge_matrix.delete(node_id)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def identify_and_store_edges(edge_matrix, rest_resource)
|
202
|
+
from_id = rest_resource.id
|
203
|
+
|
204
|
+
rest_resource.class.all_fields.each do |attr|
|
205
|
+
if attr.is_a?(Arrest::HasManyAttribute)
|
206
|
+
to_ids = rest_resource.send(attr.name) # -> foo_ids
|
207
|
+
url_part = attr.url_part
|
208
|
+
foreign_key = attr.foreign_key
|
209
|
+
edge_matrix[from_id] ||= Set.new()
|
210
|
+
if to_ids
|
211
|
+
to_ids.each do |to_id|
|
212
|
+
edge_matrix[from_id].add(Edge.new(foreign_key, url_part, to_id, true))
|
213
|
+
edge_matrix[to_id] ||= Set.new()
|
214
|
+
edge_matrix[to_id].add(Edge.new(foreign_key, url_part, from_id, false))
|
215
|
+
end
|
216
|
+
end
|
217
|
+
elsif attr.is_a?(Arrest::BelongsToAttribute)
|
218
|
+
to_id = rest_resource.send(attr.name)
|
219
|
+
if to_id
|
220
|
+
foreign_key = attr.foreign_key
|
221
|
+
has_many_clazz = attr.target_class()
|
222
|
+
puts "#{foreign_key}"
|
223
|
+
hm_candidates = has_many_clazz.all_fields.find_all do |field|
|
224
|
+
puts "->#{field.foreign_key.to_s}" if field.is_a?(Arrest::HasManyAttribute)
|
225
|
+
field.is_a?(Arrest::HasManyAttribute) && field.foreign_key.to_s == foreign_key
|
226
|
+
end
|
227
|
+
return if hm_candidates.empty?
|
228
|
+
has_many_node = hm_candidates.first
|
229
|
+
url_part = has_many_node.url_part
|
230
|
+
|
231
|
+
edge_matrix[from_id] ||= Set.new()
|
232
|
+
edge_matrix[from_id].add(Edge.new(foreign_key, url_part, to_id, true))
|
233
|
+
edge_matrix[to_id] ||= Set.new()
|
234
|
+
edge_matrix[to_id].add(Edge.new(foreign_key, url_part, from_id, false))
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
def put(rest_resource)
|
131
242
|
raise "To change an object it must have an id" unless rest_resource.respond_to?(:id) && rest_resource.id != nil
|
132
243
|
old = @@all_objects[rest_resource.id]
|
133
|
-
|
244
|
+
|
245
|
+
remove_outgoing_edges(@@edge_matrix, old.id)
|
246
|
+
|
134
247
|
rest_resource.class.all_fields.each do |f|
|
135
248
|
old.send("#{f.name}=", rest_resource.send(f.name))
|
136
249
|
end
|
250
|
+
|
251
|
+
identify_and_store_edges(@@edge_matrix, rest_resource)
|
252
|
+
|
137
253
|
true
|
138
254
|
end
|
139
255
|
|
140
|
-
|
256
|
+
|
257
|
+
def post(rest_resource)
|
258
|
+
|
141
259
|
Arrest::debug "post -> #{rest_resource.class.name} #{rest_resource.to_hash} #{rest_resource.class.all_fields.map(&:name)}"
|
142
260
|
raise "new object must have setter for id" unless rest_resource.respond_to?(:id=)
|
143
261
|
raise "new object must not have id" if rest_resource.respond_to?(:id) && rest_resource.id != nil
|
@@ -152,10 +270,13 @@ module Arrest
|
|
152
270
|
@@collections[rest_resource.resource_path] = []
|
153
271
|
end
|
154
272
|
@@collections[rest_resource.resource_path] << rest_resource.id
|
273
|
+
|
274
|
+
identify_and_store_edges(@@edge_matrix, rest_resource)
|
275
|
+
|
155
276
|
true
|
156
277
|
end
|
157
278
|
|
158
|
-
def cheat_collection
|
279
|
+
def cheat_collection(url, ids)
|
159
280
|
@@collections[url] = ids
|
160
281
|
end
|
161
282
|
|
data/lib/arrest/root_resource.rb
CHANGED
@@ -9,7 +9,7 @@ module Arrest
|
|
9
9
|
|
10
10
|
def by_url url
|
11
11
|
begin
|
12
|
-
body = body_root(source().get_many
|
12
|
+
body = body_root(source().get_many(url))
|
13
13
|
rescue Arrest::Errors::DocumentNotFoundError
|
14
14
|
Arrest::logger.info "DocumentNotFoundError for #{url} gracefully returning []"
|
15
15
|
return []
|
data/lib/arrest/string_utils.rb
CHANGED
@@ -1,64 +1,66 @@
|
|
1
|
-
|
2
|
-
class
|
3
|
-
|
4
|
-
|
5
|
-
['(
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
['(
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
if upperNext || (result == "" && upper_first)
|
54
|
-
result << c.upcase
|
1
|
+
module Arrest
|
2
|
+
class StringUtils
|
3
|
+
class << self
|
4
|
+
|
5
|
+
PLURALS = [['(quiz)$', '\1zes'],['(ox)$', '\1en'],['([m|l])ouse$', '\1ice'],['(matr|vert|ind)ix|ex$', '\1ices'],
|
6
|
+
['(x|ch|ss|sh)$', '\1es'],['([^aeiouy]|qu)ies$', '\1y'],['([^aeiouy]|q)y$$', '\1ies'],['(hive)$', '\1s'],
|
7
|
+
['(?:[^f]fe|([lr])f)$', '\1\2ves'],['(sis)$', 'ses'],['([ti])um$', '\1a'],['(buffal|tomat)o$', '\1oes'],['(bu)s$', '\1es'],
|
8
|
+
['(alias|status)$', '\1es'],['(octop|vir)us$', '\1i'],['(ax|test)is$', '\1es'],['s$', 's'],['$', 's']]
|
9
|
+
SINGULARS =[['(quiz)zes$', '\1'],['(matr)ices$', '\1ix'],['(vert|ind)ices$', '\1ex'],['^(ox)en$', '\1'],['(alias|status)es$', '\1'],
|
10
|
+
['(octop|vir)i$', '\1us'],['(cris|ax|test)es$', '\1is'],['(shoe)s$', '\1'],['[o]es$', '\1'],['[bus]es$', '\1'],['([m|l])ice$', '\1ouse'],
|
11
|
+
['(x|ch|ss|sh)es$', '\1'],['(m)ovies$', '\1ovie'],['[s]eries$', '\1eries'],['([^aeiouy]|qu)ies$', '\1y'],['[lr]ves$', '\1f'],
|
12
|
+
['(tive)s$', '\1'],['(hive)s$', '\1'],['([^f])ves$', '\1fe'],['(^analy)ses$', '\1sis'],
|
13
|
+
['([a]naly|[b]a|[d]iagno|[p]arenthe|[p]rogno|[s]ynop|[t]he)ses$', '\1\2sis'],['([ti])a$', '\1um'],['(news)$', '\1ews'], ['(.*)s$', '\1'], ['^(.*)$', '\1']]
|
14
|
+
|
15
|
+
def singular(str)
|
16
|
+
SINGULARS.each { |match_exp, replacement_exp| return str.gsub(Regexp.compile(match_exp), replacement_exp) unless str.match(Regexp.compile(match_exp)).nil?}
|
17
|
+
end
|
18
|
+
|
19
|
+
def plural(str)
|
20
|
+
PLURALS.each { |match_exp, replacement_exp| return str.gsub(Regexp.compile(match_exp), replacement_exp) unless str.match(Regexp.compile(match_exp)).nil? }
|
21
|
+
end
|
22
|
+
|
23
|
+
def plural?
|
24
|
+
PLURALS.each {|match_exp, replacement_exp| return true if str.match(Regexp.compile(match_exp))}
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def blank? str
|
29
|
+
str == nil || str == ""
|
30
|
+
end
|
31
|
+
|
32
|
+
def is_upper? str
|
33
|
+
str == str.upcase
|
34
|
+
end
|
35
|
+
|
36
|
+
def underscore str
|
37
|
+
word = str.to_s.dup
|
38
|
+
word.gsub!(/::/, '/')
|
39
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
40
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
41
|
+
word.tr!("-", "_")
|
42
|
+
word.downcase!
|
43
|
+
word
|
44
|
+
end
|
45
|
+
|
46
|
+
def classify(str, upper_first = true)
|
47
|
+
result = ""
|
48
|
+
upperNext = false
|
49
|
+
#(singular str) .each_char do |c|
|
50
|
+
(str) .each_char do |c|
|
51
|
+
if c == "_"
|
52
|
+
upperNext = true
|
55
53
|
else
|
56
|
-
result
|
54
|
+
if upperNext || (result == "" && upper_first)
|
55
|
+
result << c.upcase
|
56
|
+
else
|
57
|
+
result << c
|
58
|
+
end
|
59
|
+
upperNext = false
|
57
60
|
end
|
58
|
-
upperNext = false
|
59
61
|
end
|
62
|
+
result
|
60
63
|
end
|
61
|
-
result
|
62
64
|
end
|
63
65
|
end
|
64
|
-
end
|
66
|
+
end
|
data/lib/arrest/version.rb
CHANGED
data/test/models.rb
CHANGED
@@ -120,3 +120,13 @@ end
|
|
120
120
|
|
121
121
|
class DeleteMeAll < Arrest::RootResource
|
122
122
|
end
|
123
|
+
|
124
|
+
class Foo < Arrest::RootResource
|
125
|
+
has_many :bars#, :class_name => :Bar, :foreign_key => defaults to bar_id
|
126
|
+
has_many :other_bars, :class_name => :Bar, :foreign_key => :common_key
|
127
|
+
end
|
128
|
+
class Bar < Arrest::RootResource
|
129
|
+
has_many :foos
|
130
|
+
belongs_to :foo# foreign key defaults to class name, {:foreign_key => bar_id}
|
131
|
+
belongs_to :other_foo, {:class_name => Foo, :foreign_key => :common_key}#, :foreign_key => :other_foo_key
|
132
|
+
end
|
data/test/unit.rb
CHANGED
@@ -451,5 +451,103 @@ class FirstTest < Test::Unit::TestCase
|
|
451
451
|
all = DeleteMeAll.all
|
452
452
|
assert_equal [], all
|
453
453
|
end
|
454
|
+
|
455
|
+
def test_update_belongs_to
|
456
|
+
f1 = Foo.new()
|
457
|
+
f1.save
|
458
|
+
assert_equal 0, Arrest::Source.source.edge_count
|
459
|
+
b1 = Bar.new({:foo_id => f1.id})
|
460
|
+
b1.save
|
461
|
+
assert_equal 2, Arrest::Source.source.edge_count
|
462
|
+
assert_equal 2, Arrest::Source.source.node_count
|
463
|
+
|
464
|
+
f2 = Foo.new()
|
465
|
+
f2.save
|
466
|
+
assert_equal 2, Arrest::Source.source.edge_count
|
467
|
+
b1.foo_id = f2.id
|
468
|
+
b1.save
|
469
|
+
#Arrest::Source.source.edge_matrix.each_pair{|k,v| y k; y v}
|
470
|
+
assert_equal 2, Arrest::Source.source.edge_count
|
471
|
+
assert_equal 3, Arrest::Source.source.node_count
|
472
|
+
end
|
473
|
+
|
474
|
+
def test_has_many_matrix_in_mem_source
|
475
|
+
f1 = Foo.new()
|
476
|
+
f1.save
|
477
|
+
f2 = Foo.new()
|
478
|
+
f2.save
|
479
|
+
f3 = Foo.new()
|
480
|
+
f3.save
|
481
|
+
|
482
|
+
b1 = Bar.new({:foo_ids => [f1.id, f2.id], :foo_id => f3.id})
|
483
|
+
b1.save
|
484
|
+
|
485
|
+
assert_equal 2, b1.foos.length
|
486
|
+
|
487
|
+
b2 = Bar.new({:foo_ids => [f2.id, f3.id], :foo_id =>f1.id})
|
488
|
+
b2.save
|
489
|
+
|
490
|
+
f1.delete
|
491
|
+
|
492
|
+
b1_rel = Bar.find(b1.id)
|
493
|
+
assert_equal 1, b1_rel.foos.length
|
494
|
+
assert_equal f2.id, b1_rel.foos.first.id
|
495
|
+
|
496
|
+
|
497
|
+
f2.bar_ids=[b1.id]
|
498
|
+
f2.other_bar_ids=[b2.id]
|
499
|
+
f2.save
|
500
|
+
f2_rel = Foo.find(f2.id)
|
501
|
+
assert_equal 1, f2_rel.bars.length
|
502
|
+
assert_equal 1, f2_rel.other_bars.length
|
503
|
+
|
504
|
+
b2.delete
|
505
|
+
|
506
|
+
f2_rel = Foo.find(f2.id)
|
507
|
+
assert_equal 1, f2_rel.bars.length
|
508
|
+
assert_equal 0, f2_rel.other_bars.length
|
509
|
+
assert_equal b1.id, f2_rel.bars.first.id
|
510
|
+
|
511
|
+
end
|
512
|
+
|
513
|
+
def test_has_many_with_belongs_to
|
514
|
+
f1 = Foo.new()
|
515
|
+
f1.save
|
516
|
+
f2 = Foo.new()
|
517
|
+
f2.save
|
518
|
+
f3 = Foo.new()
|
519
|
+
f3.save
|
520
|
+
|
521
|
+
b1 = Bar.new({:other_foo_id => f1.id, :foo_id => f3.id})
|
522
|
+
b1.save
|
523
|
+
b2 = Bar.new({:other_foo_id => f2.id, :foo_id => f1.id})
|
524
|
+
b2.save
|
525
|
+
|
526
|
+
f1_rel = Foo.find(f1.id)
|
527
|
+
f2_rel = Foo.find(f2.id)
|
528
|
+
f3_rel = Foo.find(f3.id)
|
529
|
+
|
530
|
+
assert_equal 1, f1_rel.bars.length
|
531
|
+
assert_equal b1.id, f1_rel.other_bars.first.id
|
532
|
+
assert_equal b1.id, f3_rel.bars.first.id
|
533
|
+
|
534
|
+
assert_equal b2.id, f1_rel.bars.first.id
|
535
|
+
assert_equal b2.id, f2_rel.other_bars.first.id
|
536
|
+
|
537
|
+
#test update
|
538
|
+
b1.foo_id = f2.id
|
539
|
+
b1.save
|
540
|
+
|
541
|
+
|
542
|
+
|
543
|
+
f3_rel = Foo.find(f3.id)
|
544
|
+
assert f3_rel.bars.empty?
|
545
|
+
f2_rel = Foo.find(f2.id)
|
546
|
+
assert_equal b1.id, f2_rel.bars.first.id
|
547
|
+
|
548
|
+
b1.delete
|
549
|
+
f1_rel = Foo.find(f1.id)
|
550
|
+
assert f1_rel.other_bars.empty?
|
551
|
+
end
|
454
552
|
end
|
455
553
|
|
metadata
CHANGED
@@ -1,133 +1,176 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: arrest
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 61
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 17
|
10
|
+
version: 0.0.17
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Axel Tetzlaff
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
|
18
|
+
date: 2012-01-24 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
15
21
|
name: json
|
16
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
24
|
none: false
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
22
32
|
type: :runtime
|
23
|
-
|
24
|
-
|
25
|
-
- !ruby/object:Gem::Dependency
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
26
35
|
name: faraday
|
27
|
-
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
28
38
|
none: false
|
29
|
-
requirements:
|
30
|
-
- - =
|
31
|
-
- !ruby/object:Gem::Version
|
39
|
+
requirements:
|
40
|
+
- - "="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 9
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
- 7
|
46
|
+
- 5
|
32
47
|
version: 0.7.5
|
33
48
|
type: :runtime
|
34
|
-
|
35
|
-
|
36
|
-
- !ruby/object:Gem::Dependency
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
37
51
|
name: activemodel
|
38
|
-
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
39
54
|
none: false
|
40
|
-
requirements:
|
55
|
+
requirements:
|
41
56
|
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 5
|
59
|
+
segments:
|
60
|
+
- 3
|
61
|
+
version: "3"
|
44
62
|
type: :runtime
|
45
|
-
|
46
|
-
|
47
|
-
- !ruby/object:Gem::Dependency
|
63
|
+
version_requirements: *id003
|
64
|
+
- !ruby/object:Gem::Dependency
|
48
65
|
name: bundler
|
49
|
-
|
66
|
+
prerelease: false
|
67
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
50
68
|
none: false
|
51
|
-
requirements:
|
52
|
-
- -
|
53
|
-
- !ruby/object:Gem::Version
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 23
|
73
|
+
segments:
|
74
|
+
- 1
|
75
|
+
- 0
|
76
|
+
- 0
|
54
77
|
version: 1.0.0
|
55
78
|
type: :development
|
56
|
-
|
57
|
-
|
58
|
-
- !ruby/object:Gem::Dependency
|
79
|
+
version_requirements: *id004
|
80
|
+
- !ruby/object:Gem::Dependency
|
59
81
|
name: rake
|
60
|
-
|
82
|
+
prerelease: false
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
61
84
|
none: false
|
62
|
-
requirements:
|
63
|
-
- -
|
64
|
-
- !ruby/object:Gem::Version
|
65
|
-
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
66
92
|
type: :development
|
67
|
-
|
68
|
-
|
69
|
-
- !ruby/object:Gem::Dependency
|
93
|
+
version_requirements: *id005
|
94
|
+
- !ruby/object:Gem::Dependency
|
70
95
|
name: rdoc
|
71
|
-
|
96
|
+
prerelease: false
|
97
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
72
98
|
none: false
|
73
|
-
requirements:
|
74
|
-
- -
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
77
106
|
type: :development
|
78
|
-
|
79
|
-
|
80
|
-
- !ruby/object:Gem::Dependency
|
107
|
+
version_requirements: *id006
|
108
|
+
- !ruby/object:Gem::Dependency
|
81
109
|
name: rspec
|
82
|
-
|
110
|
+
prerelease: false
|
111
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
83
112
|
none: false
|
84
|
-
requirements:
|
113
|
+
requirements:
|
85
114
|
- - ~>
|
86
|
-
- !ruby/object:Gem::Version
|
87
|
-
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
hash: 7
|
117
|
+
segments:
|
118
|
+
- 2
|
119
|
+
version: "2"
|
88
120
|
type: :development
|
89
|
-
|
90
|
-
|
91
|
-
- !ruby/object:Gem::Dependency
|
121
|
+
version_requirements: *id007
|
122
|
+
- !ruby/object:Gem::Dependency
|
92
123
|
name: rr
|
93
|
-
|
124
|
+
prerelease: false
|
125
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
94
126
|
none: false
|
95
|
-
requirements:
|
96
|
-
- -
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
hash: 3
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
version: "0"
|
99
134
|
type: :development
|
100
|
-
|
101
|
-
|
102
|
-
- !ruby/object:Gem::Dependency
|
135
|
+
version_requirements: *id008
|
136
|
+
- !ruby/object:Gem::Dependency
|
103
137
|
name: simplecov
|
104
|
-
|
138
|
+
prerelease: false
|
139
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
105
140
|
none: false
|
106
|
-
requirements:
|
107
|
-
- -
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
hash: 3
|
145
|
+
segments:
|
146
|
+
- 0
|
147
|
+
version: "0"
|
110
148
|
type: :development
|
111
|
-
|
112
|
-
|
113
|
-
- !ruby/object:Gem::Dependency
|
149
|
+
version_requirements: *id009
|
150
|
+
- !ruby/object:Gem::Dependency
|
114
151
|
name: rack
|
115
|
-
|
152
|
+
prerelease: false
|
153
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
116
154
|
none: false
|
117
|
-
requirements:
|
118
|
-
- -
|
119
|
-
- !ruby/object:Gem::Version
|
120
|
-
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
hash: 3
|
159
|
+
segments:
|
160
|
+
- 0
|
161
|
+
version: "0"
|
121
162
|
type: :development
|
122
|
-
|
123
|
-
version_requirements: *18581440
|
163
|
+
version_requirements: *id010
|
124
164
|
description: Consume a rest API in a AR like fashion
|
125
|
-
email:
|
165
|
+
email:
|
126
166
|
- axel.tetzlaff@fortytools.com
|
127
167
|
executables: []
|
168
|
+
|
128
169
|
extensions: []
|
170
|
+
|
129
171
|
extra_rdoc_files: []
|
130
|
-
|
172
|
+
|
173
|
+
files:
|
131
174
|
- .gitignore
|
132
175
|
- .rspec
|
133
176
|
- Gemfile
|
@@ -138,10 +181,13 @@ files:
|
|
138
181
|
- lib/arrest/abstract_resource.rb
|
139
182
|
- lib/arrest/attributes/attribute.rb
|
140
183
|
- lib/arrest/attributes/belongs_to.rb
|
184
|
+
- lib/arrest/attributes/belongs_to_attribute.rb
|
141
185
|
- lib/arrest/attributes/converter.rb
|
142
186
|
- lib/arrest/attributes/has_attributes.rb
|
187
|
+
- lib/arrest/attributes/has_many_attribute.rb
|
143
188
|
- lib/arrest/attributes/nested_attribute.rb
|
144
189
|
- lib/arrest/attributes/polymorphic_attribute.rb
|
190
|
+
- lib/arrest/class_utils.rb
|
145
191
|
- lib/arrest/exceptions.rb
|
146
192
|
- lib/arrest/handler.rb
|
147
193
|
- lib/arrest/helper/child_collection.rb
|
@@ -164,28 +210,45 @@ files:
|
|
164
210
|
- test/nested_resource.rb
|
165
211
|
- test/unit.rb
|
166
212
|
- test/validations.rb
|
167
|
-
homepage:
|
213
|
+
homepage: ""
|
168
214
|
licenses: []
|
215
|
+
|
169
216
|
post_install_message:
|
170
217
|
rdoc_options: []
|
171
|
-
|
218
|
+
|
219
|
+
require_paths:
|
172
220
|
- lib
|
173
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
221
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
174
222
|
none: false
|
175
|
-
requirements:
|
176
|
-
- -
|
177
|
-
- !ruby/object:Gem::Version
|
178
|
-
|
179
|
-
|
223
|
+
requirements:
|
224
|
+
- - ">="
|
225
|
+
- !ruby/object:Gem::Version
|
226
|
+
hash: 3
|
227
|
+
segments:
|
228
|
+
- 0
|
229
|
+
version: "0"
|
230
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
180
231
|
none: false
|
181
|
-
requirements:
|
182
|
-
- -
|
183
|
-
- !ruby/object:Gem::Version
|
184
|
-
|
232
|
+
requirements:
|
233
|
+
- - ">="
|
234
|
+
- !ruby/object:Gem::Version
|
235
|
+
hash: 3
|
236
|
+
segments:
|
237
|
+
- 0
|
238
|
+
version: "0"
|
185
239
|
requirements: []
|
240
|
+
|
186
241
|
rubyforge_project: arrest
|
187
242
|
rubygems_version: 1.8.10
|
188
243
|
signing_key:
|
189
244
|
specification_version: 3
|
190
245
|
summary: Another ruby rest client
|
191
|
-
test_files:
|
246
|
+
test_files:
|
247
|
+
- spec/arrest_spec.rb
|
248
|
+
- spec/spec_helper.rb
|
249
|
+
- spec/support/models/user.rb
|
250
|
+
- test/has_attributed.rb
|
251
|
+
- test/models.rb
|
252
|
+
- test/nested_resource.rb
|
253
|
+
- test/unit.rb
|
254
|
+
- test/validations.rb
|