frenetic 0.0.12 → 0.0.20.alpha.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +1 -0
  2. data/.irbrc +3 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +0 -1
  5. data/Gemfile +1 -3
  6. data/README.md +138 -125
  7. data/frenetic.gemspec +5 -6
  8. data/lib/frenetic.rb +31 -43
  9. data/lib/frenetic/concerns/collection_rest_methods.rb +13 -0
  10. data/lib/frenetic/concerns/configurable.rb +59 -0
  11. data/lib/frenetic/concerns/hal_linked.rb +59 -0
  12. data/lib/frenetic/concerns/member_rest_methods.rb +15 -0
  13. data/lib/frenetic/concerns/structured.rb +53 -0
  14. data/lib/frenetic/configuration.rb +40 -76
  15. data/lib/frenetic/middleware/hal_json.rb +23 -0
  16. data/lib/frenetic/resource.rb +77 -62
  17. data/lib/frenetic/resource_collection.rb +46 -0
  18. data/lib/frenetic/version.rb +2 -2
  19. data/spec/concerns/configurable_spec.rb +50 -0
  20. data/spec/concerns/hal_linked_spec.rb +116 -0
  21. data/spec/concerns/member_rest_methods_spec.rb +41 -0
  22. data/spec/concerns/structured_spec.rb +214 -0
  23. data/spec/configuration_spec.rb +99 -0
  24. data/spec/fixtures/test_api_requests.rb +88 -0
  25. data/spec/frenetic_spec.rb +137 -0
  26. data/spec/middleware/hal_json_spec.rb +83 -0
  27. data/spec/resource_collection_spec.rb +80 -0
  28. data/spec/resource_spec.rb +211 -0
  29. data/spec/spec_helper.rb +4 -13
  30. data/spec/support/rspec.rb +5 -0
  31. data/spec/support/webmock.rb +3 -0
  32. metadata +59 -57
  33. data/.rvmrc +0 -1
  34. data/lib/frenetic/hal_json.rb +0 -23
  35. data/lib/frenetic/hal_json/response_wrapper.rb +0 -43
  36. data/lib/recursive_open_struct.rb +0 -79
  37. data/spec/fixtures/vcr_cassettes/description_error_unauthorized.yml +0 -36
  38. data/spec/fixtures/vcr_cassettes/description_success.yml +0 -38
  39. data/spec/lib/frenetic/configuration_spec.rb +0 -189
  40. data/spec/lib/frenetic/hal_json/response_wrapper_spec.rb +0 -70
  41. data/spec/lib/frenetic/hal_json_spec.rb +0 -68
  42. data/spec/lib/frenetic/resource_spec.rb +0 -182
  43. data/spec/lib/frenetic_spec.rb +0 -129
