arrest 0.0.1 → 0.0.2
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/arrest.gemspec +1 -1
- data/lib/arrest.rb +46 -5
- data/lib/arrest/abstract_resource.rb +84 -13
- data/lib/arrest/http_source.rb +4 -4
- data/lib/arrest/mem_source.rb +8 -8
- data/lib/arrest/rest_child.rb +7 -1
- data/lib/arrest/root_resource.rb +3 -1
- data/lib/arrest/source.rb +30 -0
- data/lib/arrest/version.rb +1 -1
- data/test/unit.rb +127 -0
- metadata +16 -3
data/arrest.gemspec
CHANGED
data/lib/arrest.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "arrest/version"
|
2
2
|
|
3
|
+
require "arrest/source"
|
3
4
|
require "arrest/http_source"
|
4
5
|
require "arrest/mem_source"
|
5
6
|
require "arrest/abstract_resource"
|
@@ -8,11 +9,51 @@ require "arrest/rest_child"
|
|
8
9
|
|
9
10
|
module Arrest
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
cattr_accessor :source
|
12
|
+
String.class_eval do
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
PLURALS = [['(quiz)$', '\1zes'],['(ox)$', '\1en'],['([m|l])ouse$', '\1ice'],['(matr|vert|ind)ix|ex$', '\1ices'],
|
15
|
+
['(x|ch|ss|sh)$', '\1es'],['([^aeiouy]|qu)ies$', '\1y'],['([^aeiouy]|q)y$$', '\1ies'],['(hive)$', '\1s'],
|
16
|
+
['(?:[^f]fe|([lr])f)$', '\1\2ves'],['(sis)$', 'ses'],['([ti])um$', '\1a'],['(buffal|tomat)o$', '\1oes'],['(bu)s$', '\1es'],
|
17
|
+
['(alias|status)$', '\1es'],['(octop|vir)us$', '\1i'],['(ax|test)is$', '\1es'],['s$', 's'],['$', 's']]
|
18
|
+
SINGULARS =[['(quiz)zes$', '\1'],['(matr)ices$', '\1ix'],['(vert|ind)ices$', '\1ex'],['^(ox)en$', '\1'],['(alias|status)es$', '\1'],
|
19
|
+
['(octop|vir)i$', '\1us'],['(cris|ax|test)es$', '\1is'],['(shoe)s$', '\1'],['[o]es$', '\1'],['[bus]es$', '\1'],['([m|l])ice$', '\1ouse'],
|
20
|
+
['(x|ch|ss|sh)es$', '\1'],['(m)ovies$', '\1ovie'],['[s]eries$', '\1eries'],['([^aeiouy]|qu)ies$', '\1y'],['[lr]ves$', '\1f'],
|
21
|
+
['(tive)s$', '\1'],['(hive)s$', '\1'],['([^f])ves$', '\1fe'],['(^analy)ses$', '\1sis'],
|
22
|
+
['([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']]
|
23
|
+
|
24
|
+
def singular()
|
25
|
+
SINGULARS.each { |match_exp, replacement_exp| return gsub(Regexp.compile(match_exp), replacement_exp) unless match(Regexp.compile(match_exp)).nil?}
|
26
|
+
end
|
27
|
+
|
28
|
+
def plural()
|
29
|
+
PLURALS.each { |match_exp, replacement_exp| return gsub(Regexp.compile(match_exp), replacement_exp) unless match(Regexp.compile(match_exp)).nil? }
|
30
|
+
end
|
17
31
|
|
32
|
+
def plural?
|
33
|
+
PLURALS.each {|match_exp, replacement_exp| return true if match(Regexp.compile(match_exp))}
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
def blank?
|
38
|
+
self == nil || self == ""
|
39
|
+
end
|
40
|
+
|
41
|
+
def classify(upper_first = true)
|
42
|
+
result = ""
|
43
|
+
upperNext = false
|
44
|
+
self.singular.each_char do |c|
|
45
|
+
if c == "_"
|
46
|
+
upperNext = true
|
47
|
+
else
|
48
|
+
if upperNext || (result.blank? && upper_first)
|
49
|
+
result << c.upcase
|
50
|
+
else
|
51
|
+
result << c
|
52
|
+
end
|
53
|
+
upperNext = false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
result
|
57
|
+
end
|
58
|
+
end
|
18
59
|
end
|
@@ -1,42 +1,113 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module Arrest
|
2
4
|
class AbstractResource
|
3
|
-
extend ActiveModel::Naming
|
4
|
-
|
5
|
-
attr_accessor :keys
|
6
|
-
|
7
5
|
class << self
|
8
6
|
|
7
|
+
attr_accessor :fields
|
8
|
+
|
9
9
|
def source
|
10
10
|
Arrest::Source::source
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
13
|
def body_root response
|
15
14
|
all = JSON.parse response
|
16
15
|
all["result"]
|
17
16
|
end
|
18
17
|
|
19
18
|
def build hash
|
20
|
-
|
19
|
+
self.new hash
|
21
20
|
end
|
22
21
|
|
23
22
|
def resource_name
|
24
|
-
self.name.sub(/.*:/,'').downcase.
|
23
|
+
self.name.sub(/.*:/,'').downcase.plural
|
24
|
+
end
|
25
|
+
|
26
|
+
def has_many(*args)
|
27
|
+
method_name, options = args
|
28
|
+
method_name = method_name.to_sym
|
29
|
+
|
30
|
+
clazz_name = method_name.to_s
|
31
|
+
if options
|
32
|
+
clazz = options[:class_name]
|
33
|
+
if clazz
|
34
|
+
clazz_name = clazz.to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
send :define_method, method_name do
|
38
|
+
Arrest::Source.mod.const_get(clazz_name.classify).all_for self
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parent(*args)
|
43
|
+
method_name = args[0].to_s.to_sym
|
44
|
+
send :define_method, method_name do
|
45
|
+
self.parent
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_attribute attribute_name
|
50
|
+
if @fields == nil
|
51
|
+
@fields = []
|
52
|
+
end
|
53
|
+
@fields << attribute_name
|
54
|
+
end
|
55
|
+
|
56
|
+
def attributes(*args)
|
57
|
+
args.each do |arg|
|
58
|
+
self.send :attr_accessor,arg
|
59
|
+
add_attribute arg
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def belongs_to(*args)
|
64
|
+
arg = args[0]
|
65
|
+
name = arg.to_s.downcase
|
66
|
+
attributes "#{name}_id".to_sym
|
67
|
+
send :define_method, name do
|
68
|
+
val = self.instance_variable_get("@#{name}_id")
|
69
|
+
Arrest::Source.mod.const_get(name.classify).find(val)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_accessor :id
|
75
|
+
|
76
|
+
def initialize as_i
|
77
|
+
as = {}
|
78
|
+
as_i.each_pair do |k,v|
|
79
|
+
as[k.to_sym] = v
|
80
|
+
end
|
81
|
+
unless self.class.fields == nil
|
82
|
+
self.class.fields.each do |field|
|
83
|
+
json_name = field.to_s.classify(false)
|
84
|
+
json_name[0] = json_name[0].downcase
|
85
|
+
self.instance_variable_set("@#{field.to_s}", as[json_name.to_sym])
|
86
|
+
end
|
25
87
|
end
|
88
|
+
self.id = as[:id]
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_hash
|
92
|
+
result = {}
|
93
|
+
unless self.class.fields == nil
|
94
|
+
self.class.fields.each do |field|
|
95
|
+
json_name = field.to_s.classify(false)
|
96
|
+
result[json_name] = self.instance_variable_get("@#{field.to_s}")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
result[:id] = self.id
|
100
|
+
result
|
101
|
+
|
26
102
|
end
|
27
103
|
|
28
104
|
def save
|
29
|
-
if self.respond_to?(:id) && self.id
|
105
|
+
if self.respond_to?(:id) && self.id != nil
|
30
106
|
AbstractResource::source().put self
|
31
107
|
else
|
32
108
|
AbstractResource::source().post self
|
33
109
|
end
|
34
110
|
end
|
35
111
|
|
36
|
-
|
37
|
-
def to_hash
|
38
|
-
raise "override symmetrically to build, to create a hash representation from self"
|
39
|
-
end
|
40
|
-
|
41
112
|
end
|
42
113
|
end
|
data/lib/arrest/http_source.rb
CHANGED
@@ -20,13 +20,13 @@ module Arrest
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def put rest_resource
|
23
|
-
raise "To change an object it must have an id" unless rest_resource.respond_to?(:id) && rest_resource.id
|
23
|
+
raise "To change an object it must have an id" unless rest_resource.respond_to?(:id) && rest_resource.id != nil
|
24
24
|
hash = rest_resource.to_hash
|
25
25
|
hash.delete(:id)
|
26
26
|
hash.delete("id")
|
27
27
|
|
28
28
|
response = self.connection().put do |req|
|
29
|
-
req.url
|
29
|
+
req.url rest_resource.location
|
30
30
|
add_headers req.headers
|
31
31
|
req.body = hash.to_json
|
32
32
|
end
|
@@ -35,11 +35,11 @@ module Arrest
|
|
35
35
|
|
36
36
|
def post rest_resource
|
37
37
|
raise "new object must have setter for id" unless rest_resource.respond_to?(:id=)
|
38
|
-
raise "new object must not have id" if rest_resource.respond_to?(:id) && rest_resource.id
|
38
|
+
raise "new object must not have id" if rest_resource.respond_to?(:id) && rest_resource.id != nil
|
39
39
|
hash = rest_resource.to_hash
|
40
40
|
|
41
41
|
response = self.connection().post do |req|
|
42
|
-
req.url rest_resource.class.
|
42
|
+
req.url rest_resource.class.resource_path
|
43
43
|
add_headers req.headers
|
44
44
|
req.body = hash.to_json
|
45
45
|
end
|
data/lib/arrest/mem_source.rb
CHANGED
@@ -16,13 +16,13 @@ module Arrest
|
|
16
16
|
"{
|
17
17
|
\"queryTime\" : \"0.01866644\",
|
18
18
|
\"resultCount\" : #{count},
|
19
|
-
\"
|
19
|
+
\"result\" : #{content} }"
|
20
20
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def get sub
|
24
24
|
idx = sub.rindex(/\/[0-9]*$/)
|
25
|
-
if idx
|
25
|
+
if idx
|
26
26
|
ps = [sub[0..(idx-1)], sub[(idx+1)..sub.length]]
|
27
27
|
else
|
28
28
|
ps = [sub]
|
@@ -30,7 +30,7 @@ module Arrest
|
|
30
30
|
val = traverse @@data,ps
|
31
31
|
if val.is_a?(Hash)
|
32
32
|
wrap collection_json(val.values), val.length
|
33
|
-
elsif val
|
33
|
+
elsif val == nil
|
34
34
|
wrap "{}", 0
|
35
35
|
else
|
36
36
|
wrap val.to_hash.to_json, 1
|
@@ -49,7 +49,7 @@ module Arrest
|
|
49
49
|
return hash
|
50
50
|
end
|
51
51
|
key = keys.first
|
52
|
-
if hash
|
52
|
+
if hash == nil
|
53
53
|
nil
|
54
54
|
else
|
55
55
|
traverse hash[key.to_s],keys.drop(1)
|
@@ -59,14 +59,14 @@ module Arrest
|
|
59
59
|
|
60
60
|
def put rest_resource
|
61
61
|
@@data[rest_resource.resource_path()][rest_resource.id.to_s] = rest_resource
|
62
|
-
raise "To change an object it must have an id" unless rest_resource.respond_to?(:id) && rest_resource.id
|
62
|
+
raise "To change an object it must have an id" unless rest_resource.respond_to?(:id) && rest_resource.id != nil
|
63
63
|
rest_resource
|
64
64
|
end
|
65
65
|
|
66
66
|
def post rest_resource
|
67
67
|
raise "new object must have setter for id" unless rest_resource.respond_to?(:id=)
|
68
|
-
raise "new object must not have id" if rest_resource.respond_to?(:id) && rest_resource.id
|
69
|
-
if @@data[rest_resource.resource_path()]
|
68
|
+
raise "new object must not have id" if rest_resource.respond_to?(:id) && rest_resource.id != nil
|
69
|
+
if @@data[rest_resource.resource_path()] != nil
|
70
70
|
last_id = @@data[rest_resource.resource_path()].values.map(&:id).sort.last
|
71
71
|
else
|
72
72
|
last_id = 42
|
@@ -77,7 +77,7 @@ module Arrest
|
|
77
77
|
next_id = "#{last_id}x"
|
78
78
|
end
|
79
79
|
rest_resource.id = next_id
|
80
|
-
unless @@data[rest_resource.resource_path()]
|
80
|
+
unless @@data[rest_resource.resource_path()] != nil
|
81
81
|
@@data[rest_resource.resource_path()] = {}
|
82
82
|
end
|
83
83
|
@@data[rest_resource.resource_path()][next_id.to_s] = rest_resource
|
data/lib/arrest/rest_child.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Arrest
|
2
2
|
class RestChild < AbstractResource
|
3
3
|
attr_accessor :parent
|
4
|
-
def initialize parent
|
4
|
+
def initialize parent, h
|
5
|
+
super h
|
5
6
|
@parent = parent
|
6
7
|
end
|
7
8
|
|
@@ -11,8 +12,13 @@ module Arrest
|
|
11
12
|
"#{parent.location}/#{self.resource_name}"
|
12
13
|
end
|
13
14
|
|
15
|
+
def build parent, hash
|
16
|
+
self.new parent, hash
|
17
|
+
end
|
18
|
+
|
14
19
|
|
15
20
|
def all_for parent
|
21
|
+
raise "Parent has no id yet" unless parent.id
|
16
22
|
body_root(source().get self.resource_path_for(parent)).map do |h|
|
17
23
|
self.build(parent, h)
|
18
24
|
end
|
data/lib/arrest/root_resource.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Arrest
|
2
|
+
|
3
|
+
class Source
|
4
|
+
class << self
|
5
|
+
attr_reader :source
|
6
|
+
attr_reader :mod
|
7
|
+
|
8
|
+
def source=(host=nil)
|
9
|
+
if host == nil || host.blank?
|
10
|
+
@source = MemSource.new
|
11
|
+
else
|
12
|
+
@source = HttpSource.new host
|
13
|
+
end
|
14
|
+
@source
|
15
|
+
end
|
16
|
+
|
17
|
+
def mod=(mod=nil)
|
18
|
+
if mod == nil
|
19
|
+
@mod = Kernel
|
20
|
+
elsif mod.is_a?(Module)
|
21
|
+
@mod = mod
|
22
|
+
else
|
23
|
+
raise "Expected module but got #{mod.class.name}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
Source.mod = nil
|
30
|
+
end
|
data/lib/arrest/version.rb
CHANGED
data/test/unit.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'arrest'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class Zoo < Arrest::RootResource
|
5
|
+
|
6
|
+
attributes :name
|
7
|
+
|
8
|
+
has_many :animals
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
class Animal < Arrest::RestChild
|
13
|
+
|
14
|
+
attributes :kind, :age
|
15
|
+
|
16
|
+
parent :zoo
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class FirstTest < Test::Unit::TestCase
|
21
|
+
|
22
|
+
def setup
|
23
|
+
Arrest::Source.source = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_mem_src
|
27
|
+
Arrest::Source.source = nil
|
28
|
+
src = Arrest::Source.source
|
29
|
+
assert_equal Arrest::MemSource, src.class
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_init
|
33
|
+
zooname = "Hagenbecks"
|
34
|
+
z = Zoo.new({:name => zooname})
|
35
|
+
assert_equal zooname, z.name
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_create
|
39
|
+
zoo_count_before = Zoo.all.length
|
40
|
+
new_zoo = Zoo.new({:name => "Foo"})
|
41
|
+
new_zoo.save
|
42
|
+
zoo_count_after = Zoo.all.length
|
43
|
+
|
44
|
+
assert_equal (zoo_count_before + 1), zoo_count_after
|
45
|
+
assert new_zoo.id != nil
|
46
|
+
|
47
|
+
zoo_the_last = Zoo.all.last
|
48
|
+
assert_equal new_zoo.name, zoo_the_last.name
|
49
|
+
|
50
|
+
zoo_reloaded = Zoo.find(new_zoo.id)
|
51
|
+
assert_equal new_zoo.name, zoo_reloaded.name
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_create_and_load
|
55
|
+
zoo_count_before = Zoo.all.length
|
56
|
+
new_zoo = Zoo.new({:name => "Foo"})
|
57
|
+
new_zoo.save
|
58
|
+
zoo_count_after = Zoo.all.length
|
59
|
+
|
60
|
+
assert_equal (zoo_count_before + 1), zoo_count_after
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_update
|
64
|
+
new_zoo = Zoo.new({:name => "Foo"})
|
65
|
+
new_zoo.save
|
66
|
+
|
67
|
+
new_zoo_name = "Hagenbecks"
|
68
|
+
new_zoo.name = new_zoo_name
|
69
|
+
new_zoo.save
|
70
|
+
|
71
|
+
assert new_zoo.id != nil
|
72
|
+
|
73
|
+
zoo_reloaded = Zoo.find(new_zoo.id)
|
74
|
+
|
75
|
+
assert_equal new_zoo_name, zoo_reloaded.name
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_child
|
79
|
+
new_zoo = Zoo.new({:name => "Foo"})
|
80
|
+
new_zoo.save
|
81
|
+
|
82
|
+
animal_kind = "mouse"
|
83
|
+
new_animal = Animal.new new_zoo, {:kind => animal_kind, :age => 42}
|
84
|
+
assert new_zoo.id != nil
|
85
|
+
assert_equal new_zoo.id, new_animal.zoo.id
|
86
|
+
assert_equal new_zoo.id, new_animal.parent.id
|
87
|
+
|
88
|
+
new_animal.save
|
89
|
+
|
90
|
+
assert new_animal.id != nil
|
91
|
+
|
92
|
+
zoo_reloaded = Zoo.find(new_zoo.id)
|
93
|
+
|
94
|
+
|
95
|
+
assert_equal 1, zoo_reloaded.animals.length
|
96
|
+
assert_equal 42, zoo_reloaded.animals.first.age
|
97
|
+
|
98
|
+
animal_reloaded = zoo_reloaded.animals.first
|
99
|
+
|
100
|
+
assert_equal new_zoo.id, animal_reloaded.zoo.id
|
101
|
+
|
102
|
+
assert_equal animal_kind, zoo_reloaded.animals.first.kind
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_child_update
|
106
|
+
new_zoo = Zoo.new({:name => "Foo"})
|
107
|
+
new_zoo.save
|
108
|
+
|
109
|
+
animal_kind = "mouse"
|
110
|
+
new_animal = Animal.new new_zoo, {:kind => "foo", :age => 42}
|
111
|
+
new_animal.save
|
112
|
+
|
113
|
+
animal_reloaded = new_zoo.animals.first
|
114
|
+
animal_reloaded.kind = animal_kind
|
115
|
+
animal_reloaded.save
|
116
|
+
|
117
|
+
assert_equal animal_kind, animal_reloaded.kind
|
118
|
+
|
119
|
+
animal_retry = new_zoo.animals.last
|
120
|
+
assert_equal animal_kind, animal_retry.kind
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arrest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,19 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-11-
|
13
|
-
dependencies:
|
12
|
+
date: 2011-11-07 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: &18515300 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *18515300
|
14
25
|
description: Consume a rest API in a AR like fashion
|
15
26
|
email:
|
16
27
|
- axel.tetzlaff@fortytools.com
|
@@ -29,7 +40,9 @@ files:
|
|
29
40
|
- lib/arrest/mem_source.rb
|
30
41
|
- lib/arrest/rest_child.rb
|
31
42
|
- lib/arrest/root_resource.rb
|
43
|
+
- lib/arrest/source.rb
|
32
44
|
- lib/arrest/version.rb
|
45
|
+
- test/unit.rb
|
33
46
|
homepage: ''
|
34
47
|
licenses: []
|
35
48
|
post_install_message:
|