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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +25 -32
  4. data/Rakefile +6 -0
  5. data/examples/active_record.rb +18 -15
  6. data/examples/model.rb +38 -7
  7. data/lib/spigot.rb +18 -6
  8. data/lib/spigot/configuration.rb +4 -5
  9. data/lib/spigot/map/base.rb +44 -0
  10. data/lib/spigot/map/definition.rb +69 -0
  11. data/lib/spigot/map/option.rb +27 -0
  12. data/lib/spigot/map/resource.rb +37 -0
  13. data/lib/spigot/map/service.rb +43 -0
  14. data/lib/spigot/patch.rb +1 -1
  15. data/lib/spigot/proxy.rb +3 -2
  16. data/lib/spigot/translator.rb +22 -71
  17. data/lib/spigot/version.rb +1 -1
  18. data/script/console.rb +26 -20
  19. data/spec/fixtures/data/active_user.rb +17 -0
  20. data/spec/fixtures/data/post.rb +15 -0
  21. data/spec/fixtures/data/user.rb +41 -0
  22. data/spec/fixtures/mappings/active_user_map.rb +58 -22
  23. data/spec/fixtures/mappings/post_map.rb +10 -10
  24. data/spec/fixtures/mappings/user_map.rb +73 -29
  25. data/spec/spec_helper.rb +3 -12
  26. data/spec/spigot/active_record_spec.rb +34 -26
  27. data/spec/spigot/base_spec.rb +32 -1
  28. data/spec/spigot/configuration_spec.rb +0 -27
  29. data/spec/spigot/map/base_spec.rb +70 -0
  30. data/spec/spigot/map/definition_spec.rb +45 -0
  31. data/spec/spigot/map/resource_spec.rb +57 -0
  32. data/spec/spigot/map/service_spec.rb +88 -0
  33. data/spec/spigot/translator_spec.rb +110 -113
  34. data/spigot.gemspec +3 -2
  35. metadata +43 -20
  36. data/examples/.DS_Store +0 -0
  37. data/lib/.DS_Store +0 -0
  38. data/lib/spigot/config/.DS_Store +0 -0
  39. data/spec/.DS_Store +0 -0
  40. data/spec/fixtures/.DS_Store +0 -0
  41. data/spec/fixtures/api_data.rb +0 -46
  42. data/spec/spigot/factory_spec.rb +0 -5
@@ -0,0 +1,43 @@
1
+ module Spigot
2
+ module Map
3
+ class Service
4
+
5
+ attr_reader :name
6
+ attr_accessor :resources
7
+
8
+ def initialize(name)
9
+ @name = name.to_s.underscore.to_sym
10
+ @resources = []
11
+ end
12
+
13
+ def self.service(name, &block)
14
+ service = find(name) || Spigot::Map::Service.new(name)
15
+ service.instance_eval(&block) if block_given?
16
+ current_map.update(name, service)
17
+ end
18
+
19
+ def self.find(name)
20
+ current_map.service(name)
21
+ end
22
+
23
+ def resource(name, &block)
24
+ resources << Spigot::Map::Resource.new(name, &block)
25
+ end
26
+
27
+ def reset
28
+ @resources = []
29
+ end
30
+
31
+ def [](name)
32
+ resources.detect{|r| r.instance_variable_get(:@name).to_sym == name.to_sym}
33
+ end
34
+
35
+ private
36
+
37
+ def self.current_map
38
+ Spigot.config.map
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -1,6 +1,6 @@
1
1
  class String
2
2
  # Don't really like patching string here,
3
- # but it's too clean not to.
3
+ # but it's fine for now.
4
4
  def underscore
5
5
  self.gsub(/::/, '/').
6
6
  gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
@@ -14,6 +14,7 @@ module Spigot
14
14
  # @param service [String] This is the service that dictates the proxy.
15
15
  # @param resource [Object] This is the class implementing the proxy.
16
16
  def initialize(service, resource)
17
+ raise MissingServiceError, "No service definition found for #{service}" if Spigot.config.map.service(service).nil?
17
18
  @service = service
18
19
  @resource = resource
19
20
  end
@@ -27,13 +28,13 @@ module Spigot
27
28
  ## #map
28
29
  # Return a hash of the data map the current translator is using
29
30
  def map
30
- translator.mapping.reject{|k,v| k == 'spigot'}
31
+ translator.resource_map
31
32
  end
32
33
 
33
34
  ## #options
34
35
  # Return a hash of any service specific options for this translator. `Spigot.config` not included
35
36
  def options
36
- translator.mapping['spigot'] || {}
37
+ translator.options || {}
37
38
  end
38
39
 
39
40
  end
@@ -12,9 +12,7 @@ module Spigot
12
12
  # in the yaml file for that resource.
13
13
  #
14
14
  # Relevant Configuration:
15
- # config.options_key => The key which the Translator uses to configure a resource mapping.
16
- # config.path => The path which the Translator will look in to find the mappings.
17
- # config.translations => A hash that overrides any mappings found in the `path` directory.
15
+ # config.options_key => The key which the Translator uses to configure a resource_map.
18
16
 