data/.rvmrc DELETED
@@ -1 +0,0 @@
1
- rvm --create use ruby-1.9.3-p194@frenetic > /dev/null
@@ -1,23 +0,0 @@
1
- require 'json'
2
- require 'recursive_open_struct'
3
- require 'frenetic/hal_json/response_wrapper'
4
-
5
- class Frenetic
6
-
7
- class HalJson < Faraday::Middleware
8
- def call( environment )
9
- @app.call(environment).on_complete { |env| on_complete(env) }
10
- end
11
-
12
- def on_complete( env )
13
- if success? env
14
- env[:body] = ResponseWrapper.new( JSON.parse(env[:body]) )
15
- end
16
- end
17
-
18
- def success?( env )
19
- (200..201) === env[:status]
20
- end
21
- end
22
-
23
- end
@@ -1,43 +0,0 @@
1
- class Frenetic
2
- class HalJson < Faraday::Middleware
3
- # TODO: The API for this differs greatly from the `inspect` output.
4
- # Perhaps the Hash keys should be normalized and then aliased back to the original keys?
5
- class ResponseWrapper < RecursiveOpenStruct
6
- include Enumerable
7
-
8
- def []( key )
9
- self.send(key)
10
- end
11
-
12
- def members
13
- methods(false).grep(%r{_as_a_hash}).map { |m| m[0...-10] }
14
- end
15
- alias_method :keys, :members
16
-
17
- def each
18
- members.each do |method|
19
- yield method, send(method)
20
- end
21
-
22
- self
23
- end
24
-
25
- class << self
26
- # Do not define setters
27
- def define_setter( * ); end
28
-
29
- def define_getter( method_name, hash_key )
30
- method_name = case method_name
31
- when :_embedded then :resources
32
- when :_links then :links
33
- when :href then :url
34
- else method_name
35
- end
36
-
37
- super
38
- end
39
- end
40
-
41
- end
42
- end
43
- end
@@ -1,79 +0,0 @@
1
- # Stolen from https://github.com/aetherknight/recursive-open-struct/blob/master/lib/recursive_open_struct.rb
2
- require 'ostruct'
3
-
4
- class RecursiveOpenStruct < OpenStruct
5
-
6
- def new_ostruct_member(name)
7
- name = name.to_sym
8
- unless self.respond_to?(name)
9
- class << self; self; end.class_eval { define_accessors name, name }
10
- end
11
- name
12
- end
13
-
14
- def keys
15
- @table.keys
16
- end
17
-
18
- ###
19
-
20
- def self.define_accessors( *args )
21
- define_getter *args
22
- define_setter *args
23
- define_getter_as_a_hash *args
24
- end
25
-
26
- def self.define_getter( method_name, hash_key )
27
- define_method( method_name ) do
28
- v = @table[hash_key]
29
- v.is_a?(Hash) ? self.class.new(v) : v
30
- end
31
- end
32
-
33
- def self.define_setter( method_name, hash_key )
34
- define_method("#{method_name}=") { |x| modifiable[hash_key] = x }
35
- end
36
-
37
- def self.define_getter_as_a_hash( method_name, hash_key )
38
- define_method("#{method_name}_as_a_hash") { @table[hash_key] }
39
- end
40
-
41
- ###
42
-
43
- def debug_inspect(indent_level = 0, recursion_limit = 12)
44
- display_recursive_open_struct(@table, indent_level, recursion_limit)
45
- end
46
-
47
- def display_recursive_open_struct(ostrct_or_hash, indent_level, recursion_limit)
48
-
49
- if recursion_limit <= 0 then
50
- # protection against recursive structure (like in the tests)
51
- puts ' '*indent_level + '(recursion limit reached)'
52
- else
53
- #puts ostrct_or_hash.inspect
54
- if ostrct_or_hash.is_a?(self.class) then
55
- ostrct_or_hash = ostrct_or_hash.marshal_dump
56
- end
57
-
58
- # We'll display the key values like this : key = value
59
- # to align display, we look for the maximum key length of the data that will be displayed
60
- # (everything except hashes)
61
- data_indent = ostrct_or_hash
62
- .reject { |k, v| v.is_a?(self.class) || v.is_a?(Hash) }
63
- .max {|a,b| a[0].to_s.length <=> b[0].to_s.length}[0].length
64
- # puts "max length = #{data_indent}"
65
-
66
- ostrct_or_hash.each do |key, value|
67
- if (value.is_a?(self.class) || value.is_a?(Hash)) then
68
- puts ' '*indent_level + key.to_s + '.'
69
- display_recursive_open_struct(value, indent_level + 1, recursion_limit - 1)
70
- else
71
- puts ' '*indent_level + key.to_s + ' '*(data_indent - key.to_s.length) + ' = ' + value.inspect
72
- end
73
- end
74
- end
75
-
76
- true
77
- end
78
-
79
- end
@@ -1,36 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: get
5
- uri: http://1234567890:@example.org:5447/api
6
- body:
7
- encoding: US-ASCII
8
- string: ''
9
- headers:
10
- Accept-Encoding:
11
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
- Accept:
13
- - ! '*/*'
14
- User-Agent:
15
- - Ruby
16
- response:
17
- status:
18
- code: 401
19
- message: ! 'Unauthorized '
20
- headers:
21
- Content-Type:
22
- - application/json
23
- Content-Length:
24
- - '32'
25
- Server:
26
- - WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09)
27
- Date:
28
- - Sun, 08 Apr 2012 02:09:53 GMT
29
- Connection:
30
- - Keep-Alive
31
- body:
32
- encoding: US-ASCII
33
- string: ! '{\"error\":\"401 Unauthorized\"}'
34
- http_version: !!null
35
- recorded_at: Sun, 08 Apr 2012 02:09:53 GMT
36
- recorded_with: VCR 2.0.1
@@ -1,38 +0,0 @@
1
- ---
2
- http_interactions:
3
- - request:
4
- method: get
5
- uri: http://1234567890:@example.org:5447/api
6
- body:
7
- encoding: US-ASCII
8
- string: ''
9
- headers:
10
- Accept-Encoding:
11
- - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
- Accept:
13
- - ! '*/*'
14
- User-Agent:
15
- - Ruby
16
- response:
17
- status:
18
- code: 200
19
- message: ! 'OK '
20
- headers:
21
- Content-Type:
22
- - application/json
23
- Content-Length:
24
- - '620'
25
- Server:
26
- - WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09)
27
- Date:
28
- - Sun, 08 Apr 2012 02:09:53 GMT
29
- Connection:
30
- - Keep-Alive
31
- body:
32
- encoding: US-ASCII
33
- string: ! '{"_links":{"self":{"href":"/api/"},"inkers":{"href":"/api/inkers"},"inker":{"href":"/api/inker/{id}"},"sessions":{"href":"/api/sessions"}},"_embedded":{"schema":{"_links":{"self":{"href":"/api/schema"}},"mediaType":"application/vnd.customink-inker_directory-v1+json","inker":{"description":"A
34
- CustomInk employee","type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"city":{"type":"string"},"images":{"type":"array","items":{"type":"object"}}}},"session":{"description":"A
35
- gatewat for generating an authenticated session.","type":"object","properties":{"st-ticket-FOO":"string"}}}}}'
36
- http_version: !!null
37
- recorded_at: Sun, 08 Apr 2012 02:09:53 GMT
38
- recorded_with: VCR 2.0.1
@@ -1,189 +0,0 @@
1
- describe Frenetic::Configuration do
2
- let(:config) { { url:'http://example.org' } }
3
-
4
- let(:cache_cfg) do
5
- {
6
- metastore: 'foo',
7
- entitystore: 'bar'
8
- }
9
- end
10
-
11
- let(:instance) { described_class.new( config ) }
12
-
13
- subject { instance }
14
-
15
- it { should respond_to(:adapter) }
16
- it { should respond_to(:cache) }
17
- it { should respond_to(:url) }
18
- it { should respond_to(:username) }
19
- it { should respond_to(:password) }
20
- it { should respond_to(:headers) }
21
- it { should respond_to(:request) }
22
- it { should respond_to(:response) }
23
- it { should respond_to(:middleware) }
24
-
25
- describe '#attributes' do
26
- before { instance.use 'MyMiddleware' }
27
-
28
- subject { instance.attributes }
29
-
30
- it { should include(:adapter) }
31
- it { should include(:cache) }
32
- it { should include(:url) }
33
- it { should include(:username) }
34
- it { should include(:password) }
35
- it { should include(:headers) }
36
- it { should include(:request) }
37
- it { should include(:response) }
38
- it { should_not include(:middleware) }
39
-
40
- it 'should validate the configuration' do
41
- instance.should_receive :validate!
42
-
43
- subject
44
- end
45
-
46
- context 'with string keys' do
47
- let(:config) { {'url' => 'https://example.org'} }
48
-
49
- it 'should symbolize the keys' do
50
- subject[:url].should == 'https://example.org'
51
- end
52
- end
53
- end
54
-
55
- describe '#headers' do
56
- let(:attrs) { instance.headers }
57
-
58
- context 'Accepts' do
59
- subject { attrs[:accept] }
60
-
61
- it { should == 'application/hal+json' }
62
-
63
- context 'with other specific headers' do
64
- before { config.merge!(headers:{foo:123} )}
65
-
66
- it { should == 'application/hal+json' }
67
- end
68
-
69
- context 'with a custom Accepts header' do
70
- before { config.merge!(headers:{'accept' => 'application/vnd.yoursite-v1.hal+json'} )}
71
-
72
- it { should == 'application/vnd.yoursite-v1.hal+json' }
73
- end
74
- end
75
-
76
- context 'User-Agent' do
77
- subject { attrs[:user_agent] }
78
-
79
- it { should match %r{Frenetic v\d+\.\d+\.\d+; .+\Z} }
80
-
81
- context 'with a custom value' do
82
- before { config.merge!( headers:{user_agent:'MyApp'}) }
83
-
84
- it { should match %r{\AMyApp \(Frenetic v\d+\.\d+\.\d+; .+\)\Z} }
85
- end
86
- end
87
- end
88
-
89
- describe '#cache' do
90
- before { config.merge!(cache:cache_cfg) }
91
-
92
- subject { instance.cache }
93
-
94
- it { should include(metastore:'foo') }
95
- it { should include(entitystore:'bar') }
96
- it { should include(ignore_headers:%w[Set-Cookie X-Content-Digest]) }
97
-
98
- context 'with custom ignore headers' do
99
- before { cache_cfg.merge!(ignore_headers:%w{Set-Cookie X-My-Header}) }
100
-
101
- it { should include(ignore_headers:%w[Set-Cookie X-My-Header X-Content-Digest]) }
102
- end
103
- end
104
-
105
- describe '#username' do
106
- subject { instance.username }
107
-
108
- before { config.merge!(api_key:'api_key') }
109
-
110
- it { should == 'api_key' }
111
-
112
- context 'and an App ID' do
113
- before { config.merge!(app_id:'app_id') }
114
-
115
- it { should == 'app_id' }
116
- end
117
- end
118
-
119
- describe '#password' do
120
- subject { instance.password }
121
-
122
- before { config.merge!(api_key:'api_key') }
123
-
124
- it { should be_nil }
125
-
126
- context 'and an App ID' do
127
- before { config.merge!(app_id:'app_id') }
128
-
129
- it { should == 'api_key' }
130
- end
131
- end
132
-
133
- describe '#initialize' do
134
- subject { instance.attributes }
135
-
136
- it { should be_a Hash }
137
- it { should_not be_empty }
138
- end
139
-
140
- describe '#validate!' do
141
- subject { instance.validate! }
142
-
143
- shared_examples_for 'a misconfigured instance' do
144
- it 'by raising an error when empty' do
145
- expect{ subject }.to raise_error Frenetic::ConfigurationError
146
- end
147
- end
148
-
149
- context ':url' do
150
- before { config.delete :url }
151
-
152
- it_should_behave_like 'a misconfigured instance'
153
- end
154
-
155
- context ':cache' do
156
- context 'with a missing :metastore' do
157
- before { config.merge!(cache:{}) }
158
-
159
- it_should_behave_like 'a misconfigured instance'
160
- end
161
-
162
- context 'with a missing :entitystore' do
163
- before { config.merge!(cache:{metastore:'store'}) }
164
-
165
- it_should_behave_like 'a misconfigured instance'
166
- end
167
-
168
- context 'with no missing properties' do
169
- before { config.merge!(cache:{metastore:'mstore',entitystore:'estore'}) }
170
-
171
- it 'should not raise an error' do
172
- expect{ subject }.to_not raise_error
173
- end
174
- end
175
- end
176
- end
177
-
178
- describe '#use' do
179
- before do
180
- stub_const 'MyMiddleware', Class.new
181
-
182
- instance.use MyMiddleware, foo:123
183
- end
184
-
185
- it 'should use the middleware' do
186
- subject.middleware.should include [ MyMiddleware, {foo:123} ]
187
- end
188
- end
189
- end
@@ -1,70 +0,0 @@
1
- describe Frenetic::HalJson::ResponseWrapper do
2
- let(:properties) do
3
- { 'a' => 1, 'b' => 2 }
4
- end
5
- let(:wrapper) { Frenetic::HalJson::ResponseWrapper.new( properties ) }
6
-
7
- subject { wrapper }
8
-
9
- describe "#members" do
10
- subject { wrapper.members }
11
-
12
- its(:size) { should == 2 }
13
- its(:first) { should == 'a' }
14
- its(:last) { should == 'b' }
15
- end
16
-
17
- describe "#keys" do
18
- subject { wrapper.keys }
19
-
20
- it { should == wrapper.members }
21
- end
22
-
23
- describe "#each" do
24
- before do
25
- @items = []
26
- wrapper.each do |*args|
27
- @items << args
28
- end
29
- end
30
-
31
- it { should be_a Frenetic::HalJson::ResponseWrapper }
32
- it "should iterate over each getter" do
33
- @items.should == [ ['a',1], ['b',2] ]
34
- end
35
- end
36
-
37
- describe ".define_setter" do
38
- subject { wrapper.methods(false) }
39
-
40
- it "should not create setters" do
41
- subject.none? { |name| name.to_s =~ %r{=} }.should be_true
42
- end
43
- end
44
-
45
- describe ".define_getter" do
46
- context "with a :_links property" do
47
- let(:properties) { { '_links' => 'foo' } }
48
-
49
- it "should create a :links property" do
50
- wrapper.links.should == 'foo'
51
- end
52
- end
53
-
54
- context "with a :_embedded property" do
55
- let(:properties) { { '_embedded' => 'foo' } }
56
-
57
- it "should create a :resources property" do
58
- wrapper.resources.should == 'foo'
59
- end
60
- end
61
-
62
- context "with a :_embedded property" do
63
- let(:properties) { { 'href' => 'foo' } }
64
-
65
- it "should create a :url property" do
66
- wrapper.url.should == 'foo'
67
- end
68
- end
69
- end
70
- end