spigot 0.1.0 → 0.2.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 +4 -4
- data/.gitignore +1 -0
- data/README.md +25 -32
- data/Rakefile +6 -0
- data/examples/active_record.rb +18 -15
- data/examples/model.rb +38 -7
- data/lib/spigot.rb +18 -6
- data/lib/spigot/configuration.rb +4 -5
- data/lib/spigot/map/base.rb +44 -0
- data/lib/spigot/map/definition.rb +69 -0
- data/lib/spigot/map/option.rb +27 -0
- data/lib/spigot/map/resource.rb +37 -0
- data/lib/spigot/map/service.rb +43 -0
- data/lib/spigot/patch.rb +1 -1
- data/lib/spigot/proxy.rb +3 -2
- data/lib/spigot/translator.rb +22 -71
- data/lib/spigot/version.rb +1 -1
- data/script/console.rb +26 -20
- data/spec/fixtures/data/active_user.rb +17 -0
- data/spec/fixtures/data/post.rb +15 -0
- data/spec/fixtures/data/user.rb +41 -0
- data/spec/fixtures/mappings/active_user_map.rb +58 -22
- data/spec/fixtures/mappings/post_map.rb +10 -10
- data/spec/fixtures/mappings/user_map.rb +73 -29
- data/spec/spec_helper.rb +3 -12
- data/spec/spigot/active_record_spec.rb +34 -26
- data/spec/spigot/base_spec.rb +32 -1
- data/spec/spigot/configuration_spec.rb +0 -27
- data/spec/spigot/map/base_spec.rb +70 -0
- data/spec/spigot/map/definition_spec.rb +45 -0
- data/spec/spigot/map/resource_spec.rb +57 -0
- data/spec/spigot/map/service_spec.rb +88 -0
- data/spec/spigot/translator_spec.rb +110 -113
- data/spigot.gemspec +3 -2
- metadata +43 -20
- data/examples/.DS_Store +0 -0
- data/lib/.DS_Store +0 -0
- data/lib/spigot/config/.DS_Store +0 -0
- data/spec/.DS_Store +0 -0
- data/spec/fixtures/.DS_Store +0 -0
- data/spec/fixtures/api_data.rb +0 -46
- data/spec/spigot/factory_spec.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78085ef374e7bfcf77dd307ab4d659152baddaf0
|
4
|
+
data.tar.gz: 8852de5e0d8a2e3ad3ea6d2ba467b61483617be3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b71f04cd9d4677e6a090cf59460b6ef0619b88cddb4f4fd9ce2f8f890b4e93ddf4288def1ccd15205675e0f4b924e85e78b3bbea25f767b68f17cc17ab69e84
|
7
|
+
data.tar.gz: 2acb389f90be427885ee81c1159d6925b32344d3bbd8e148aa18232052182e4bf28b59f8a48ed14c9b12af3c32be3912466cb7c109ea1c91bdf5c2d4ea4f3f10
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,48 +1,41 @@
|
|
1
1
|
# Spigot
|
2
2
|
|
3
|
+
[](https://travis-ci.org/mwerner/spigot)
|
4
|
+
[](https://codeclimate.com/github/mwerner/spigot)
|
5
|
+
|
3
6
|
Spigot is an attempt to bring some sanity to consuming external API data. Without Spigot, you need
|
4
7
|
to do this manual mapping at creation, such as:
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
16
|
-
|
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).
|
9
|
+
if params[:data].present?
|
10
|
+
data = params[:data]
|
11
|
+
record = User.where(external_id: data[:id]).first
|
19
12
|
|
20
|
-
|
21
|
-
|
13
|
+
if record.nil?
|
14
|
+
url = "https://github.com/#{data[:login]}"
|
22
15
|
|
23
|
-
|
16
|
+
user = User.new({
|
17
|
+
name: data[:first_name],
|
18
|
+
email: data[:email_address],
|
19
|
+
url: url
|
20
|
+
})
|
24
21
|
|
25
|
-
|
22
|
+
if data[:profile].present?
|
23
|
+
user.bio = data[:profile][:text]
|
24
|
+
end
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
# Our Model
|
30
|
-
class User < ActiveRecord::Base
|
31
|
-
include Spigot::Base
|
26
|
+
user.save!
|
27
|
+
end
|
32
28
|
end
|
33
29
|
|
34
|
-
|
35
|
-
|
30
|
+
This becomes particularly difficult as you start having multiple external sources for the same resource (ex: users from both twitter and facebook).
|
31
|
+
Spigot uses a ruby api to map the data you receive to the columns of your database. As a result, you're
|
32
|
+
able to convey a mapping of their structure into your attributes in a concise format. Afterwards, you can accomplish the above work in a simple statement:
|
33
|
+
|
34
|
+
User.find_or_create_by_api(:github, params[:data])
|
36
35
|
|
37
|
-
|
38
|
-
user:
|
39
|
-
full_name: name
|
40
|
-
login: email
|
41
|
-
token: auth
|
36
|
+
Much better.
|
42
37
|
|
43
|
-
|
44
|
-
User.find_or_create_by_api(:github, data).inspect
|
45
|
-
#=> #<User id: 1, name: "Dean Martin", email: "dino@amore.io", token: "abc123">
|
38
|
+
[Read More](http://mwerner.github.io/spigot/)
|
46
39
|
|
47
40
|
## Installation
|
48
41
|
|
data/Rakefile
CHANGED
data/examples/active_record.rb
CHANGED
@@ -23,28 +23,31 @@ class User < ActiveRecord::Base
|
|
23
23
|
include Spigot::Base
|
24
24
|
end
|
25
25
|
|
26
|
-
Spigot.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
Spigot.define do
|
27
|
+
service :github do
|
28
|
+
resource :user do
|
29
|
+
id :github_id
|
30
|
+
login :username
|
31
|
+
avatar_url :image_url
|
32
|
+
url :profile_url
|
33
|
+
options do
|
34
|
+
primary_key :username
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
36
38
|
end
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
profile_url = "https://api.github.com/users/mwerner"
|
41
|
+
puts "Making a request to an external API (Github): #{profile_url}"
|
42
|
+
response = Net::HTTP.get_response URI.parse(profile_url)
|
43
|
+
puts "Parse the response:\n `data = JSON.parse(response.body)`"
|
41
44
|
data = JSON.parse(response.body)
|
42
45
|
|
43
|
-
puts "\
|
46
|
+
puts "\nIt Returned a whole bunch of data: "
|
44
47
|
puts "#{data.inspect[0..100]}... etc, etc, etc (#{data.keys.length} more keys received)"
|
45
48
|
|
46
49
|
puts "\nWe don't want to use all of it. We can define a map on Spigot:"
|
47
|
-
puts User.spigot(:github).map.inspect
|
50
|
+
puts User.spigot(:github).map.to_hash.inspect
|
48
51
|
puts "Each key is an attribute received from the API, and the corresponding value is our column name."
|
49
52
|
|
50
53
|
puts "\nWe define our primary key in the spigot `User` options, so we know how to check if the record already exists:"
|
data/examples/model.rb
CHANGED
@@ -1,12 +1,39 @@
|
|
1
1
|
require 'spigot'
|
2
2
|
|
3
|
-
Spigot.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
Spigot.define do
|
4
|
+
service :twitter do
|
5
|
+
resource :user do
|
6
|
+
id :twitter_id
|
7
|
+
name :name
|
8
|
+
username :username
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Spigot.define do
|
14
|
+
service :github do
|
15
|
+
resource :user do
|
16
|
+
id :github_id
|
17
|
+
full_name :name
|
18
|
+
login :username
|
19
|
+
contact do
|
20
|
+
address :address
|
21
|
+
telephone do
|
22
|
+
work :work_phone
|
23
|
+
home :home_phone
|
24
|
+
end
|
25
|
+
url :homepage do |value|
|
26
|
+
"https://github.com/#{value}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
resource :pull_request do
|
32
|
+
id :id
|
33
|
+
title :title
|
34
|
+
body :body
|
35
|
+
end
|
36
|
+
end
|
10
37
|
end
|
11
38
|
|
12
39
|
class User
|
@@ -29,6 +56,10 @@ class User
|
|
29
56
|
end
|
30
57
|
end
|
31
58
|
|
59
|
+
puts "Map Built:"
|
60
|
+
puts Spigot.config.map.to_hash
|
61
|
+
|
32
62
|
user = User.build
|
63
|
+
puts "\nUser Parsed:"
|
33
64
|
puts user.name
|
34
65
|
puts user.inspect
|
data/lib/spigot.rb
CHANGED
@@ -3,21 +3,33 @@ require "spigot/errors"
|
|
3
3
|
require "spigot/patch"
|
4
4
|
|
5
5
|
module Spigot
|
6
|
-
autoload :Configuration, 'spigot/configuration'
|
7
|
-
autoload :Translator, 'spigot/translator'
|
8
|
-
autoload :Record, 'spigot/record'
|
9
|
-
autoload :Base, 'spigot/base'
|
10
6
|
autoload :ActiveRecord, 'spigot/active_record'
|
7
|
+
autoload :Base, 'spigot/base'
|
8
|
+
autoload :Configuration, 'spigot/configuration'
|
11
9
|
autoload :Proxy, 'spigot/proxy'
|
10
|
+
autoload :Record, 'spigot/record'
|
11
|
+
autoload :Translator, 'spigot/translator'
|
12
|
+
module Map
|
13
|
+
autoload :Base, 'spigot/map/base'
|
14
|
+
autoload :Definition, 'spigot/map/definition'
|
15
|
+
autoload :Option, 'spigot/map/option'
|
16
|
+
autoload :Resource, 'spigot/map/resource'
|
17
|
+
autoload :Service, 'spigot/map/service'
|
18
|
+
end
|
12
19
|
|
13
|
-
def self.
|
14
|
-
|
20
|
+
def self.define(&block)
|
21
|
+
(config.map || Spigot::Map::Base.new).define(&block)
|
15
22
|
end
|
16
23
|
|
17
24
|
def self.configure
|
18
25
|
yield config
|
19
26
|
end
|
20
27
|
|
28
|
+
def self.config
|
29
|
+
Configuration.instance
|
30
|
+
end
|
31
|
+
|
32
|
+
##=> Support
|
21
33
|
def self.root
|
22
34
|
File.expand_path('../..', __FILE__)
|
23
35
|
end
|
data/lib/spigot/configuration.rb
CHANGED
@@ -4,13 +4,12 @@ module Spigot
|
|
4
4
|
class Configuration
|
5
5
|
include Singleton
|
6
6
|
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :options_key, :logger, :map
|
8
8
|
|
9
9
|
@@defaults = {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
logger: nil
|
10
|
+
options_key: 'options',
|
11
|
+
logger: nil,
|
12
|
+
map: nil
|
14
13
|
}
|
15
14
|
|
16
15
|
def self.defaults
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Spigot
|
2
|
+
module Map
|
3
|
+
class Base
|
4
|
+
|
5
|
+
attr_reader :services
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@services = []
|
9
|
+
Spigot.config.map = self
|
10
|
+
end
|
11
|
+
|
12
|
+
def define(&block)
|
13
|
+
Spigot::Map::Service.class_eval(&block) if block_given?
|
14
|
+
end
|
15
|
+
|
16
|
+
def update(name, service)
|
17
|
+
@services.reject!{|s| s.name == name.to_s.underscore.to_sym}
|
18
|
+
@services << service
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset
|
22
|
+
@services = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def service(name)
|
26
|
+
services.detect{|service| service.name == name.to_s.underscore.to_sym}
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_hash
|
30
|
+
hash = {};
|
31
|
+
services.each do |service|
|
32
|
+
service_map = {}
|
33
|
+
service.resources.each{|resource| service_map.merge!(resource.to_hash) }
|
34
|
+
hash.merge!(service.name.to_sym => service_map)
|
35
|
+
end
|
36
|
+
hash
|
37
|
+
end
|
38
|
+
|
39
|
+
def inspect
|
40
|
+
"#<Spigot::Map::Base #{to_hash.to_s}>"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Spigot
|
2
|
+
module Map
|
3
|
+
class Definition
|
4
|
+
|
5
|
+
def initialize(name, args=nil, parent=nil, &block)
|
6
|
+
@name = name
|
7
|
+
@value = args
|
8
|
+
@children = []
|
9
|
+
self.instance_eval(&block) if block_given?
|
10
|
+
@parse = block unless @children.any?
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.define(resource, name, value=nil, &block)
|
14
|
+
definition = new(name, value, &block)
|
15
|
+
resource.append definition
|
16
|
+
definition
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse(data)
|
20
|
+
return {} if data.nil?
|
21
|
+
|
22
|
+
data.default_proc = proc{|h, k| h.key?(k.to_s) ? h[k.to_s] : nil} if data.is_a?(Hash)
|
23
|
+
if @children.empty?
|
24
|
+
value = @parse ? @parse.call(data[@name]) : data[@name]
|
25
|
+
return { @value.to_sym => value }
|
26
|
+
end
|
27
|
+
|
28
|
+
if data[@name].is_a?(Array)
|
29
|
+
set = []
|
30
|
+
data[@name].each do |element|
|
31
|
+
set << parse_children(element)
|
32
|
+
end
|
33
|
+
{ @name.to_sym => set }
|
34
|
+
else
|
35
|
+
parse_children(data[@name])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_hash
|
40
|
+
result = {}; value = nil
|
41
|
+
if @children.any?
|
42
|
+
value = {}
|
43
|
+
@children.each{|child| value.merge!(child.to_hash) }
|
44
|
+
else
|
45
|
+
value = @value
|
46
|
+
end
|
47
|
+
|
48
|
+
result.merge!({@name => value})
|
49
|
+
end
|
50
|
+
|
51
|
+
# Spigot::Map::Definition.new(:user){ username :login }
|
52
|
+
# Spigot::Map::Definition.new(:user){ username = :login }
|
53
|
+
def method_missing(name, *args, &block)
|
54
|
+
name = name.to_s.gsub('=','').to_sym
|
55
|
+
@children << Spigot::Map::Definition.new(name, *args, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def parse_children(data)
|
61
|
+
child_hash = {}
|
62
|
+
@children.each do |child|
|
63
|
+
child_hash.merge!(child.parse(data))
|
64
|
+
end
|
65
|
+
child_hash
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Spigot
|
2
|
+
module Map
|
3
|
+
class Option
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
@conditions = []
|
7
|
+
instance_eval(&block) if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def primary_key(key=nil)
|
11
|
+
return @primary_key if key.nil?
|
12
|
+
@primary_key = key
|
13
|
+
end
|
14
|
+
|
15
|
+
def foreign_key(key=nil)
|
16
|
+
return @foreign_key if key.nil?
|
17
|
+
@foreign_key = key
|
18
|
+
end
|
19
|
+
|
20
|
+
def conditions(attributes=nil)
|
21
|
+
return @attributes if attributes.nil?
|
22
|
+
@conditions = attributes
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Spigot
|
2
|
+
module Map
|
3
|
+
class Resource
|
4
|
+
|
5
|
+
attr_reader :definitions
|
6
|
+
|
7
|
+
def initialize(name, &block)
|
8
|
+
@name = name.to_s.underscore.to_sym
|
9
|
+
@definitions = []
|
10
|
+
@options = Spigot::Map::Option.new
|
11
|
+
self.instance_eval(&block) if block_given?
|
12
|
+
end
|
13
|
+
|
14
|
+
def append(definition)
|
15
|
+
@definitions << definition
|
16
|
+
end
|
17
|
+
|
18
|
+
def options(&block)
|
19
|
+
@options = Spigot::Map::Option.new(&block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
resource = {}
|
24
|
+
@definitions.each{|rule| resource.merge!(rule.to_hash) }
|
25
|
+
{@name => resource}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Spigot::Map::Resource.new(:user){ username :login }
|
29
|
+
# Spigot::Map::Resource.new(:user){ username = :login }
|
30
|
+
def method_missing(name, *args, &block)
|
31
|
+
name = name.to_s.gsub('=','').to_sym
|
32
|
+
Spigot::Map::Definition.define(self, name, *args, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|