frenetic 0.0.20.alpha.6 → 1.0.0.alpha.1
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.
- 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
|