stockboy 0.6.0 → 0.7.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/.rspec +0 -2
- data/.travis.yml +1 -0
- data/CHANGELOG.md +7 -0
- data/README.md +31 -4
- data/lib/stockboy/attribute_map.rb +18 -14
- data/lib/stockboy/candidate_record.rb +12 -13
- data/lib/stockboy/configurator.rb +76 -38
- data/lib/stockboy/filters.rb +9 -0
- data/lib/stockboy/job.rb +28 -5
- data/lib/stockboy/provider.rb +9 -25
- data/lib/stockboy/provider_repeater.rb +64 -0
- data/lib/stockboy/providers/file.rb +7 -6
- data/lib/stockboy/providers/ftp.rb +7 -6
- data/lib/stockboy/providers/http.rb +3 -2
- data/lib/stockboy/providers/imap.rb +7 -5
- data/lib/stockboy/providers/soap.rb +79 -3
- data/lib/stockboy/registry.rb +11 -0
- data/lib/stockboy/translator.rb +1 -0
- data/lib/stockboy/version.rb +1 -1
- data/spec/stockboy/attribute_map_spec.rb +13 -2
- data/spec/stockboy/configurator_spec.rb +21 -0
- data/spec/stockboy/job_spec.rb +44 -19
- data/spec/stockboy/provider_repeater_spec.rb +80 -0
- data/spec/stockboy/provider_spec.rb +22 -2
- data/spec/stockboy/providers/file_spec.rb +7 -7
- data/spec/stockboy/providers/ftp_spec.rb +3 -3
- data/spec/stockboy/providers/http_spec.rb +4 -2
- data/spec/stockboy/providers/imap_spec.rb +2 -2
- data/spec/stockboy/providers/soap_spec.rb +7 -2
- data/stockboy.gemspec +1 -1
- metadata +6 -3
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'stockboy/provider'
|
2
|
+
|
3
|
+
module Stockboy
|
4
|
+
class ProviderRepeater
|
5
|
+
|
6
|
+
YIELD_ONCE = proc { |output, provider| output << provider }
|
7
|
+
|
8
|
+
attr_reader :base_provider
|
9
|
+
|
10
|
+
def initialize(provider, &yielder)
|
11
|
+
@orig_provider = provider
|
12
|
+
@base_provider = provider.dup
|
13
|
+
@yielder = yielder || YIELD_ONCE
|
14
|
+
end
|
15
|
+
|
16
|
+
def data
|
17
|
+
# return base_provider.data unless block_given?
|
18
|
+
return nil unless block_given?
|
19
|
+
each do |nth_provider|
|
20
|
+
yield nth_provider.data
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear
|
25
|
+
@base_provider = @orig_provider.dup
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def each
|
30
|
+
return to_enum unless block_given?
|
31
|
+
enum = to_enum
|
32
|
+
while true
|
33
|
+
begin
|
34
|
+
provider = enum.next
|
35
|
+
unless provider.respond_to? :data
|
36
|
+
raise ArgumentError, "expected Provider, got #{provider.class}"
|
37
|
+
end
|
38
|
+
rescue StopIteration
|
39
|
+
return $!.result
|
40
|
+
end
|
41
|
+
y = yield provider
|
42
|
+
provider.clear
|
43
|
+
enum.feed y
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_enum
|
48
|
+
Enumerator.new do |y|
|
49
|
+
begin
|
50
|
+
@yielder.call(y, base_provider)
|
51
|
+
rescue LocalJumpError
|
52
|
+
raise $!, "use output << provider instead of yield", $!.backtrace
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def method_missing(method, *args, &block)
|
60
|
+
base_provider.public_send(method, *args, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -78,7 +78,7 @@ module Stockboy::Providers
|
|
78
78
|
private
|
79
79
|
|
80
80
|
def fetch_data
|
81
|
-
errors
|
81
|
+
errors << "file #{file_name} not found" unless matching_file
|
82
82
|
data_file = ::File.new(matching_file, 'r') if matching_file
|
83
83
|
validate_file(data_file)
|
84
84
|
if valid?
|
@@ -89,7 +89,8 @@ module Stockboy::Providers
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def validate
|
92
|
-
errors
|
92
|
+
errors << "file_dir must be specified" if file_dir.blank?
|
93
|
+
errors << "file_name must be specified" if file_name.blank?
|
93
94
|
errors.empty?
|
94
95
|
end
|
95
96
|
|
@@ -98,7 +99,7 @@ module Stockboy::Providers
|
|
98
99
|
end
|
99
100
|
|
100
101
|
def validate_file(data_file)
|
101
|
-
return errors
|
102
|
+
return errors << "no matching files" unless data_file
|
102
103
|
validate_file_newer(data_file)
|
103
104
|
validate_file_smaller(data_file)
|
104
105
|
validate_file_larger(data_file)
|
@@ -107,21 +108,21 @@ module Stockboy::Providers
|
|
107
108
|
def validate_file_newer(data_file)
|
108
109
|
@data_time ||= data_file.mtime
|
109
110
|
if file_newer && @data_time < file_newer
|
110
|
-
errors
|
111
|
+
errors << "no new files since #{file_newer}"
|
111
112
|
end
|
112
113
|
end
|
113
114
|
|
114
115
|
def validate_file_smaller(data_file)
|
115
116
|
@data_size ||= data_file.size
|
116
117
|
if file_smaller && @data_size > file_smaller
|
117
|
-
errors
|
118
|
+
errors << "file size larger than #{file_smaller}"
|
118
119
|
end
|
119
120
|
end
|
120
121
|
|
121
122
|
def validate_file_larger(data_file)
|
122
123
|
@data_size ||= data_file.size
|
123
124
|
if file_larger && @data_size < file_larger
|
124
|
-
errors
|
125
|
+
errors << "file size smaller than #{file_larger}"
|
125
126
|
end
|
126
127
|
end
|
127
128
|
end
|
@@ -114,7 +114,7 @@ module Stockboy::Providers
|
|
114
114
|
response
|
115
115
|
end
|
116
116
|
rescue Net::FTPError => e
|
117
|
-
errors
|
117
|
+
errors << e.message
|
118
118
|
logger.warn e.message
|
119
119
|
end
|
120
120
|
|
@@ -161,7 +161,8 @@ module Stockboy::Providers
|
|
161
161
|
end
|
162
162
|
|
163
163
|
def validate
|
164
|
-
errors
|
164
|
+
errors << "host must be specified" if host.blank?
|
165
|
+
errors << "file_name must be specified" if file_name.blank?
|
165
166
|
errors.empty?
|
166
167
|
end
|
167
168
|
|
@@ -175,7 +176,7 @@ module Stockboy::Providers
|
|
175
176
|
end
|
176
177
|
|
177
178
|
def validate_file(data_file)
|
178
|
-
return errors
|
179
|
+
return errors << "No matching files" unless data_file
|
179
180
|
validate_file_newer(data_file)
|
180
181
|
validate_file_smaller(data_file)
|
181
182
|
validate_file_larger(data_file)
|
@@ -184,21 +185,21 @@ module Stockboy::Providers
|
|
184
185
|
def validate_file_newer(data_file)
|
185
186
|
@data_time ||= client { |ftp| ftp.mtime(data_file) }
|
186
187
|
if file_newer and @data_time < file_newer
|
187
|
-
errors
|
188
|
+
errors << "No new files since #{file_newer}"
|
188
189
|
end
|
189
190
|
end
|
190
191
|
|
191
192
|
def validate_file_smaller(data_file)
|
192
193
|
@data_size ||= client { |ftp| ftp.size(data_file) }
|
193
194
|
if file_smaller and @data_size > file_smaller
|
194
|
-
errors
|
195
|
+
errors << "File size larger than #{file_smaller}"
|
195
196
|
end
|
196
197
|
end
|
197
198
|
|
198
199
|
def validate_file_larger(data_file)
|
199
200
|
@data_size ||= client { |ftp| ftp.size(data_file) }
|
200
201
|
if file_larger and @data_size < file_larger
|
201
|
-
errors
|
202
|
+
errors << "File size smaller than #{file_larger}"
|
202
203
|
end
|
203
204
|
end
|
204
205
|
end
|
@@ -132,7 +132,8 @@ module Stockboy::Providers
|
|
132
132
|
private
|
133
133
|
|
134
134
|
def validate
|
135
|
-
errors
|
135
|
+
errors << "uri must be specified" if uri.blank?
|
136
|
+
errors << "method (:get, :post) must be specified" if method.blank?
|
136
137
|
errors.empty?
|
137
138
|
end
|
138
139
|
|
@@ -142,7 +143,7 @@ module Stockboy::Providers
|
|
142
143
|
request.auth.basic(username, password) if username && password
|
143
144
|
response = HTTPI.send(method, request)
|
144
145
|
if response.error?
|
145
|
-
errors
|
146
|
+
errors << "HTTP response error: #{response.code}"
|
146
147
|
else
|
147
148
|
@data = response.body
|
148
149
|
end
|
@@ -162,7 +162,7 @@ module Stockboy::Providers
|
|
162
162
|
end
|
163
163
|
yield @open_client
|
164
164
|
rescue ::Net::IMAP::Error => e
|
165
|
-
errors
|
165
|
+
errors << "IMAP connection error"
|
166
166
|
ensure
|
167
167
|
if first_connection
|
168
168
|
@open_client.disconnect
|
@@ -253,7 +253,9 @@ module Stockboy::Providers
|
|
253
253
|
end
|
254
254
|
|
255
255
|
def validate
|
256
|
-
errors
|
256
|
+
errors << "host must be specified" if host.blank?
|
257
|
+
errors << "username must be specified" if username.blank?
|
258
|
+
errors << "password must be specified" if password.blank?
|
257
259
|
errors.empty?
|
258
260
|
end
|
259
261
|
|
@@ -281,7 +283,7 @@ module Stockboy::Providers
|
|
281
283
|
end
|
282
284
|
|
283
285
|
def validate_file(data_file)
|
284
|
-
return errors
|
286
|
+
return errors << "No matching attachments" unless data_file
|
285
287
|
validate_file_smaller(data_file)
|
286
288
|
validate_file_larger(data_file)
|
287
289
|
end
|
@@ -289,14 +291,14 @@ module Stockboy::Providers
|
|
289
291
|
def validate_file_smaller(data_file)
|
290
292
|
@data_size ||= data_file.bytesize
|
291
293
|
if file_smaller && @data_size > file_smaller
|
292
|
-
errors
|
294
|
+
errors << "File size larger than #{file_smaller}"
|
293
295
|
end
|
294
296
|
end
|
295
297
|
|
296
298
|
def validate_file_larger(data_file)
|
297
299
|
@data_size ||= data_file.bytesize
|
298
300
|
if file_larger && @data_size < file_larger
|
299
|
-
errors
|
301
|
+
errors << "File size smaller than #{file_larger}"
|
300
302
|
end
|
301
303
|
end
|
302
304
|
end
|
@@ -34,12 +34,24 @@ module Stockboy::Providers
|
|
34
34
|
#
|
35
35
|
dsl_attr :request
|
36
36
|
|
37
|
+
# @return [Symbol]
|
38
|
+
# @example
|
39
|
+
# env_namespace :soapenv
|
40
|
+
#
|
41
|
+
dsl_attr :env_namespace
|
42
|
+
|
37
43
|
# @return [String]
|
38
44
|
# @!attribute [rw] namespace
|
39
45
|
# Optional if specified in WSDL
|
40
46
|
#
|
41
47
|
dsl_attr :namespace
|
42
48
|
|
49
|
+
# @return [Hash]
|
50
|
+
# @!attribute [rw] namespaces
|
51
|
+
# Optional if specified in WSDL
|
52
|
+
#
|
53
|
+
dsl_attr :namespaces
|
54
|
+
|
43
55
|
# @return [String]
|
44
56
|
# @!attribute [rw] namespace_id
|
45
57
|
# Optional if specified in WSDL
|
@@ -62,6 +74,45 @@ module Stockboy::Providers
|
|
62
74
|
#
|
63
75
|
dsl_attr :message
|
64
76
|
|
77
|
+
# Message tag name
|
78
|
+
#
|
79
|
+
# @!attribute [rw] message_tag
|
80
|
+
# @return [String]
|
81
|
+
# @example
|
82
|
+
# message_tag "GetResult"
|
83
|
+
#
|
84
|
+
dsl_attr :message_tag
|
85
|
+
|
86
|
+
# @return [String]
|
87
|
+
# @example
|
88
|
+
# soap_action "urn:processMessage"
|
89
|
+
#
|
90
|
+
dsl_attr :soap_action
|
91
|
+
|
92
|
+
# XML string to override the Soap headers
|
93
|
+
#
|
94
|
+
# @!attribute [rw] soap_header
|
95
|
+
# @return [String]
|
96
|
+
# @example
|
97
|
+
# soap_header "<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">..."
|
98
|
+
#
|
99
|
+
dsl_attr :soap_header
|
100
|
+
|
101
|
+
# @return [Hash]
|
102
|
+
# @example
|
103
|
+
# attributes {}
|
104
|
+
#
|
105
|
+
dsl_attr :attributes
|
106
|
+
|
107
|
+
# Change the default response type, default :hash
|
108
|
+
#
|
109
|
+
# @!attribute [rw] response_format
|
110
|
+
# @return [Symbol]
|
111
|
+
# @example
|
112
|
+
# response_format :xml
|
113
|
+
#
|
114
|
+
dsl_attr :response_format
|
115
|
+
|
65
116
|
# Hash of optional HTTP request headers
|
66
117
|
#
|
67
118
|
# @!attribute [rw] headers
|
@@ -71,12 +122,22 @@ module Stockboy::Providers
|
|
71
122
|
#
|
72
123
|
dsl_attr :headers
|
73
124
|
|
125
|
+
# Array of WSSE Auth values
|
126
|
+
#
|
127
|
+
# @!attribute [rw] wsse_auth
|
128
|
+
# @return [Array]
|
129
|
+
# @example
|
130
|
+
# wsse_auth ["Username", "Password"]
|
131
|
+
#
|
132
|
+
dsl_attr :wsse_auth
|
133
|
+
|
74
134
|
# @!endgroup
|
75
135
|
|
76
136
|
# Initialize a new SOAP provider
|
77
137
|
#
|
78
138
|
def initialize(opts={}, &block)
|
79
139
|
super
|
140
|
+
@response_format = opts[:response_format] || :hash
|
80
141
|
DSL.new(self).instance_eval(&block) if block_given?
|
81
142
|
end
|
82
143
|
|
@@ -100,21 +161,36 @@ module Stockboy::Providers
|
|
100
161
|
end
|
101
162
|
opts[:convert_response_tags_to] = ->(tag) { string_pool(tag) }
|
102
163
|
opts[:namespace] = namespace if namespace
|
164
|
+
opts[:namespaces] = namespaces if namespaces
|
103
165
|
opts[:namespace_identifier] = namespace_id if namespace_id
|
166
|
+
opts[:env_namespace] = env_namespace if env_namespace
|
104
167
|
opts[:headers] = headers if headers
|
168
|
+
opts[:wsse_auth] = wsse_auth if wsse_auth
|
105
169
|
opts
|
106
170
|
end
|
107
171
|
|
108
172
|
def validate
|
109
|
-
errors
|
173
|
+
errors << "endpoint or wsdl must be specified" if endpoint.blank? unless wsdl
|
110
174
|
errors.blank?
|
111
175
|
end
|
112
176
|
|
113
177
|
def fetch_data
|
114
178
|
with_string_pool do
|
115
|
-
|
179
|
+
response = client.call(
|
180
|
+
@request,
|
181
|
+
message: message,
|
182
|
+
message_tag: message_tag,
|
183
|
+
soap_action: soap_action,
|
184
|
+
soap_header: soap_header,
|
185
|
+
attributes: attributes
|
186
|
+
)
|
187
|
+
@data = case response_format
|
188
|
+
when :xml
|
189
|
+
response.xml
|
190
|
+
else
|
191
|
+
response.body
|
192
|
+
end
|
116
193
|
end
|
117
194
|
end
|
118
|
-
|
119
195
|
end
|
120
196
|
end
|
data/lib/stockboy/registry.rb
CHANGED
data/lib/stockboy/translator.rb
CHANGED
data/lib/stockboy/version.rb
CHANGED
@@ -12,14 +12,15 @@ module Stockboy
|
|
12
12
|
end
|
13
13
|
|
14
14
|
describe ".new" do
|
15
|
+
let(:row) { Attribute.new(:email, "email", []) }
|
16
|
+
|
15
17
|
it "initializes from hash attribute" do
|
16
|
-
row = Attribute.new(:email, "email", [])
|
17
18
|
map = AttributeMap.new(:email => row)
|
18
19
|
map[:email].should == row
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
|
-
it "
|
23
|
+
it "sets same destination as default" do
|
23
24
|
subject[:email].should == Attribute.new(:email, "email", [])
|
24
25
|
end
|
25
26
|
|
@@ -55,5 +56,15 @@ module Stockboy
|
|
55
56
|
subject.map(&:to).should == [:email, :updated_at]
|
56
57
|
end
|
57
58
|
|
59
|
+
describe "#insert" do
|
60
|
+
subject(:map) { AttributeMap.new }
|
61
|
+
|
62
|
+
it "sets options from hash" do
|
63
|
+
upcase = ->(r) { r.upcase }
|
64
|
+
map.insert :test, from: "Test", as: upcase
|
65
|
+
map[:test].should == Attribute.new(:test, "Test", [upcase])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
58
69
|
end
|
59
70
|
end
|
@@ -72,6 +72,27 @@ module Stockboy
|
|
72
72
|
subject.attributes &proc{}
|
73
73
|
subject.config[:attributes].should be attribute_map
|
74
74
|
end
|
75
|
+
|
76
|
+
it "replaces existing attributes" do
|
77
|
+
subject.attribute :first_name
|
78
|
+
subject.attributes do last_name end
|
79
|
+
subject.config[:attributes][:first_name].should be_nil
|
80
|
+
subject.config[:attributes][:last_name].should be_an Attribute
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#attribute" do
|
85
|
+
it "inserts a single attribute" do
|
86
|
+
subject.attribute :test, from: "Test"
|
87
|
+
subject.config[:attributes][:test].should == Attribute.new(:test, "Test", [])
|
88
|
+
end
|
89
|
+
|
90
|
+
it "respects existing attributes added first" do
|
91
|
+
subject.attributes do first_name end
|
92
|
+
subject.attribute :last_name
|
93
|
+
subject.config[:attributes][:first_name].should be_an Attribute
|
94
|
+
subject.config[:attributes][:last_name].should be_an Attribute
|
95
|
+
end
|
75
96
|
end
|
76
97
|
|
77
98
|
describe "#on" do
|