spigot 0.0.1 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a336365a8fdebfc3d1bd45fa11afea3c0a489ba6
4
- data.tar.gz: 30a5f6d8ce9c7fce3644233db8d35ff57cc45f17
3
+ metadata.gz: c6e0174fc401969ff57755d4eb751c1ebb3bd5c5
4
+ data.tar.gz: 2844483e9860825c20e4e6b6d255ffe80586843c
5
5
  SHA512:
6
- metadata.gz: b9df09433c59668681cfe6bd9ddb3c1c591b9e3d31c0aff96596477da1ca6144ecd94f1fcbed2b587a54e47bd6d2596cf9bfd6258020939d5229831c31711d96
7
- data.tar.gz: e725567b5260a2b0d9e358191cdb5dee627c6a36e844d49582de010d3f1795e8a6d2dcb8e304ece3e24962de3752aa9a78cf1d369a813f55d9faaed5e2b49a11
6
+ metadata.gz: 6edf56a3600624852a424f314faf8a1b092eb77a3e216e894964972a081a86348033f52f485540a0970a3c006683a91e7ebfcdba39a5ab9b6c489e08dd34d7ee
7
+ data.tar.gz: a88764f01212671cf27efdcf1d12218ceec4915b49c767c17b2b1b8560cabefc7ddca0e9ea7ab93686fe1d36f95d9f035cb46c793bd7077d0181eb57094cfc89
data/README.md CHANGED
@@ -1,45 +1,38 @@
1
1
  # Spigot
2
2
 
3
- Spigot provides a clean interface translating API data into context relevant objects
4
-
5
- ## Installation
6
-
7
- Add this line to your application's Gemfile:
8
-
9
- gem 'spigot'
10
-
11
- And then execute:
12
-
13
- $ bundle
14
-
15
- Or install it yourself as:
16
-
17
- $ gem install spigot
18
-
19
- ## Usage
3
+ Spigot is an attempt to bring some sanity to consuming external API data. Without Spigot, you need
4
+ to do this manual mapping at creation, such as:
5
+
6
+ Pull.where(number: pull.number).first_or_initialize.tap do |t|
7
+ t.title = pull.title
8
+ t.body = pull.body
9
+ t.url = pull._links.html.href
10
+ t.head_ref = pull.head.ref
11
+ t.head_sha = pull.head.sha
12
+ t.base_ref = pull.base.ref
13
+ t.base_sha = pull.base.sha
14
+ t.save
15
+ end
20
16
 
21
- You express the mapping between your model's data and the data received from an API in
22
- a yaml file. The mappings follow the structure of the data received. Any attribute you wish
23
- to retain, assign the name of your model's attribute to the name of the key received.
17
+ Spigot reads config files in an expected format to map the data you receive to the columns of your database.
18
+ This becomes particularly difficult as you start having multiple sources for the same resource (ex: users).
24
19
 
25
- Remember, the key is their attribute, the value is yours:
20
+ Spigot is able to do the translation for you and put the API data into a language your implementation understands.
21
+ This leaves you with a simple statement to accomplish the same as above:
26
22
 
27
- user:
28
- login: 'email'
29
- full_name: 'name'
23
+ Pull.find_or_create_by_api(:github, pull)
30
24
 