19
17
  attr_reader :service, :resource
20
18
  attr_accessor :data
@@ -35,20 +33,10 @@ module Spigot
35
33
  @data = data || {}
36
34
  end
37
35
 
38
- ## #format(custom_map)
36
+ ## #format
39
37
  # Formats the hash of data passed in to the format specified in the yaml file.
40
- #
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)
51
- end
38
+ def format
39
+ @format ||= data.is_a?(Array) ? data.map{|el| parse(el) } : parse(data)
52
40
  end
53
41
 
54
42
  ## #id
@@ -78,81 +66,44 @@ module Spigot
78
66
  # Default: nil
79
67
  # Array of attributes included in the database query, these are names of columns in your database.
80
68
  def options
81
- @options ||= mapping[Spigot.config.options_key] || {}
69
+ @options ||= resource_map.instance_variable_get(:@options)
82
70
  end
83
71
 
84
72
  def primary_key
85
- options['primary_key'] || "#{service}_id"
73
+ options.primary_key || "#{service}_id"
86
74
  end
87
75
 
88
76
  def foreign_key
89
- options['foreign_key'] || mapping.invert[primary_key] || 'id'
77
+ options.foreign_key || 'id'
90
78
  end
91
79
 
92
80
  def conditions
93
- p_keys = [*(condition_keys.blank? ? primary_key : condition_keys)].map(&:to_s)
94
- keys = mapping.select{|k, v| p_keys.include?(v.to_s) }
95
- format(keys)
81
+ {primary_key => format[primary_key]}
96
82
  end
97
83
 
98
- ## #mapping
99
- # Return a hash of the data map currently being used by this translator, including options.
100
- def mapping
101
- return @mapping if defined?(@mapping)
102
- @mapping = translations[resource_key]
103
- raise MissingResourceError, "There is no #{resource_key} mapping for #{service}" if @mapping.nil?
104
- @mapping
84
+ ## #resource_map
85
+ # Return the mapped resource object for the current service and resource
86
+ def resource_map
87
+ return @resource_map if defined?(@resource_map)
88
+ resource_key = resource.to_s.underscore
89
+ @resource_map = service_map[resource_key]
90
+ raise MissingResourceError, "There is no #{resource_key} resource_map for #{service}" if @resource_map.nil?
91
+ @resource_map
105
92
  end
106
93
 
107
94
  private
108
95
 
109
- def translate(map, dataset)
96
+ def parse(dataset)
110
97
  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
98
+ resource_map.definitions.each do |rule|
99
+ result = rule.parse(dataset)
100
+ formatted.merge!(result)
123
101
  end
124
-
125
102
  formatted
126
103
  end
127
104
 
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
-
138
- def condition_keys
139
- options['conditions'].to_s.split(',').map(&:strip)
140
- end
141
-
142
- def resource_key
143
- resource.to_s.underscore
144
- end
145
-
146
- def translations
147
- @translations ||= Hashie::Mash.new(Spigot.config.translations || YAML.load(translation_file))
148
- end
149
-
150
- def translation_file
151
- begin
152
- @translation_file ||= File.read(File.join(Spigot.config.path, "#{service.to_s}.yml"))
153
- rescue Errno::ENOENT => e
154
- raise MissingServiceError, "There is no service map for #{service} defined"
155
- end
105
+ def service_map
106
+ Spigot.config.map ? Spigot.config.map.service(service) : {}
156
107
  end
157
108
 
158
109
  end
@@ -1,3 +1,3 @@
1
1
  module Spigot
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,6 +1,32 @@
1
1
  require 'active_record'
2
2
  require 'spigot'
3
3
 
4
+ Spigot.define do
5
+ service :github do
6
+ resource :active_user do
7
+ id :github_id
8
+ full_name :name
9
+ login :username
10
+ contact do
11
+ address :address
12
+ telephone do
13
+ work :work_phone
14
+ home :home_phone
15
+ end
16
+ url :homepage do |value|
17
+ "https://github.com/#{value}"
18
+ end
19
+ end
20
+ end
21
+
22
+ resource :pull_request do
23
+ id :id
24
+ title :title
25
+ body :body
26
+ end
27
+ end
28
+ end
29
+
4
30
  ActiveRecord::Base.logger = Spigot.logger
5
31
  require File.join(Spigot.root, 'spec', 'support', 'active_record')
6
32
 
@@ -8,24 +34,4 @@ class ActiveUser < ActiveRecord::Base
8
34
  include Spigot::Base
9
35
  end
10
36
 
11
- map = {'activeuser' => {
12
- 'name' => 'name',
13
- 'username' => 'login',
14
- 'spigot' => {
15
- 'primary_key' => 'username'
16
- }
17
- }}
18
-
19
- conditions = {'activeuser' => {
20
- 'name' => 'name',
21
- 'login' => 'username',
22
- 'spigot' => {
23
- 'primary_key' => 'username'
24
- }
25
- }}
26
-
27
- Spigot.configure do |config|
28
- config.translations = conditions
29
- end
30
-
31
37
  user = ActiveUser.create(name: 'Matt', username: 'mttwrnr', token: 'abc123')
