stockboy 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|