31
- Reads as: "For Users, their `login` is my `email` and their `full_name` is my `name`"
25
+ Much better. [Read More](https://github.com/mwerner/spigot/wiki)
32
26
 
33
27
  ## Example
34
28
 
35
29
  # Our Model
36
- class User
30
+ class User < ActiveRecord::Base
37
31
  include Spigot::Base
38
- attr_accessor :name, :email, :auth
39
32
  end
40
33
 
41
34
  # Api Data Received
42
- data = {"full_name":"Dean Martin","login":"dino@amore.io","token":"abc123"}
35
+ data = JSON.parse("{\"full_name\":\"Dean Martin\",\"login\":\"dino@amore.io\",\"token\":\"abc123\"}")
43
36
 
44
37
  # Spigot yaml file to map this data correctly
45
38
  user:
@@ -48,169 +41,22 @@ Reads as: "For Users, their `login` is my `email` and their `full_name` is my `n
48
41
  token: auth
49
42
 
50
43
  # Usage
51
- User.new_by_api(data).inspect
52
- #=> #<User:0x007ffa2918c7b8 @name="Dean Martin", @email="dino@amore.io", @auth="abc123">
53
-
54
- ## Map Format
55
-
56
- Each Spigot map file represents one service, with as many resources defined as you like.
57
-
58
- ##### Basic Map
59
-
60
- user:
61
- name: 'full_name'
62
- email: 'login'
63
- username: 'username'
64
-
65
- ##### Multiple Resources
66
-
67
- Spigot will look up the map for the name of the class implementing the method. This let's you map
68
- several of your resources that you're getting from the same service.
69
-
70
- # ./config/github.yml
71
- user:
72
- name: 'full_name'
73
- email: 'login'
74
- username: 'username'
75
-
76
- post:
77
- title: 'title'
78
- body: 'description'
79
- created_at: 'timestamp'
80
-
81
- ##### Options
82
-
83
- Spigot will look for an options key named `spigot` in the defined map.
84
- Those options let you configure additional options for that resource,
85
- such as denoting the identification in the data received.
86
-
87
- user:
88
- name: 'full_name'
89
- email: 'login'
90
- username: 'username'
91
- spigot:
92
- primary_key: 'service_id'
93
- foreign_key: 'id'
94
-
95
- ## ActiveRecord
96
-
97
- If you include Spigot on an ActiveRecord class, you get a few more methods.
98
-
99
- ##### `find_by_api`
100
-
101
- This uses the primary key defined in that resource's options to query the database
102
- with the value taken from the api data. (**Note** Does not update any values on the
103
- record received from the database.)
104
-
105
- # Spigot yaml file for the github service
106
- user:
107
- full_name: name
108
- login: email
109
- token: auth
110
- spigot:
111
- primary_key: email
112
-
113
- # API Data
114
- data = {"full_name":"Dean","login":"dino@amore.io","token":"bcd456"}
115
-
116
- #=> User.find_by_api(:github, data)
117
- User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."email" = 'dino@amore.io' ORDER BY "users"."id" ASC LIMIT 1
44
+ User.find_or_create_by_api(:github, data).inspect
118
45
  #=> #<User id: 1, name: "Dean Martin", email: "dino@amore.io", token: "abc123">
119
46
 
120
- ##### `find_all_by_api`
121
-
122
- Operates just like `find_by_api`, but returns all matches on the primary key. The method
123
- returns an `ActveRecord::Relation` allowing you to chain other constraints if you need.
124
-
125
- # Spigot yaml file for the github service
126
- user:
127
- full_name: name
128
- login: email
129
- token: auth
130
- spigot:
131
- primary_key: full_name
132
-
133
- # API Data
134
- data = {"full_name":"Dean","login":"dino@amore.io","token":"bcd456"}
135
-
136
- #=> User.find_all_by_api(:github, data)
137
- User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."name" = 'Dean'
138
- => #<ActiveRecord::Relation [#<User id: 1, name: "Dean", email: "dino@amore.io", token: "abc123">, #<User id: 2, name: "Dean", email: "minerals@notrocks.io", token: '92fnd'>]>
139
-
140
- ##### `create_by_api`
141
-
142
- Creates a record in your database using the provided API data, without doing
143
- any kind of query before, beyond your model's defined validations. Notice the
144
- creation does not use any of the API that isn't defined in the map.
145
-
146
- # Spigot yaml file for the github service
147
- user:
148
- full_name: name
149
- login: email
150
- token: auth
151
- spigot:
152
- primary_key: email
153
-
154
- # API Data
155
- data = {"full_name":"Frank Sinatra","login":"live@tilidie.io","id":"3"}
156
-
157
- #=> User.create_by_api(:github, data)
158
- SQL (0.1ms) INSERT INTO "users" ("name", "email") VALUES (?, ?) [["name", "Frank Sinatra"], ["email", "live@tilidie.io"]]
159
- => #<User id: 4, name: "Frank Sinatra", email: "live@tilidie.io", token: nil>
160
-
161
- ##### `update_by_api`
162
-
163
- Updates a record in your database. If no record matching the primary key is found, nothing happens.
164
-
165
- # Spigot yaml file for the github service
166
- user:
167
- full_name: name
168
- login: email
169
- token: auth
170
- spigot:
171
- primary_key: email
172
-
173
- # API Data
174
- data = {"full_name":"Dino Baby","login":"dean@amore.io","token":"bcd456"}
175
-
176
- #=> User.update_by_api(:github, data)
177
- User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."email" = 'livetilidie' ORDER BY "active_users"."id" ASC LIMIT 1
178
- SQL (0.1ms) UPDATE "users" SET "name" = ?, token = ? WHERE "users"."id" = 3 [["name", "Dino Baby"], ["token", "bcd456"]]
179
- => #<User id: 3, name: "Dino Baby", email: "dean@amore.io", token: "bcd456">
180
-
181
- ##### `find_or_create_by_api`
182
-
183
- Query the database to find an existing record. If none is found, create one with the provided API data.
184
-
185
- ##### `create_or_update_by_api`
186
-
187
- Query the database to find an existing record. If a record is found, update
188
- the record with the received API data. If no record is found, create one with
189
- the provided API data.
47
+ ## Installation
190
48
 
191
- ## Configuration
49
+ Add this line to your application's Gemfile:
192
50
 
193
- There are a handful of options that let you make spigot work the way you need.
51
+ gem 'spigot'
194
52
 
195
- logger:
196
- Specify a logger you would like Spigot to log to.
197
- type: Object
198
- default: Logger.new(STDOUT)
53
+ And then execute:
199
54
 
200
- options_key:
201
- The key which Spigot uses for configuring a resource map.
202
- type: String
203
- default: 'spigot'
55
+ $ bundle
204
56
 
205
- path:
206
- The directory which holds all the yaml files for the implemented services
207
- type: String
208
- default: 'config/spigot'
57
+ Or install it yourself as:
209
58
 
210
- translations:
211
- A map that, if present, overrides the resource maps found in the `path` directory
212
- type: Hash
213
- default: nil
59
+ $ gem install spigot
214
60
 
215
61
  ## Contributing
216
62
 
@@ -0,0 +1,11 @@
1
+ class String
2
+ # Don't really like patching string here,
3
+ # but it's too clean not to.
4
+ def underscore
5
+ self.gsub(/::/, '/').
6
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
7
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
8
+ tr("-", "_").
9
+ downcase
10
+ end
11
+ end
@@ -1,4 +1,5 @@
1
1
  require 'yaml'
2
+ require 'hashie'
2
3
 
3
4
  module Spigot
4
5
  class Translator
@@ -37,16 +38,17 @@ module Spigot
37
38
  ## #format(custom_map)
38
39
  # Formats the hash of data passed in to the format specified in the yaml file.
39
40
  #
40
- # @param custom_map [Hash] Optional hash that you can prefer to use over the correlated translation.
41
- def format(custom_map=nil)
42
- translations = custom_map || mapping
43
- formatted = {}
44
- data.each_pair do |key, val|
45
- next if key == Spigot.config.options_key
46
- attribute = translations[key.to_s]
47
- formatted.merge!(attribute.to_s => data[key]) unless attribute.nil?
41
+ # @param custom_map [Hash] Optional hash that you can prefer to use over the correlated translation.
42
+ # @param custom_data [Hash] Optional data that you can prefer to use over the @data currently on the object.
43
+ def format(custom_map=nil, custom_data=nil)
44
+ map = Hashie::Mash.new(custom_map || mapping)
45
+ dataset = custom_data || data
46
+
47
+ if dataset.is_a?(Array)
48
+ dataset.map{|n| translate(map, n) }
49
+ elsif dataset.is_a?(Hash)
50
+ translate(map, dataset)
48
51
  end
49
- formatted
50
52
  end
51
53
 
52
54
  ## #id
@@ -97,23 +99,52 @@ module Spigot
97
99
  # Return a hash of the data map currently being used by this translator, including options.
98
100
  def mapping
99
101
  return @mapping if defined?(@mapping)
100
- @mapping = translations[resource_key.to_s]
102
+ @mapping = translations[resource_key]
101
103
  raise MissingResourceError, "There is no #{resource_key} mapping for #{service}" if @mapping.nil?
102
104
  @mapping
103
105
  end
104
106
 
105
107
  private
106
108
 
109
+ def translate(map, dataset)
110
+ formatted = {}
111
+
112
+ if dataset.is_a?(Array)
113
+ return dataset.map{|element| translate(map, element)}
114
+ else
115
+ dataset.each_pair do |key, val|
116
+ next if key == Spigot.config.options_key
117
+ attribute = map[key]
118
+ next if attribute.nil?
119
+
120
+ result = attribute.is_a?(Hash) ? translate(attribute, val) : val
121
+ formatted.merge! mergeable_content(key, result, map)
122
+ end
123
+ end
124
+
125
+ formatted
126
+ end
127
+
128
+ def mergeable_content(key, value, map)
129
+ if value.is_a?(Array)
130
+ {key.to_s => value}
131
+ elsif value.is_a?(Hash)
132
+ value
133
+ else
134
+ {map[key] => value}
135
+ end
136
+ end
137
+
107
138
  def condition_keys
108
139
  options['conditions'].to_s.split(',').map(&:strip)
109
140
  end
110
141
 
111
142
  def resource_key
112
- resource.to_s.downcase.gsub('::', '/')
143
+ resource.to_s.underscore
113
144
  end
114
145
 
115
146
  def translations
116
- @translations ||= Spigot.config.translations || YAML.load(translation_file)
147
+ @translations ||= Hashie::Mash.new(Spigot.config.translations || YAML.load(translation_file))
117
148
  end
118
149
 
119
150
  def translation_file
@@ -1,3 +1,3 @@
1
1
  module Spigot
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/spigot.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "spigot/version"
2
2
  require "spigot/errors"
3
+ require "spigot/patch"
3
4
 
4
5
  module Spigot
5
6
  autoload :Configuration, 'spigot/configuration'
@@ -13,6 +13,27 @@ module Spigot
13
13
  {'full_name' => 'Frank Sinatra', 'login' => 'livetilidie', 'auth_token' => '456bcd'}
14
14
  end
15
15
 
16
+ def self.nested_user
17
+ {'full_name' => 'Dean Martin', 'login' => {
18
+ 'email' => 'dino@amore.io',
19
+ 'user_name' => 'classyasfuck'
20
+ }, 'auth_token' => '123abc'}
21
+ end
22
+
23
+ def self.double_nested_user
24
+ {'full_name' => 'Dean Martin', 'login' => {
25
+ 'contact' => {'work_email' => 'dino@amore.io', 'user_name' => 'classyasfuck' }
26
+ }, 'auth_token' => '123abc'}
27
+ end
28
+
29
+ def self.user_array
30
+ [{'full_name' => 'Dean Martin', 'login' => 'classyasfuck'}, {'full_name' => 'Frank Sinatra', 'login' => 'livetilidie'}]
31
+ end
32
+
33
+ def self.nested_user_array
34
+ {'account' => 'Rockafella', 'users' => user_array, 'count' => 2}
35
+ end
36
+
16
37
  def self.basic_post
17
38
  {'title' => 'Brief Article', 'body' => 'lorem ipsum'}
18
39
  end
@@ -4,19 +4,19 @@ module Spigot
4
4
  class ActiveUser
5
5
 
6
6
  def self.basic
7
- {'activeuser' => base}
7
+ {'active_user' => base}
8
8
  end
9
9
 
10
10
  def self.with_options
11
- {'activeuser' => base.merge('spigot' => options)}
11
+ {'active_user' => base.merge('spigot' => options)}
12
12
  end
13
13
 
14
14
  def self.non_unique_key
15
- {'activeuser' => base.merge('auth_token' => 'token', 'spigot' => non_unique)}
15
+ {'active_user' => base.merge('auth_token' => 'token', 'spigot' => non_unique)}
16
16
  end
17
17
 
18
18
  def self.with_invalid_options
19
- {'activeuser' => base.merge('spigot' => invalid_options)}
19
+ {'active_user' => base.merge('spigot' => invalid_options)}
20
20
  end
21
21
 
22
22
  private
@@ -6,6 +6,32 @@ module Spigot
6
6
  {'user' => base}
7
7
  end
8
8
 
9
+ def self.symbolized
10
+ {user: {full_name: 'name', login: 'username'}}
11
+ end
12
+
13
+ def self.nested
14
+ {'user' => base.merge('login' => {'email' => 'contact', 'user_name' => 'username'})}
15
+ end
16
+
17
+ def self.nested_twice
18
+ {'user' => base.merge('login' => {
19
+ 'contact' => {'work_email' => 'email', 'user_name' => 'username'}
20
+ })}
21
+ end
22
+
23
+ def self.array
24
+ {'user' => base}
25
+ end
26
+
27
+ def self.nested_array
28
+ {'user' => {'account' => 'name', 'count' => 'user_count', 'users' => base}}
29
+ end
30
+
31
+ def self.nested_account_members
32
+ {'activeuser' => {'account_name' => 'name', 'url' => 'url', 'members' => {'login' => 'email', 'full_name' => 'name'}}}
33
+ end
34
+
9
35
  def self.with_options
10
36
  {'user' => base.merge('spigot' => options)}
11
37
  end
@@ -45,6 +45,18 @@ describe Spigot::Translator do
45
45
  end
46
46
  end
47
47
 
48
+ context 'with a symbol keyed map' do
49
+ let(:subject){Spigot::Translator.new(:github, User.new, Spigot::ApiData.basic_user)}
50
+
51
+ context 'with a basic mapping' do
52
+ with_mapping(:basic_user, Spigot::Mapping::User.symbolized)
53
+
54
+ it 'reads one layer' do
55
+ expect(subject.format).to eq({'name' => 'Dean Martin', 'username' => 'classyasfuck'})
56
+ end
57
+ end
58
+ end
59
+
48
60
  context 'and a valid resource map' do
49
61
  with_mapping(:basic_user, Spigot::Mapping::User.basic)
50
62
 
@@ -64,12 +76,46 @@ describe Spigot::Translator do
64
76
  end
65
77
  end
66
78
 
67
- context 'with a mapping containing several resources' do
68
- with_mapping(:multiple_resources, Spigot::Mapping.multiple_resources)
79
+ context 'with a mapping containing nested data' do
80
+ let(:subject){Spigot::Translator.new(:github, User.new, Spigot::ApiData.nested_user)}
81
+ with_mapping(:basic_user, Spigot::Mapping::User.nested)
69
82
 
70
- it 'selects the user map' do
71
- expect(subject.format).to eq({'name' => 'Dean Martin', 'username' => 'classyasfuck'})
83
+ it 'traverses into the nested hash' do
84
+ expect(subject.format).to eq({
85
+ 'name' => 'Dean Martin',
86
+ 'username' => 'classyasfuck',
87
+ 'contact' => 'dino@amore.io'
88
+ })
72
89
  end
90
+
91
+ context 'twice' do
92
+ with_mapping(:basic_user, Spigot::Mapping::User.nested_twice)
93
+ let(:subject){Spigot::Translator.new(:github, User.new, Spigot::ApiData.double_nested_user)}
94
+
95
+ it 'traverses multiple levels' do
96
+ expect(subject.format.values).to include(*['Dean Martin','classyasfuck','dino@amore.io'])
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ context 'with an array of values' do
103
+ let(:subject){Spigot::Translator.new(:github, User.new, Spigot::ApiData.user_array)}
104
+ with_mapping(:basic_user, Spigot::Mapping::User.basic)
105
+
106
+ it 'returns an array of formatted data' do
107
+ expect(subject.format.length).to eq(2)
108
+ expect(subject.format.map{|u| u['name']}).to include('Dean Martin', 'Frank Sinatra')
109
+ end
110
+ end
111
+
112
+ context 'with a nested array of values' do
113
+ let(:subject){Spigot::Translator.new(:github, User.new, Spigot::ApiData.nested_user_array)}
114
+ with_mapping(:basic_user, Spigot::Mapping::User.nested_array)
115
+
116
+ it 'handles a nested array of values' do
117
+ expect(subject.format.keys).to include('name', 'users', 'user_count')
118
+ expect(subject.format['users']).to be_a(Array)
73
119
  end
74
120
  end
75
121
 
data/spigot.gemspec CHANGED
@@ -18,11 +18,11 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_dependency 'activerecord'
21
+ spec.add_dependency 'hashie'
21
22
 
22
23
  spec.add_development_dependency "bundler", "~> 1.3"
23
24
  spec.add_development_dependency "rake"
24
25
  spec.add_development_dependency "rspec"
25
26
  spec.add_development_dependency "mocha"
26
- spec.add_development_dependency 'hashie'
27
27
  spec.add_development_dependency 'sqlite3'
28
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spigot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Werner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-15 00:00:00.000000000 Z
11
+ date: 2013-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -25,35 +25,35 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: hashie
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - '>='
32
32
  - !ruby/object:Gem::Version
33
- version: '1.3'
34
- type: :development
33
+ version: '0'
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - '>='
39
39
  - !ruby/object:Gem::Version
40
- version: '1.3'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '1.3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '1.3'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec
56
+ name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - '>='
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: mocha
70
+ name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - '>='
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: hashie
84
+ name: mocha
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - '>='
@@ -132,6 +132,7 @@ files:
132
132
  - lib/spigot/config/spigot/github.yml
133
133
  - lib/spigot/configuration.rb
134
134
  - lib/spigot/errors.rb
135
+ - lib/spigot/patch.rb
135
136
  - lib/spigot/proxy.rb
136
137
  - lib/spigot/record.rb
137
138
  - lib/spigot/translator.rb