@@ -0,0 +1,17 @@
1
+ module Spigot
2
+ module Data
3
+ class ActiveUser
4
+ class << self
5
+
6
+ def basic
7
+ {full_name: 'Dean Martin', login: 'classyasfuck', auth_token: '123abc'}
8
+ end
9
+
10
+ def alt
11
+ {full_name: 'Frank Sinatra', login: 'livetilidie', auth_token: '987zyx'}
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module Spigot
2
+ module Data
3
+ class Post
4
+ class << self
5
+ def basic
6
+ {'title' => 'Brief Article', 'body' => 'lorem ipsum'}
7
+ end
8
+
9
+ def alt
10
+ {'title' => 'Regular Article', 'body' => 'dolor sit amet', 'author' => 'Dean Martin'}
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ module Spigot
2
+ module Data
3
+ class User
4
+ class << self
5
+ def basic
6
+ {'id' => '123', 'full_name' => 'Dean Martin', 'login' => 'classyasfuck'}
7
+ end
8
+
9
+ def alt
10
+ {'full_name' => 'Frank Sinatra', 'login' => 'livetilidie', 'auth_token' => '456bcd'}
11
+ end
12
+
13
+ def full
14
+ basic.merge('auth_token' => '123abc')
15
+ end
16
+
17
+ def array
18
+ [full, alt]
19
+ end
20
+
21
+ def nested_array
22
+ {'account' => 'Rockafella', 'users' => array, 'count' => 2}
23
+ end
24
+
25
+ def nested
26
+ full.merge('login' => login_info)
27
+ end
28
+
29
+ def double_nested
30
+ full.merge('login' => {'contact' => login_info, 'last_seen_ip' => '127.0.0.1'})
31
+ end
32
+
33
+ private
34
+
35
+ def login_info
36
+ {'email' => 'dino@amore.io', 'user_name' => 'classyasfuck'}
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,42 +1,78 @@
1
1
  module Spigot
2
2
  module Mapping
3
-
4
3
  class ActiveUser
5
4
 
6
- def self.basic
7
- {'active_user' => base}
5
+ def self.stub
6
+ template do
7
+ login :username
8
+ full_name :name
9
+ end
8
10
  end
9
11
 
10
12
  def self.with_options
11
- {'active_user' => base.merge('spigot' => options)}
12
- end
13
-
14
- def self.non_unique_key
15
- {'active_user' => base.merge('auth_token' => 'token', 'spigot' => non_unique)}
13
+ template do
14
+ login :username
15
+ full_name :name
16
+ options do
17
+ primary_key :username
18
+ foreign_key :login
19
+ end
20
+ end
16
21
  end
17
22
 
18
- def self.with_invalid_options
19
- {'active_user' => base.merge('spigot' => invalid_options)}
23
+ def self.non_unique_keys
24
+ template do
25
+ login :username
26
+ full_name :name
27
+ auth_token :token
28
+ options do
29
+ primary_key :token
30
+ foreign_key :auth_token
31
+ end
32
+ end
20
33
  end
21
34
 
22
35
  private
23
36
 
24
- def self.base
25
- {'full_name' => 'name', 'login' => 'username'}
37
+ def self.template(&block)
38
+ Spigot.define do
39
+ service :github do
40
+ resource :active_user do
41
+ self.instance_eval(&block)
42
+ end
43
+ end
44
+ end
26
45
  end
27
46
 
28
- def self.options
29
- {'primary_key' => 'username', 'foreign_key' => 'login'}
30
- end
47
+ # def self.basic
48
+ # {'active_user' => base}
49
+ # end
31
50
 
32
- def self.non_unique
33
- {'primary_key' => 'token', 'foreign_key' => 'auth_token'}
34
- end
51
+ # def self.non_unique_key
52
+ # {'active_user' => base.merge('auth_token' => 'token', 'spigot' => non_unique)}
53
+ # end
35
54
 
36
- def self.invalid_options
37
- {'primary_key' => 'nosuchcolumn', 'foreign_key' => 'nosuchkey'}
38
- end
39
- end
55
+ # def self.with_invalid_options
56
+ # {'active_user' => base.merge('spigot' => invalid_options)}
57
+ # end
58
+
59
+ # private
60
+
61
+ # def self.base
62
+ # {'full_name' => 'name', 'login' => 'username'}
63
+ # end
40
64
 
65
+ # def self.options
66
+ # {'primary_key' => 'username', 'foreign_key' => 'login'}
67
+ # end
68
+
69
+ # def self.non_unique
70
+ # {'primary_key' => 'token', 'foreign_key' => 'auth_token'}
71
+ # end
72
+
73
+ # def self.invalid_options
74
+ # {'primary_key' => 'nosuchcolumn', 'foreign_key' => 'nosuchkey'}
75
+ # end
76
+ end
41
77
  end
42
78
  end