frenetic 0.0.12 → 0.0.20.alpha.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.
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