rfsms 0.1.2
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.
- data/Rakefile +29 -0
- data/bin/autospec +16 -0
- data/bin/htmldiff +16 -0
- data/bin/ldiff +16 -0
- data/bin/nokogiri +16 -0
- data/bin/rackup +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/lib/rfsms.rb +307 -0
- data/spec/rfsms/answer_spec.rb +40 -0
- data/spec/rfsms/balance_answer_spec.rb +38 -0
- data/spec/rfsms/cancel_answer_spec.rb +52 -0
- data/spec/rfsms/connection_spec.rb +83 -0
- data/spec/rfsms/send_answer_spec.rb +95 -0
- data/spec/spec_helper.rb +5 -0
- metadata +93 -0
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#
|
2
|
+
# To change this template, choose Tools | Templates
|
3
|
+
# and open the template in the editor.
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rake'
|
7
|
+
require 'rake/clean'
|
8
|
+
#require 'rake/rdoctask'
|
9
|
+
#require 'rdoc/task'
|
10
|
+
require 'rake/testtask'
|
11
|
+
require 'rspec/core/rake_task'
|
12
|
+
|
13
|
+
#Rake::RDocTask.new do |rdoc|
|
14
|
+
# files =['README', 'LICENSE', 'lib/**/*.rb']
|
15
|
+
# rdoc.rdoc_files.add(files)
|
16
|
+
# rdoc.main = "README" # page to start on
|
17
|
+
# rdoc.title = "Rfsms Docs"
|
18
|
+
# rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
|
19
|
+
# rdoc.options << '--line-numbers'
|
20
|
+
#end
|
21
|
+
|
22
|
+
Rake::TestTask.new do |t|
|
23
|
+
t.test_files = FileList['test/**/*.rb']
|
24
|
+
end
|
25
|
+
|
26
|
+
RSpec::Core::RakeTask.new do |spec|
|
27
|
+
spec.pattern = 'spec/**/*.rb'
|
28
|
+
spec.rspec_opts = ['--backtrace']
|
29
|
+
end
|
data/bin/autospec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'autospec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rspec-core', 'autospec')
|
data/bin/htmldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'htmldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('diff-lcs', 'htmldiff')
|
data/bin/ldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'ldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('diff-lcs', 'ldiff')
|
data/bin/nokogiri
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'nokogiri' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('nokogiri', 'nokogiri')
|
data/bin/rackup
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rackup' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rack', 'rackup')
|
data/bin/rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rake' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rake', 'rake')
|
data/bin/rspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rspec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rspec-core', 'rspec')
|
data/lib/rfsms.rb
ADDED
@@ -0,0 +1,307 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
# Sender SMS via rfsms.ru
|
3
|
+
# v0.1.1
|
4
|
+
|
5
|
+
require 'net/http'
|
6
|
+
require 'net/https'
|
7
|
+
require 'nori'
|
8
|
+
require 'i18n'
|
9
|
+
require 'active_support/all'
|
10
|
+
|
11
|
+
Nori.configure do |config|
|
12
|
+
config.convert_tags_to { |tag| tag.to_s.underscore.to_sym }
|
13
|
+
end
|
14
|
+
|
15
|
+
module Rfsms
|
16
|
+
DATEREGEXP = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2]\d|3[01]) (?:[01]\d|2[0-4]):(?:[0-5]\d):(?:[0-5]\d)$/
|
17
|
+
DATEFORMAT = "%Y-%m-%d %H:%M:%S"
|
18
|
+
INTEGER_REGEXP = /^\d+$/
|
19
|
+
FLOAT_REGEXP = /^\d+(?:\.\d+)?$/
|
20
|
+
|
21
|
+
class AnswerError < RuntimeError; end
|
22
|
+
class IncorrectAnswerError < AnswerError; end
|
23
|
+
|
24
|
+
class Answer
|
25
|
+
attr_reader :descr
|
26
|
+
|
27
|
+
# Инициализация объекта ответа с проверкой элементов в передаваемом блоке validate.
|
28
|
+
# Если validate возвращает false или nil возникает исключение Rfsms::IncorrectAnswerError
|
29
|
+
def initialize(body, &validate)
|
30
|
+
body_e = Nori.parse(body, :nokogiri)
|
31
|
+
p body_e if $DEBUG
|
32
|
+
|
33
|
+
elements = body_e[:data]
|
34
|
+
raise IncorrectAnswerError unless elements.is_a?(Hash)
|
35
|
+
p elements if $DEBUG
|
36
|
+
|
37
|
+
if code = elements.delete(:code) and @descr = elements.delete(:descr)
|
38
|
+
if code == '1'
|
39
|
+
raise IncorrectAnswerError if validate and !validate.call(elements)
|
40
|
+
elements.each_pair do |tag, value|
|
41
|
+
instance_variable_set(:"@#{tag}", value)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise AnswerError, "#{code}: #{@descr}"
|
45
|
+
end
|
46
|
+
else
|
47
|
+
raise IncorrectAnswerError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class CancelAnswer < Answer
|
53
|
+
attr_reader :action, :cancel_col
|
54
|
+
|
55
|
+
def initialize(body)
|
56
|
+
super(body) do |e|
|
57
|
+
e[:action] =~ /^(?:check|make)$/
|
58
|
+
end
|
59
|
+
@cancel_col = @cancel_col.to_i
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class SendAnswer < Answer
|
64
|
+
attr_reader :smsid, :datetime, :action, :all_recivers,
|
65
|
+
:col_send_abonent, :col_non_send_abonent, :price_of_sending,
|
66
|
+
:colsms_of_sending, :price
|
67
|
+
|
68
|
+
def initialize(body)
|
69
|
+
super(body) do |e|
|
70
|
+
e.has_key?(:smsid) and
|
71
|
+
e[:datetime] =~ DATEREGEXP and
|
72
|
+
e[:action] =~ /^(?:check|make|send)$/ and
|
73
|
+
e[:all_recivers] =~ INTEGER_REGEXP and
|
74
|
+
e[:col_send_abonent] =~ INTEGER_REGEXP and
|
75
|
+
e[:col_non_send_abonent] =~ INTEGER_REGEXP and
|
76
|
+
e[:price_of_sending] =~ FLOAT_REGEXP and
|
77
|
+
e[:colsms_of_sending] =~ INTEGER_REGEXP and
|
78
|
+
e[:price] =~ FLOAT_REGEXP
|
79
|
+
end
|
80
|
+
@datetime = DateTime.strptime(@datetime, DATEFORMAT)
|
81
|
+
@all_recivers = @all_recivers.to_i
|
82
|
+
@col_send_abonent = @col_send_abonent.to_i
|
83
|
+
@col_non_send_abonent = @col_non_send_abonent.to_i
|
84
|
+
@price_of_sending = @price_of_sending.to_f
|
85
|
+
@colsms_of_sending = @colsms_of_sending.to_i
|
86
|
+
@price = @price.to_f
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class BalanceAnswer < Answer
|
91
|
+
attr_reader :account
|
92
|
+
|
93
|
+
def to_f
|
94
|
+
self.account
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
self.to_f.to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_str
|
102
|
+
self.to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
def +(other)
|
106
|
+
case other
|
107
|
+
when Float
|
108
|
+
self.to_f + other
|
109
|
+
when Integer, BalanceAnswer
|
110
|
+
self.to_f + other.to_f
|
111
|
+
else
|
112
|
+
n1, n2 = other.coerce(self)
|
113
|
+
n1 + n2
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def coerce(other)
|
118
|
+
case other
|
119
|
+
when Float
|
120
|
+
return other, self.to_f
|
121
|
+
when Integer
|
122
|
+
return other.to_f, self
|
123
|
+
when String
|
124
|
+
return other, self.to_s
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def initialize(body)
|
129
|
+
super(body) do |e|
|
130
|
+
e[:account] =~ FLOAT_REGEXP
|
131
|
+
end
|
132
|
+
@account = @account.to_f
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class ReportAnswer < Answer
|
137
|
+
attr_reader :sms
|
138
|
+
class SMS
|
139
|
+
attr_reader :smsid, :datetime, :text, :source, :all_col, :delivered_col,
|
140
|
+
:not_delivered_col, :waiting_col, :enqueued_col, :payment
|
141
|
+
|
142
|
+
def initialize(elements)
|
143
|
+
p elements if $DEBUG
|
144
|
+
unless @smsid = elements[:smsid] and
|
145
|
+
@datetime = elements[:datetime] and @datetime =~ DATEREGEXP and
|
146
|
+
@text = elements[:text] and
|
147
|
+
@source = elements[:source] and
|
148
|
+
@all_col = elements[:all_col] and
|
149
|
+
@delivered_col = elements[:delivered_col] and
|
150
|
+
@not_delivered_col = elements[:not_delivered_col] and
|
151
|
+
@waiting_col = elements[:waiting_col] and
|
152
|
+
@enqueued_col = elements[:enqueued_col] and
|
153
|
+
@payment = elements[:payment]
|
154
|
+
raise IncorrectAnswerError
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def initialize(body)
|
160
|
+
super(body)
|
161
|
+
@sms = case @sms
|
162
|
+
when Hash
|
163
|
+
[SMS.new(@sms)]
|
164
|
+
when Array
|
165
|
+
@sms.map {|sms| SMS.new(sms) }
|
166
|
+
else
|
167
|
+
[]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class Connection
|
173
|
+
def initialize(url, login, password)
|
174
|
+
@uri = URI.parse(url)
|
175
|
+
@connection = Net::HTTP.new(@uri.host, @uri.port)
|
176
|
+
@connection.use_ssl = true
|
177
|
+
@connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
178
|
+
@login = login
|
179
|
+
@password = password
|
180
|
+
end
|
181
|
+
|
182
|
+
# отправляет text по указанным phones = [].
|
183
|
+
#
|
184
|
+
# @param [String] text текст сообщения
|
185
|
+
# @param [Array<String, Hash>] phones телефоны на которые будет отправлено, может быть хэшем где значения - индивидуальный текст
|
186
|
+
# @param [Time] datetime время в которое необходимо отправить, nil - сейчас
|
187
|
+
# @param [String] source кто отправил
|
188
|
+
# @param [Boolean] onlydelivery)
|
189
|
+
# @return [Rfsms::SendAnswer]
|
190
|
+
def send(text, phones, datetime = nil, source = nil, onlydelivery = false)
|
191
|
+
message = message_header
|
192
|
+
message << "<text>#{text}</text>"
|
193
|
+
|
194
|
+
case phones
|
195
|
+
when Array
|
196
|
+
phones.each do |phone|
|
197
|
+
message << " <to number='#{phone}'></to>\n"
|
198
|
+
end
|
199
|
+
when Hash
|
200
|
+
phones.each_pair do |phone, t|
|
201
|
+
message << " <to number='#{phone}'>#{t}</to>\n"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
message << "<datetime>#{datetime.strftime(DATEFORMAT)}</datetime>" if datetime
|
206
|
+
message << "<source>#{source}</source>" if source
|
207
|
+
message << "<onlydelivery>1</onlydelivery>" if onlydelivery
|
208
|
+
message << message_footer
|
209
|
+
|
210
|
+
STDERR.puts "+SENDED:\n"+message+"\n-SENDED\n" if $DEBUG
|
211
|
+
@connection.request_post('/send.xml', message) do |response|
|
212
|
+
case response
|
213
|
+
when Net::HTTPSuccess
|
214
|
+
return SendAnswer.new(response.body)
|
215
|
+
else
|
216
|
+
response.error!
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# получает отчет о списке рассылки при указанных датах начала(start) и
|
222
|
+
# конца(stop) типа Time
|
223
|
+
# если даты не указаны получает за весь период
|
224
|
+
#
|
225
|
+
# @param [Hash{Symbol => Time}] options :start - начало, :stop - окончание
|
226
|
+
# @return [Rfsms::ReportAnswer]
|
227
|
+
def report(options = {})
|
228
|
+
message = message_header
|
229
|
+
unless options.empty?
|
230
|
+
start, stop = options[:start].strftime(DATEFORMAT), options[:stop].strftime(DATEFORMAT)
|
231
|
+
if start =~ DATEREGEXP and stop =~ DATEREGEXP
|
232
|
+
message << <<-PERIOD
|
233
|
+
<start>#{start}</start>
|
234
|
+
<stop>#{stop}</stop>
|
235
|
+
PERIOD
|
236
|
+
else
|
237
|
+
raise ArgumentError, "Date #{start} or date #{stop} is incorrect!"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
message << message_footer
|
241
|
+
|
242
|
+
STDERR.puts "+SENDED:\n"+message+"\n-SENDED\n" if $DEBUG
|
243
|
+
@connection.request_post('/report.xml', message) do |response|
|
244
|
+
case response
|
245
|
+
when Net::HTTPSuccess
|
246
|
+
return ReportAnswer.new(response.body)
|
247
|
+
else
|
248
|
+
response.error!
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# возвращает текущий баланс
|
254
|
+
#
|
255
|
+
# @return [Rfsms::BalanceAnwer]
|
256
|
+
def balance
|
257
|
+
message = message_header + message_footer
|
258
|
+
STDERR.puts "+SENDED:\n"+message+"\n-SENDED\n" if $DEBUG
|
259
|
+
@connection.request_post('/balance.xml', message) do |response|
|
260
|
+
case response
|
261
|
+
when Net::HTTPSuccess
|
262
|
+
return BalanceAnswer.new(response.body)
|
263
|
+
else
|
264
|
+
response.error!
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# отменяет СМС с идентификатором smsid
|
270
|
+
#
|
271
|
+
# @param [Integer, String] smsid
|
272
|
+
# @return [Rfsms::CancelAnswer]
|
273
|
+
def cancel(smsid)
|
274
|
+
message = message_header
|
275
|
+
message << <<END
|
276
|
+
<smsid>#{smsid}</smsid>
|
277
|
+
END
|
278
|
+
message << message_footer
|
279
|
+
STDERR.puts "+SENDED:\n"+message+"\n-SENDED\n" if $DEBUG
|
280
|
+
@connection.request_post('/cancel.xml', message) do |response|
|
281
|
+
case response
|
282
|
+
when Net::HTTPSuccess
|
283
|
+
return CancelAnswer.new(response.body)
|
284
|
+
else
|
285
|
+
response.error!
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
private
|
291
|
+
|
292
|
+
# формирует заголовок с данными авторизации
|
293
|
+
def message_header
|
294
|
+
<<END
|
295
|
+
<data>
|
296
|
+
<login>#{@login}</login>
|
297
|
+
<password>#{@password}</password>
|
298
|
+
END
|
299
|
+
end
|
300
|
+
|
301
|
+
# формирует подвал запроса
|
302
|
+
def message_footer
|
303
|
+
"</data>"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Rfsms::Answer do
|
5
|
+
before(:each) do
|
6
|
+
@correct_body = <<-ANSWER
|
7
|
+
<data>
|
8
|
+
<code>1</code>
|
9
|
+
<descr>Операция успешно завершена</descr>
|
10
|
+
</data>
|
11
|
+
ANSWER
|
12
|
+
@correct_elements = Nori.parse(@correct_body, :nokogiri)[:data]
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "конструктор" do
|
16
|
+
it "при корректных входных данных должен создавать экземпляр с соответствующими входу полями" do
|
17
|
+
answer = Rfsms::Answer.new(@correct_body)
|
18
|
+
answer.should be_an_instance_of(Rfsms::Answer)
|
19
|
+
answer.descr.should be == @correct_elements[:descr]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "при значении элемента code отличного от 1 должен вызывать исключение Rfsms::AnswerError" do
|
23
|
+
lambda do
|
24
|
+
Rfsms::Answer.new(@correct_body.gsub(%r{(?<=<code>)\d+(?=</code>)}, '500'))
|
25
|
+
end.should raise_error Rfsms::AnswerError
|
26
|
+
end
|
27
|
+
|
28
|
+
it "при отсутствии одного из входных элементов вызывает исключение Rfsms::IncorrectAnswerError" do
|
29
|
+
lambda do
|
30
|
+
incorrect_elements = @correct_body.gsub(%r{<code>.*</code>}, '')
|
31
|
+
Rfsms::Answer.new(incorrect_elements)
|
32
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
33
|
+
lambda do
|
34
|
+
incorrect_elements = @correct_body.gsub(%r{<descr>.*</descr>}, '')
|
35
|
+
Rfsms::Answer.new(incorrect_elements)
|
36
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Rfsms::BalanceAnswer do
|
5
|
+
before(:each) do
|
6
|
+
@correct_body = <<-ANSWER
|
7
|
+
<data>
|
8
|
+
<code>1</code>
|
9
|
+
<descr>Операция успешно завершена</descr>
|
10
|
+
<account>10000.94</account>
|
11
|
+
</data>
|
12
|
+
ANSWER
|
13
|
+
@correct_elements = Nori.parse(@correct_body, :nokogiri)[:data]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "при корректных входных данных конструктор создает объект с соответствующими входу полями" do
|
17
|
+
correct_balance_answer = Rfsms::BalanceAnswer.new(@correct_body)
|
18
|
+
correct_balance_answer.should be_an_instance_of(Rfsms::BalanceAnswer)
|
19
|
+
correct_balance_answer.descr.should be == @correct_elements[:descr]
|
20
|
+
correct_balance_answer.account.should be == @correct_elements[:account].to_f
|
21
|
+
end
|
22
|
+
|
23
|
+
it "при отсутствии одного из входных элементов конструктор вызывает исключение Rfsms::IncorrectAnswerError" do
|
24
|
+
lambda do
|
25
|
+
incorrect_elements = @correct_body.gsub(%r{<code>.*</code>}, '')
|
26
|
+
Rfsms::BalanceAnswer.new(incorrect_elements)
|
27
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
28
|
+
lambda do
|
29
|
+
incorrect_elements = @correct_body.gsub(%r{<descr>.*</descr>}, '')
|
30
|
+
Rfsms::BalanceAnswer.new(incorrect_elements)
|
31
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
32
|
+
lambda do
|
33
|
+
incorrect_elements = @correct_body.gsub(%r{<account>.*</account>}, '')
|
34
|
+
Rfsms::BalanceAnswer.new(incorrect_elements)
|
35
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Rfsms::CancelAnswer do
|
5
|
+
before(:each) do
|
6
|
+
@correct_body = <<-ANSWER
|
7
|
+
<data>
|
8
|
+
<code>1</code>
|
9
|
+
<descr>Операция завершена успешно</descr>
|
10
|
+
<action>make</action>
|
11
|
+
</data>
|
12
|
+
ANSWER
|
13
|
+
@correct_elements = Nori.parse(@correct_body, :nokogiri)[:data]
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "конструктор" do
|
17
|
+
it "при корректных входных данных создает экземпляр ответа с соответствующими входу полями" do
|
18
|
+
cancel_answer = Rfsms::CancelAnswer.new(@correct_body)
|
19
|
+
cancel_answer.should be_an_instance_of(Rfsms::CancelAnswer)
|
20
|
+
cancel_answer.descr.should be == @correct_elements[:descr]
|
21
|
+
cancel_answer.action.should be == @correct_elements[:action]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "при отсутствии одного из входных элементов вызывает исключение Rfsms::IncorrectAnswerError" do
|
25
|
+
lambda do
|
26
|
+
incorrect_elements = @correct_body.gsub(%r{<code>\d+</code>}, '')
|
27
|
+
Rfsms::CancelAnswer.new(incorrect_elements)
|
28
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
29
|
+
lambda do
|
30
|
+
incorrect_elements = @correct_body.gsub(%r{<descr>.+</descr>}, '')
|
31
|
+
Rfsms::CancelAnswer.new(incorrect_elements)
|
32
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
33
|
+
lambda do
|
34
|
+
incorrect_elements = @correct_body.gsub(%r{<action>.+</action>}, '')
|
35
|
+
Rfsms::CancelAnswer.new(incorrect_elements)
|
36
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "при значении входного элемента action отличного от make или check вызывает исключение Rfsms::IncorrectAnswerError" do
|
40
|
+
lambda do
|
41
|
+
Rfsms::CancelAnswer.new(@correct_body.gsub(%r{(?<=<action>).+(?=</action>)}, 'make'))
|
42
|
+
end.should_not raise_error(Rfsms::IncorrectAnswerError)
|
43
|
+
lambda do
|
44
|
+
Rfsms::CancelAnswer.new(@correct_body.gsub(%r{(?<=<action>).+(?=</action>)}, 'check'))
|
45
|
+
end.should_not raise_error(Rfsms::IncorrectAnswerError)
|
46
|
+
lambda do
|
47
|
+
Rfsms::CancelAnswer.new(@correct_body.gsub(%r{(?<=<action>).+(?=</action>)}, 'incorrect'))
|
48
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Rfsms::Connection do
|
5
|
+
before(:all) do
|
6
|
+
@connection = Rfsms::Connection.new('https://transport.rfsms.ru:7214', 'newcom', 'newc0w2m2')
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "Метод send(message, phones)" do
|
10
|
+
it "при отправке SMS по списку правильных номеров должен возвращать ответ (Rfsms::Answer)" do
|
11
|
+
correct_phones = [PHONE, '8-909-190-9409']
|
12
|
+
send_answer = nil
|
13
|
+
lambda { send_answer = @connection.send('test', correct_phones) }.should_not raise_error
|
14
|
+
send_answer.should be_an_instance_of(Rfsms::SendAnswer)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "должен вызывать исключение AnswerError, если все номера некорректны" do
|
18
|
+
incorrect_phones = ['8912383887', '8-909-190-94097']
|
19
|
+
lambda { @connection.send('test', incorrect_phones) }.should raise_error Rfsms::AnswerError
|
20
|
+
end
|
21
|
+
|
22
|
+
it "должен возвращать количество отправленных SMS соответствующее количеству корректных номеров" do
|
23
|
+
correct_phones = [PHONE, '8-909-190-9409']
|
24
|
+
incorrect_phones = ['8912383887', '8-909-190-94097']
|
25
|
+
answer = nil
|
26
|
+
lambda { answer = @connection.send('test', incorrect_phones.concat(correct_phones)) }.should_not raise_error
|
27
|
+
answer.col_send_abonent.should be == 2
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "Метод report(start, stop)" do
|
32
|
+
it "должен получать список рассылки за определенный период при корректно указанных датах начала и конца" do
|
33
|
+
start = Time.now - 2.hour
|
34
|
+
correct_phones = [PHONE]
|
35
|
+
@connection.send('test', correct_phones).should be_an_instance_of(Rfsms::SendAnswer)
|
36
|
+
stop = Time.now + 2.hour
|
37
|
+
|
38
|
+
report = @connection.report(:start => start, :stop => stop)
|
39
|
+
report.should be_an_instance_of(Rfsms::ReportAnswer)
|
40
|
+
report.sms.size.should be > 0
|
41
|
+
end
|
42
|
+
|
43
|
+
it "должен получать список рассылки за весь период при неуказанных дате начала и конца" do
|
44
|
+
# TODO: Исключить это дублирование
|
45
|
+
# start = Time.now
|
46
|
+
# correct_phones = ['89123838878']
|
47
|
+
# @connection.send('test', correct_phones)
|
48
|
+
# stop = Time.now
|
49
|
+
#
|
50
|
+
# @connection.send('test2', correct_phones)
|
51
|
+
#
|
52
|
+
# report_with_period = @connection.report(:start => start, :stop => stop)
|
53
|
+
# report_with_period.should be_an_instance_of(Rfsms::ReportAnswer)
|
54
|
+
#
|
55
|
+
# report = @connection.report
|
56
|
+
# report.should be_an_instance_of(Rfsms::ReportAnswer)
|
57
|
+
#
|
58
|
+
# report.sms.size.should be > report_with_period.sms.size
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "Метод balance()" do
|
63
|
+
it "должен возвращать текущее значение баланса типа Float в поле account созданного объекта" do
|
64
|
+
balance = nil
|
65
|
+
lambda { balance = @connection.balance }.should_not raise_error
|
66
|
+
balance.should be_an_instance_of(Rfsms::BalanceAnswer)
|
67
|
+
balance.account.should be_an_instance_of(Float)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "Метод cancel(smsid)" do
|
72
|
+
it "должен отменять отложенную SMS (группу SMS) с идентификатором smsid и возвращать Rfsms::CancelAnswer" do
|
73
|
+
send_answer = @connection.send(
|
74
|
+
"Проверка отмены рассылки",
|
75
|
+
[PHONE],
|
76
|
+
Time.now + 1.hour
|
77
|
+
)
|
78
|
+
canceled = @connection.cancel(send_answer.smsid)
|
79
|
+
canceled.should be_an_instance_of(Rfsms::CancelAnswer)
|
80
|
+
canceled.cancel_col.should be > 0
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Rfsms::SendAnswer do
|
5
|
+
before(:each) do
|
6
|
+
# Элементы ответа от сервера при отправке 2 правильных sms
|
7
|
+
@correct_answer_body_for_2sms = <<-ANSWER
|
8
|
+
<data>
|
9
|
+
<code>1</code>
|
10
|
+
<descr>Операция успешно завершена</descr>
|
11
|
+
<smsid>un1quine221d</smsid>
|
12
|
+
<datetime>#{Time.now.strftime(DATETIME_FORMAT)}</datetime>
|
13
|
+
<action>send</action>
|
14
|
+
<allRecivers>2</allRecivers>
|
15
|
+
<colSendAbonent>2</colSendAbonent>
|
16
|
+
<colNonSendAbonent>0</colNonSendAbonent>
|
17
|
+
<priceOfSending>0.76</priceOfSending>
|
18
|
+
<colsmsOfSending>2</colsmsOfSending>
|
19
|
+
<price>0.38</price>
|
20
|
+
</data>
|
21
|
+
ANSWER
|
22
|
+
@correct_answer_elements_for_2sms = Nori.parse(@correct_answer_body_for_2sms, :nokogiri)[:data]
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "конструктор" do
|
26
|
+
it "при корректных входных данных должен создавать экземпляр с соответствующими входу полями" do
|
27
|
+
send_answer = Rfsms::SendAnswer.new(@correct_answer_body_for_2sms)
|
28
|
+
send_answer.should be_an_instance_of(Rfsms::SendAnswer)
|
29
|
+
send_answer.descr.should be == @correct_answer_elements_for_2sms[:descr]
|
30
|
+
send_answer.smsid.should be == @correct_answer_elements_for_2sms[:smsid]
|
31
|
+
send_answer.datetime.should be == DateTime.strptime(@correct_answer_elements_for_2sms[:datetime], DATETIME_FORMAT)
|
32
|
+
send_answer.action.should be == @correct_answer_elements_for_2sms[:action]
|
33
|
+
send_answer.all_recivers.should be == @correct_answer_elements_for_2sms[:all_recivers].to_i
|
34
|
+
send_answer.col_send_abonent.should be == @correct_answer_elements_for_2sms[:col_send_abonent].to_i
|
35
|
+
send_answer.col_non_send_abonent.should be == @correct_answer_elements_for_2sms[:col_non_send_abonent].to_i
|
36
|
+
send_answer.price_of_sending.should be == @correct_answer_elements_for_2sms[:price_of_sending].to_f
|
37
|
+
send_answer.colsms_of_sending.should be == @correct_answer_elements_for_2sms[:colsms_of_sending].to_i
|
38
|
+
send_answer.price.should be == @correct_answer_elements_for_2sms[:price].to_f
|
39
|
+
end
|
40
|
+
|
41
|
+
it "при значении элемента code отличного от 1 должен вызывать исключение Rfsms::AnswerError" do
|
42
|
+
lambda do
|
43
|
+
Rfsms::SendAnswer.new(@correct_answer_body_for_2sms.gsub(%r{(?<=<code>).*(?=</code>)}, '500'))
|
44
|
+
end.should raise_error Rfsms::AnswerError
|
45
|
+
end
|
46
|
+
|
47
|
+
it "при отсутствии любого из входных данных должен вызывать исключение Rfsms::IncorrectAnswerError" do
|
48
|
+
lambda do
|
49
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<code>.*</code>}, '')
|
50
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
51
|
+
end.should raise_error(Rfsms::IncorrectAnswerError)
|
52
|
+
lambda do
|
53
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<descr>.*</descr>}, '')
|
54
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
55
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
56
|
+
lambda do
|
57
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<smsid>.*</smsid>}, '')
|
58
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
59
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
60
|
+
lambda do
|
61
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<datetime>.*</datetime>}, '')
|
62
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
63
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
64
|
+
lambda do
|
65
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<action>.*</action>}, '')
|
66
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
67
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
68
|
+
lambda do
|
69
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<allRecivers>.*</allRecivers>}, '')
|
70
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
71
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
72
|
+
lambda do
|
73
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<colSendAbonent>.*</colSendAbonent>}, '')
|
74
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
75
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
76
|
+
lambda do
|
77
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<colNonSendAbonent>.*</colNonSendAbonent>}, '')
|
78
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
79
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
80
|
+
lambda do
|
81
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<priceOfSending>.*</priceOfSending>}, '')
|
82
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
83
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
84
|
+
lambda do
|
85
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<colsmsOfSending>.*</colsmsOfSending>}, '')
|
86
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
87
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
88
|
+
lambda do
|
89
|
+
incorrect_elements = @correct_answer_body_for_2sms.gsub(%r{<price>.*</price>}, '')
|
90
|
+
Rfsms::SendAnswer.new(incorrect_elements)
|
91
|
+
end.should raise_error Rfsms::IncorrectAnswerError
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rfsms
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Danil Korotaev
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
requirement: &14005300 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.5.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *14005300
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: nori
|
27
|
+
requirement: &14001060 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.2
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *14001060
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: activesupport
|
38
|
+
requirement: &14013340 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 3.1.1
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *14013340
|
47
|
+
description: Sender SMS via rfsms.ru
|
48
|
+
email: greyd@mail333.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- Rakefile
|
54
|
+
- bin/rspec
|
55
|
+
- bin/rackup
|
56
|
+
- bin/autospec
|
57
|
+
- bin/rake
|
58
|
+
- bin/ldiff
|
59
|
+
- bin/htmldiff
|
60
|
+
- bin/nokogiri
|
61
|
+
- lib/rfsms.rb
|
62
|
+
- spec/rfsms/answer_spec.rb
|
63
|
+
- spec/rfsms/cancel_answer_spec.rb
|
64
|
+
- spec/rfsms/send_answer_spec.rb
|
65
|
+
- spec/rfsms/balance_answer_spec.rb
|
66
|
+
- spec/rfsms/connection_spec.rb
|
67
|
+
- spec/spec_helper.rb
|
68
|
+
homepage: http://rubygems.org/gems/rfsms
|
69
|
+
licenses: []
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.8.15
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: rfsms is sender SMS via rfsms.ru
|
92
|
+
test_files: []
|
93
|
+
has_rdoc:
|