spigot 0.0.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/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +221 -0
- data/Rakefile +1 -0
- data/examples/.DS_Store +0 -0
- data/examples/active_record.rb +57 -0
- data/examples/model.rb +34 -0
- data/lib/.DS_Store +0 -0
- data/lib/spigot.rb +32 -0
- data/lib/spigot/active_record.rb +106 -0
- data/lib/spigot/base.rb +43 -0
- data/lib/spigot/config/.DS_Store +0 -0
- data/lib/spigot/config/spigot/github.yml +7 -0
- data/lib/spigot/configuration.rb +28 -0
- data/lib/spigot/errors.rb +8 -0
- data/lib/spigot/proxy.rb +40 -0
- data/lib/spigot/record.rb +71 -0
- data/lib/spigot/translator.rb +128 -0
- data/lib/spigot/version.rb +3 -0
- data/script/console.rb +31 -0
- data/spec/.DS_Store +0 -0
- data/spec/fixtures/.DS_Store +0 -0
- data/spec/fixtures/api_data.rb +25 -0
- data/spec/fixtures/mappings/active_user_map.rb +42 -0
- data/spec/fixtures/mappings/mappings.rb +7 -0
- data/spec/fixtures/mappings/post_map.rb +22 -0
- data/spec/fixtures/mappings/user_map.rb +33 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/spigot/active_record_spec.rb +95 -0
- data/spec/spigot/base_spec.rb +7 -0
- data/spec/spigot/configuration_spec.rb +60 -0
- data/spec/spigot/factory_spec.rb +5 -0
- data/spec/spigot/translator_spec.rb +165 -0
- data/spec/support/active_record.rb +15 -0
- data/spigot.gemspec +28 -0
- metadata +193 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module Spigot
|
2
|
+
module Mapping
|
3
|
+
|
4
|
+
class Post
|
5
|
+
|
6
|
+
def self.basic
|
7
|
+
{'post' => base}
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.namespaced
|
11
|
+
{'wrapper/post' => base}
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.base
|
17
|
+
{'title' => 'title', 'body' => 'description'}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Spigot
|
2
|
+
module Mapping
|
3
|
+
|
4
|
+
class User
|
5
|
+
def self.basic
|
6
|
+
{'user' => base}
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.with_options
|
10
|
+
{'user' => base.merge('spigot' => options)}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.with_conditions
|
14
|
+
{'user' => base.merge('spigot' => options.merge(conditions))}
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.base
|
20
|
+
{'full_name' => 'name', 'login' => 'username'}
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.options
|
24
|
+
{'primary_key' => 'username', 'foreign_key' => 'login'}
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.conditions
|
28
|
+
{'conditions' => 'username, name'}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'spigot'
|
3
|
+
require 'hashie'
|
4
|
+
require 'active_record'
|
5
|
+
require "test/unit"
|
6
|
+
require "mocha/setup"
|
7
|
+
|
8
|
+
%w(fixtures support).each do |dir|
|
9
|
+
Dir[File.join(Spigot.root, "spec/#{dir}/**/*.rb")].each {|f| require f}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Mocked Classes
|
13
|
+
User = Class.new(Hashie::Mash)
|
14
|
+
|
15
|
+
class ActiveUser < ActiveRecord::Base
|
16
|
+
include Spigot::Base
|
17
|
+
end
|
18
|
+
|
19
|
+
module Wrapper
|
20
|
+
Post = Class.new(Hashie::Mash)
|
21
|
+
end
|
22
|
+
|
23
|
+
def with_mapping(name, map)
|
24
|
+
before do
|
25
|
+
instance_variable_set("@prior_#{name}".to_sym, Spigot.config.translations)
|
26
|
+
Spigot.configure{|c| c.translations = map }
|
27
|
+
end
|
28
|
+
|
29
|
+
after do
|
30
|
+
map_cache = instance_variable_get("@prior_#{name}".to_sym)
|
31
|
+
Spigot.configure{|c| c.translations = map_cache }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
RSpec.configure do |config|
|
36
|
+
config.after(:each) do
|
37
|
+
ActiveUser.delete_all
|
38
|
+
Spigot.config.reset
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spigot::ActiveRecord do
|
4
|
+
let(:subject){ActiveUser}
|
5
|
+
let(:service){:github}
|
6
|
+
let(:data){ Spigot::ApiData.user.merge(id: '987') }
|
7
|
+
|
8
|
+
context 'with invalid mapping' do
|
9
|
+
with_mapping(:basic_active_user, Spigot::Mapping::ActiveUser.with_invalid_options)
|
10
|
+
|
11
|
+
context '#find_by_api' do
|
12
|
+
it 'requires the primary key to be accurate' do
|
13
|
+
expect {
|
14
|
+
subject.find_by_api(service, {full_name: 'Dean Martin'}).should_not be_nil
|
15
|
+
}.to raise_error(Spigot::InvalidSchemaError)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with valid mapping' do
|
21
|
+
with_mapping(:active_user_with_options, Spigot::Mapping::ActiveUser.with_options)
|
22
|
+
let(:user){ subject.create(name: 'Dean Martin', username: 'classyasfuck') }
|
23
|
+
|
24
|
+
context '#find_by_api' do
|
25
|
+
before{ user }
|
26
|
+
|
27
|
+
it 'queries by the specified primary_key' do
|
28
|
+
subject.find_by_api(service, data).should eq(user)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context '#find_all_by_api' do
|
33
|
+
with_mapping(:non_unique_key, Spigot::Mapping::ActiveUser.non_unique_key)
|
34
|
+
before do
|
35
|
+
user.update_attribute(:token, '123abc')
|
36
|
+
subject.create(name: 'Frank Sinatra', username: 'livetilidie', token: '123abc')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns all records matching primary key' do
|
40
|
+
subject.find_all_by_api(service, data).length.should eq(2)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context '#create_by_api' do
|
45
|
+
it 'creates a record' do
|
46
|
+
record = subject.create_by_api(service, data)
|
47
|
+
record.name.should eq('Dean Martin')
|
48
|
+
record.username.should eq('classyasfuck')
|
49
|
+
record.token.should be_nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context '#update_by_api' do
|
54
|
+
before{ user }
|
55
|
+
it 'updates a record' do
|
56
|
+
record = subject.update_by_api(service, data.merge(full_name: 'Dino Baby'))
|
57
|
+
record.name.should eq('Dino Baby')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context '#find_or_create_by_api' do
|
62
|
+
before{ user }
|
63
|
+
it 'returns an existing record' do
|
64
|
+
record = subject.find_or_create_by_api(service, data)
|
65
|
+
record.id.should eq(user.id)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'creates a record when none exists' do
|
69
|
+
record = subject.find_or_create_by_api(service, Spigot::ApiData.updated_user)
|
70
|
+
record.id.should_not eq(user.id)
|
71
|
+
record.name.should eq('Frank Sinatra')
|
72
|
+
record.username.should eq('livetilidie')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context '#create_or_update_by_api' do
|
77
|
+
before{ user }
|
78
|
+
|
79
|
+
it 'updates an existing record' do
|
80
|
+
record = subject.create_or_update_by_api(service, data.merge(full_name: 'Dino Baby'))
|
81
|
+
record.id.should eq(user.id)
|
82
|
+
record.name.should eq('Dino Baby')
|
83
|
+
record.username.should eq('classyasfuck')
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'creates a record when none exists' do
|
87
|
+
record = subject.create_or_update_by_api(service, Spigot::ApiData.updated_user)
|
88
|
+
record.id.should_not eq(user.id)
|
89
|
+
record.name.should eq('Frank Sinatra')
|
90
|
+
record.username.should eq('livetilidie')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spigot::Configuration do
|
4
|
+
before do
|
5
|
+
@prior_translations = Spigot.config.translations
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
Spigot.configure{|c| c.translations = @prior_translations }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'defaults' do
|
13
|
+
it 'is a hash of default configuration' do
|
14
|
+
expect(Spigot::Configuration.defaults).to be_kind_of(Hash)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'access' do
|
19
|
+
before do
|
20
|
+
@previous_path = Spigot.config.path
|
21
|
+
end
|
22
|
+
|
23
|
+
after do
|
24
|
+
Spigot.configure{|config| config.path = @previous_path }
|
25
|
+
end
|
26
|
+
|
27
|
+
it "is callable from .configure" do
|
28
|
+
Spigot.configure do |c|
|
29
|
+
expect(c).to be_kind_of(Spigot::Configuration)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'options' do
|
34
|
+
let(:path){'/baller'}
|
35
|
+
let(:map){{'user' => {a: 1}}}
|
36
|
+
let(:options_key){'my_special_key'}
|
37
|
+
|
38
|
+
it "is able to set the path" do
|
39
|
+
Spigot.configure{|config| config.path = path }
|
40
|
+
expect(Spigot.config.path).to eq(path)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "is able to set translations" do
|
44
|
+
Spigot.configure{|config| config.translations = map }
|
45
|
+
expect(Spigot.config.translations).to eq(map)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "is able to set the options_key" do
|
49
|
+
Spigot.configure{|config| config.options_key = options_key }
|
50
|
+
expect(Spigot.config.options_key).to eq(options_key)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "is able to set the logger" do
|
54
|
+
logger = Logger.new(STDOUT)
|
55
|
+
Spigot.configure{|config| config.logger = logger }
|
56
|
+
expect(Spigot.config.logger).to eq(logger)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Spigot::Translator do
|
4
|
+
|
5
|
+
context '#initialize' do
|
6
|
+
it 'requires a service' do
|
7
|
+
expect{
|
8
|
+
Spigot::Translator.new(nil, Struct)
|
9
|
+
}.to raise_error(Spigot::InvalidServiceError)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'requires a resource' do
|
13
|
+
expect{
|
14
|
+
Spigot::Translator.new(:github, nil)
|
15
|
+
}.to raise_error(Spigot::InvalidResourceError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'accepts an instance or class resource' do
|
19
|
+
by_class = Spigot::Translator.new(:github, User)
|
20
|
+
by_instance = Spigot::Translator.new(:github, User.new)
|
21
|
+
expect(by_class.resource).to eq(by_instance.resource)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context '#format' do
|
26
|
+
let(:subject){Spigot::Translator.new(:github, User.new, {a: '1'})}
|
27
|
+
|
28
|
+
context 'with a missing map' do
|
29
|
+
it 'raises error with a missing file' do
|
30
|
+
expect {
|
31
|
+
subject.format
|
32
|
+
}.to raise_error(Spigot::MissingServiceError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with a missing resource map' do
|
37
|
+
before do
|
38
|
+
subject.stubs(:translation_file).returns("missing:\n foo: 'bar'")
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'raises error with a missing resource map' do
|
42
|
+
expect{
|
43
|
+
subject.format
|
44
|
+
}.to raise_error(Spigot::MissingResourceError)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'and a valid resource map' do
|
49
|
+
with_mapping(:basic_user, Spigot::Mapping::User.basic)
|
50
|
+
|
51
|
+
it 'returns empty hash from nil data' do
|
52
|
+
expect(subject.format).to eq({})
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with basic user data' do
|
57
|
+
let(:subject){Spigot::Translator.new(:github, User.new, Spigot::ApiData.basic_user)}
|
58
|
+
|
59
|
+
context 'with a basic mapping' do
|
60
|
+
with_mapping(:basic_user, Spigot::Mapping::User.basic)
|
61
|
+
|
62
|
+
it 'reads one layer' do
|
63
|
+
expect(subject.format).to eq({'name' => 'Dean Martin', 'username' => 'classyasfuck'})
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'with a mapping containing several resources' do
|
68
|
+
with_mapping(:multiple_resources, Spigot::Mapping.multiple_resources)
|
69
|
+
|
70
|
+
it 'selects the user map' do
|
71
|
+
expect(subject.format).to eq({'name' => 'Dean Martin', 'username' => 'classyasfuck'})
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with a namedspaced resource' do
|
77
|
+
let(:subject){Spigot::Translator.new(:github, Wrapper::Post.new, Spigot::ApiData.post)}
|
78
|
+
with_mapping(:namespaced_post, Spigot::Mapping::Post.namespaced)
|
79
|
+
|
80
|
+
it 'accesses the wrapper/post key' do
|
81
|
+
expect(subject.format).to eq({'title' => 'Regular Article', 'description' => 'dolor sit amet'})
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context '#lookup' do
|
87
|
+
let(:subject){Spigot::Translator.new(:github, User.new, {a: '1'})}
|
88
|
+
|
89
|
+
it 'returns the value at a given key' do
|
90
|
+
subject.lookup(:a).should eq('1')
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context '#id' do
|
95
|
+
let(:subject){Spigot::Translator.new(:github, User.new, {id: '123'})}
|
96
|
+
|
97
|
+
it 'returns the value at the foreign_key' do
|
98
|
+
subject.stubs(:foreign_key).returns('id')
|
99
|
+
subject.id.should eq('123')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context '#conditions' do
|
104
|
+
let(:subject){Spigot::Translator.new(:github, User.new, Spigot::ApiData.user)}
|
105
|
+
with_mapping(:user_with_conditions, Spigot::Mapping::User.with_options)
|
106
|
+
|
107
|
+
it 'should return a hash' do
|
108
|
+
previous_translations = Spigot.config.translations
|
109
|
+
Spigot.configure{|c| c.translations = Spigot::Mapping::User.with_options }
|
110
|
+
subject.conditions.should eq({"username"=>"classyasfuck"})
|
111
|
+
Spigot.configure{|c| c.translations = previous_translations }
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'with conditions specified' do
|
115
|
+
with_mapping(:user_with_conditions, Spigot::Mapping::User.with_conditions)
|
116
|
+
|
117
|
+
it 'can specify the keys used in the map options' do
|
118
|
+
subject.conditions.should eq({"username"=>"classyasfuck", "name"=>"Dean Martin"})
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'can specify only one key' do
|
122
|
+
subject.conditions.should eq({"username"=>"classyasfuck", "name"=>"Dean Martin"})
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context '#options' do
|
128
|
+
let(:subject){Spigot::Translator.new(:github, User.new, {remote_id: '987'})}
|
129
|
+
|
130
|
+
context 'without options provided' do
|
131
|
+
with_mapping(:basic_user, Spigot::Mapping::User.basic)
|
132
|
+
|
133
|
+
context 'defaults' do
|
134
|
+
it '#primary_key is the name of the service _id' do
|
135
|
+
subject.primary_key.should eq('github_id')
|
136
|
+
end
|
137
|
+
|
138
|
+
it '#foreign_key is id' do
|
139
|
+
subject.foreign_key.should eq('id')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'with options provided' do
|
145
|
+
with_mapping(:user_with_options, Spigot::Mapping::User.with_options)
|
146
|
+
|
147
|
+
it 'reads the options from the spigot key' do
|
148
|
+
subject.options.should eq(Spigot::Mapping::User.with_options['user']['spigot'])
|
149
|
+
end
|
150
|
+
|
151
|
+
context '#primary_key' do
|
152
|
+
it 'reads a primary key from the mapping' do
|
153
|
+
subject.primary_key.should eq('username')
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context '#foreign_key' do
|
158
|
+
it 'reads a foreign key from the mapping' do
|
159
|
+
subject.foreign_key.should eq('login')
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
ActiveRecord::Base.establish_connection({
|
2
|
+
:adapter => "sqlite3",
|
3
|
+
:database => ':memory:'
|
4
|
+
})
|
5
|
+
|
6
|
+
ActiveRecord::Schema.define do
|
7
|
+
self.verbose = false
|
8
|
+
|
9
|
+
create_table :active_users, :force => true do |t|
|
10
|
+
t.string :name
|
11
|
+
t.integer :github_id
|
12
|
+
t.string :username
|
13
|
+
t.string :token
|
14
|
+
end
|
15
|
+
end
|