frenetic 0.0.20.alpha.6 → 1.0.0.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.ruby-version +1 -1
- data/Appraisals +9 -0
- data/Gemfile +1 -1
- data/README.md +2 -2
- data/frenetic.gemspec +8 -5
- data/gemfiles/faraday_08.gemfile +10 -0
- data/gemfiles/faraday_08.gemfile.lock +77 -0
- data/gemfiles/faraday_09.gemfile +10 -0
- data/gemfiles/faraday_09.gemfile.lock +77 -0
- data/lib/frenetic.rb +57 -30
- data/lib/frenetic/briefly_memoizable.rb +34 -0
- data/lib/frenetic/concerns/collection_rest_methods.rb +1 -1
- data/lib/frenetic/concerns/hal_linked.rb +5 -35
- data/lib/frenetic/concerns/member_rest_methods.rb +0 -2
- data/lib/frenetic/concerns/structured.rb +0 -5
- data/lib/frenetic/connection.rb +110 -0
- data/lib/frenetic/errors.rb +112 -0
- data/lib/frenetic/hypermedia_link.rb +74 -0
- data/lib/frenetic/hypermedia_link_set.rb +43 -0
- data/lib/frenetic/middleware/hal_json.rb +9 -12
- data/lib/frenetic/resource.rb +22 -6
- data/lib/frenetic/resource_collection.rb +0 -1
- data/lib/frenetic/resource_mockery.rb +55 -1
- data/lib/frenetic/version.rb +1 -1
- data/spec/{concerns/breifly_memoizable_spec.rb → briefly_memoizable_spec.rb} +10 -18
- data/spec/concerns/hal_linked_spec.rb +49 -62
- data/spec/concerns/member_rest_methods_spec.rb +8 -10
- data/spec/concerns/structured_spec.rb +70 -75
- data/spec/connection_spec.rb +137 -0
- data/spec/fixtures/test_api_requests.rb +8 -2
- data/spec/frenetic_spec.rb +221 -133
- data/spec/hypermedia_link_set_spec.rb +155 -0
- data/spec/hypermedia_link_spec.rb +153 -0
- data/spec/middleware/hal_json_spec.rb +13 -15
- data/spec/resource_collection_spec.rb +17 -16
- data/spec/resource_mockery_spec.rb +69 -0
- data/spec/resource_spec.rb +110 -63
- data/spec/support/rspec.rb +0 -1
- metadata +88 -75
- data/lib/frenetic/concerns/briefly_memoizable.rb +0 -34
- data/lib/frenetic/concerns/configurable.rb +0 -59
- data/lib/frenetic/concerns/resource_mockery.rb +0 -48
- data/lib/frenetic/configuration.rb +0 -88
- data/spec/concerns/configurable_spec.rb +0 -50
- data/spec/concerns/resource_mockery_spec.rb +0 -56
- data/spec/configuration_spec.rb +0 -134
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
|
3
|
-
# Memoizes method calls, but only for a specific period of time.
|
4
|
-
# Useful for supporting HTTP Cache-Control without an external caching layer
|
5
|
-
# like Rack::Cache
|
6
|
-
module BrieflyMemoizable
|
7
|
-
extend ActiveSupport::Concern
|
8
|
-
|
9
|
-
module ClassMethods
|
10
|
-
def briefly_memoize( symbol )
|
11
|
-
original_method = "_unmemoized_#{symbol}".to_sym
|
12
|
-
memoized_ivar = "@#{symbol}"
|
13
|
-
age_ivar = "@#{symbol}_age"
|
14
|
-
|
15
|
-
class_eval <<-EOS
|
16
|
-
if method_defined?(:#{original_method}) # if method_defined?(:_unmemoized_mime_type)
|
17
|
-
raise "Already memoized #{symbol}" # raise "Already memoized mime_type"
|
18
|
-
end # end
|
19
|
-
alias #{original_method} #{symbol} # alias _unmemoized_mime_type mime_type
|
20
|
-
|
21
|
-
def #{symbol}(*args) # def mime_type(*args)
|
22
|
-
#{memoized_ivar} = nil if #{age_ivar} && Time.now > #{age_ivar} # @mime_type = nil if @mime_type_age && Time.now > @mime_type_age
|
23
|
-
#
|
24
|
-
#{memoized_ivar} ||= #{original_method}(*args) # @mime_type ||= _unmemoized_mime_type(*args)
|
25
|
-
end # end
|
26
|
-
|
27
|
-
def reload_#{symbol}! # def reload_mime_type!
|
28
|
-
#{memoized_ivar} = nil # @mime_type = nil
|
29
|
-
end # end
|
30
|
-
EOS
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
require 'active_support/configurable'
|
2
|
-
require 'active_support/concern'
|
3
|
-
|
4
|
-
require 'frenetic/configuration'
|
5
|
-
|
6
|
-
class Frenetic
|
7
|
-
module Configurable
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
|
10
|
-
included do
|
11
|
-
include ActiveSupport::Configurable
|
12
|
-
# Don't allow the class to be configured
|
13
|
-
class << self
|
14
|
-
undef :configure
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def initialize( cfg = {} )
|
19
|
-
config.merge! Frenetic::Configuration.new(cfg).attributes
|
20
|
-
|
21
|
-
@builder_config = Proc.new if block_given?
|
22
|
-
end
|
23
|
-
|
24
|
-
def configure
|
25
|
-
yield config
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def validate_configuration!
|
31
|
-
raise( ConfigError, 'A URL must be defined' ) unless config.url
|
32
|
-
end
|
33
|
-
|
34
|
-
def configure_authentication( builder )
|
35
|
-
if config.username
|
36
|
-
builder.request :basic_auth, config.username, config.password
|
37
|
-
end
|
38
|
-
|
39
|
-
if config.api_token
|
40
|
-
builder.request :token_auth, config.api_token
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def configure_caching( builder )
|
45
|
-
if config.cache[:metastore]
|
46
|
-
dependency 'rack-cache'
|
47
|
-
|
48
|
-
builder.use FaradayMiddleware::RackCompatible, Rack::Cache::Context, config.cache
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def dependency( lib = nil )
|
53
|
-
lib ? require(lib) : yield
|
54
|
-
rescue NameError, LoadError => err
|
55
|
-
raise ConfigError, "Missing dependency for #{self}: #{err.message}"
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'ostruct'
|
2
|
-
require 'active_support/concern'
|
3
|
-
|
4
|
-
class Frenetic
|
5
|
-
module ResourceMockery
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
included do
|
9
|
-
# I'm sure this violates some sort of CS principle or best practice,
|
10
|
-
# but it solves the problem for now.
|
11
|
-
superclass.send :instance_variable_set, '@mock_class', self
|
12
|
-
end
|
13
|
-
|
14
|
-
def attributes
|
15
|
-
@params
|
16
|
-
end
|
17
|
-
|
18
|
-
def properties
|
19
|
-
@params.each_with_object({}) do |(k,v), props|
|
20
|
-
props[k] = v.class.to_s.underscore
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# Provides a place for a Resources that are mocked to declare reasonable
|
25
|
-
# default values for Mock Resources
|
26
|
-
def default_attributes
|
27
|
-
{}
|
28
|
-
end
|
29
|
-
|
30
|
-
module ClassMethods
|
31
|
-
def api_client
|
32
|
-
superclass.api_client
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def build_params( p )
|
39
|
-
defaults = default_attributes.with_indifferent_access
|
40
|
-
@params = defaults.merge( (p || {}).with_indifferent_access )
|
41
|
-
end
|
42
|
-
|
43
|
-
def build_structure
|
44
|
-
@structure = OpenStruct.new( @attrs )
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
require 'addressable/uri'
|
2
|
-
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
-
|
4
|
-
class Frenetic
|
5
|
-
class Configuration
|
6
|
-
|
7
|
-
@@defaults = {
|
8
|
-
headers: {
|
9
|
-
accept: 'application/hal+json',
|
10
|
-
user_agent: "Frenetic v#{Frenetic::VERSION}; #{Socket.gethostname}"
|
11
|
-
}
|
12
|
-
}
|
13
|
-
|
14
|
-
def initialize( cfg = {} )
|
15
|
-
@_cfg = cfg.symbolize_keys
|
16
|
-
end
|
17
|
-
|
18
|
-
def adapter
|
19
|
-
@_cfg[:adapter] || Faraday.default_adapter
|
20
|
-
end
|
21
|
-
|
22
|
-
def api_token
|
23
|
-
@_cfg[:api_token]
|
24
|
-
end
|
25
|
-
|
26
|
-
def attributes
|
27
|
-
{
|
28
|
-
adapter: adapter,
|
29
|
-
api_token: api_token,
|
30
|
-
cache: cache,
|
31
|
-
default_root_cache_age: default_root_cache_age,
|
32
|
-
headers: headers,
|
33
|
-
password: password,
|
34
|
-
ssl: ssl,
|
35
|
-
test_mode: test_mode,
|
36
|
-
url: url,
|
37
|
-
username: username
|
38
|
-
}
|
39
|
-
end
|
40
|
-
|
41
|
-
def cache
|
42
|
-
if @_cfg[:cache] == :rack
|
43
|
-
{
|
44
|
-
metastore: 'file:tmp/rack/meta',
|
45
|
-
entitystore: 'file:tmp/rack/body',
|
46
|
-
ignore_headers: %w{Authorization Set-Cookie X-Content-Digest}
|
47
|
-
}
|
48
|
-
else
|
49
|
-
{}
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def default_root_cache_age
|
54
|
-
@_cfg[:default_root_cache_age]
|
55
|
-
end
|
56
|
-
|
57
|
-
def headers
|
58
|
-
@@defaults[:headers].merge( @_cfg[:headers] || {} ).tap do |h|
|
59
|
-
if @_cfg[:headers] && @_cfg[:headers][:user_agent]
|
60
|
-
if h[:user_agent] != @@defaults[:headers][:user_agent]
|
61
|
-
h[:user_agent] = "#{h[:user_agent]} (#{@@defaults[:headers][:user_agent]})"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def password
|
68
|
-
@_cfg[:password] || @_cfg[:api_key]
|
69
|
-
end
|
70
|
-
|
71
|
-
def ssl
|
72
|
-
@_cfg[:ssl] || { verify:true }
|
73
|
-
end
|
74
|
-
|
75
|
-
def test_mode
|
76
|
-
@_cfg[:test_mode] || false
|
77
|
-
end
|
78
|
-
|
79
|
-
def url
|
80
|
-
Addressable::URI.parse @_cfg[:url]
|
81
|
-
end
|
82
|
-
|
83
|
-
def username
|
84
|
-
@_cfg[:username] || @_cfg[:app_id]
|
85
|
-
end
|
86
|
-
|
87
|
-
end
|
88
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
describe Frenetic::Configurable do
|
2
|
-
let(:test_cfg) do
|
3
|
-
{
|
4
|
-
url:'http://example.org'
|
5
|
-
}
|
6
|
-
end
|
7
|
-
|
8
|
-
subject(:instance) { Frenetic.new( test_cfg ) }
|
9
|
-
|
10
|
-
describe '#config' do
|
11
|
-
subject { instance.config }
|
12
|
-
|
13
|
-
it { should_not be_empty }
|
14
|
-
end
|
15
|
-
|
16
|
-
describe '#configure' do
|
17
|
-
subject do
|
18
|
-
cfg = nil
|
19
|
-
instance.configure { |c| cfg = c }
|
20
|
-
cfg
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'should yield the configuration' do
|
24
|
-
subject.should be_a Hash
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
describe '.configure' do
|
29
|
-
subject { Frenetic.configure{|c|} }
|
30
|
-
|
31
|
-
it 'should not exist' do
|
32
|
-
expect{ subject }.to raise_error NoMethodError
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe '#initialize' do
|
37
|
-
let(:callback) do
|
38
|
-
Proc.new { |b| }
|
39
|
-
end
|
40
|
-
|
41
|
-
subject do
|
42
|
-
Frenetic.new( &callback ).instance_variable_get( "@builder_config" )
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'retain block arguments' do
|
46
|
-
subject.should eq callback
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
require 'frenetic/resource_mockery'
|
4
|
-
|
5
|
-
describe Frenetic::ResourceMockery do
|
6
|
-
let(:my_temp_resource) do
|
7
|
-
Class.new(Frenetic::Resource)
|
8
|
-
end
|
9
|
-
|
10
|
-
let(:my_mocked_resource) do
|
11
|
-
Class.new(my_temp_resource) do
|
12
|
-
def default_attributes
|
13
|
-
{ qux:'qux' }
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
before do
|
19
|
-
stub_const 'MyNamespace::MyMockedResource', my_mocked_resource
|
20
|
-
|
21
|
-
MyNamespace::MyMockedResource.send :include, described_class
|
22
|
-
end
|
23
|
-
|
24
|
-
let(:params) { { foo:1, bar:'baz' } }
|
25
|
-
|
26
|
-
subject { MyNamespace::MyMockedResource.new params }
|
27
|
-
|
28
|
-
it 'should violate some basic CS principles by telling the parent-class of its existence' do
|
29
|
-
expect(my_temp_resource.instance_variables).to include :@mock_class
|
30
|
-
end
|
31
|
-
|
32
|
-
describe '#properties' do
|
33
|
-
subject { super().properties }
|
34
|
-
|
35
|
-
it 'should return a hash of available properties' do
|
36
|
-
subject.should include 'foo' => 'fixnum'
|
37
|
-
subject.should include 'bar' => 'string'
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe '#attributes' do
|
42
|
-
subject { super().attributes }
|
43
|
-
|
44
|
-
it 'should return a hash of the resources attributes' do
|
45
|
-
subject.should include 'foo' => 1
|
46
|
-
subject.should include 'bar' => 'baz'
|
47
|
-
subject.should include 'qux' => 'qux'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
describe '#default_attributes' do
|
52
|
-
it 'should allow implementors to specify sane defaults' do
|
53
|
-
subject.qux.should == 'qux'
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
data/spec/configuration_spec.rb
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
describe Frenetic::Configuration do
|
2
|
-
let(:cfg) { Hash.new }
|
3
|
-
|
4
|
-
subject(:instance) { described_class.new cfg }
|
5
|
-
|
6
|
-
describe '#adapter' do
|
7
|
-
subject { instance.adapter }
|
8
|
-
|
9
|
-
it { should == :net_http }
|
10
|
-
end
|
11
|
-
|
12
|
-
describe '#attributes' do
|
13
|
-
subject { instance.attributes }
|
14
|
-
|
15
|
-
it { should include :adapter }
|
16
|
-
it { should include :api_token }
|
17
|
-
it { should include :cache }
|
18
|
-
it { should include :default_root_cache_age }
|
19
|
-
it { should include :headers }
|
20
|
-
it { should include :password }
|
21
|
-
it { should include :ssl }
|
22
|
-
it { should include :test_mode }
|
23
|
-
it { should include :url }
|
24
|
-
it { should include :username }
|
25
|
-
end
|
26
|
-
|
27
|
-
describe '#api_token' do
|
28
|
-
let(:cfg) do
|
29
|
-
{ api_token:'API_TOKEN' }
|
30
|
-
end
|
31
|
-
|
32
|
-
subject { instance.api_token }
|
33
|
-
|
34
|
-
it { should == 'API_TOKEN' }
|
35
|
-
end
|
36
|
-
|
37
|
-
describe '#cache' do
|
38
|
-
subject { instance.cache }
|
39
|
-
|
40
|
-
context 'with a value of :rack' do
|
41
|
-
let(:cfg) do
|
42
|
-
{ cache: :rack }
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'should return Rack::Cache options' do
|
46
|
-
subject.should include metastore:'file:tmp/rack/meta'
|
47
|
-
subject.should include entitystore:'file:tmp/rack/body'
|
48
|
-
subject.should include ignore_headers:%w{Authorization Set-Cookie X-Content-Digest}
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
describe '#default_root_cache_age' do
|
54
|
-
let(:cfg) do
|
55
|
-
{ default_root_cache_age:3600 }
|
56
|
-
end
|
57
|
-
|
58
|
-
subject { instance.default_root_cache_age }
|
59
|
-
|
60
|
-
it { should == 3600 }
|
61
|
-
end
|
62
|
-
|
63
|
-
describe '#headers' do
|
64
|
-
let(:cfg) do
|
65
|
-
{ headers:{ accept:'MIME', x_foo:'BAR' } }
|
66
|
-
end
|
67
|
-
|
68
|
-
subject { instance.headers }
|
69
|
-
|
70
|
-
it 'should properly merge in nested header values' do
|
71
|
-
subject.should include :user_agent
|
72
|
-
subject.should include accept:'MIME'
|
73
|
-
subject.should include x_foo:'BAR'
|
74
|
-
end
|
75
|
-
|
76
|
-
context 'with a custom User-Agent' do
|
77
|
-
let(:cfg) do
|
78
|
-
{ headers:{ user_agent:'Foo v1.1' } }
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'should append the Frenentic User-Agent' do
|
82
|
-
subject[:user_agent].should match %r{\AFoo v1.1 \(Frenetic v.[^;]*; [^)]*\)\Z}
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
describe '#password' do
|
88
|
-
subject { instance.password }
|
89
|
-
|
90
|
-
context 'with a specifed Api key' do
|
91
|
-
let(:cfg) do
|
92
|
-
{ api_key:'API_KEY' }
|
93
|
-
end
|
94
|
-
|
95
|
-
it { should == 'API_KEY' }
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
describe '#ssl' do
|
100
|
-
subject { instance.ssl }
|
101
|
-
|
102
|
-
it { should include verify:true }
|
103
|
-
end
|
104
|
-
|
105
|
-
describe '#test_mode' do
|
106
|
-
subject { instance.test_mode }
|
107
|
-
|
108
|
-
it { should be_false }
|
109
|
-
end
|
110
|
-
|
111
|
-
describe '#url' do
|
112
|
-
let(:cfg) do
|
113
|
-
{ url:'http://example.org' }
|
114
|
-
end
|
115
|
-
|
116
|
-
subject { instance.url }
|
117
|
-
|
118
|
-
it { should be_a Addressable::URI }
|
119
|
-
|
120
|
-
its(:to_s) { should == 'http://example.org' }
|
121
|
-
end
|
122
|
-
|
123
|
-
describe '#username' do
|
124
|
-
subject { instance.username }
|
125
|
-
|
126
|
-
context 'with a specifed App Id' do
|
127
|
-
let(:cfg) do
|
128
|
-
{ app_id:'APP_ID' }
|
129
|
-
end
|
130
|
-
|
131
|
-
it { should == 'APP_ID' }
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|