qiwi 0.1.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.
- data/.rspec +1 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +85 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +20 -0
- data/README.md +69 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/config/routes.rb +3 -0
- data/lib/qiwi.rb +11 -0
- data/lib/qiwi/IShopClientWS.wsdl +107 -0
- data/lib/qiwi/client.rb +106 -0
- data/lib/qiwi/config.rb +21 -0
- data/lib/qiwi/engine.rb +8 -0
- data/lib/qiwi/handler.rb +50 -0
- data/lib/qiwi/request.rb +144 -0
- data/lib/qiwi/response.rb +24 -0
- data/lib/qiwi/server.rb +78 -0
- data/lib/qiwi/transaction.rb +93 -0
- data/qiwi.gemspec +102 -0
- data/spec/client_spec.rb +107 -0
- data/spec/handler_spec.rb +38 -0
- data/spec/qiwi_spec.rb +4 -0
- data/spec/request_spec.rb +33 -0
- data/spec/server_spec.rb +83 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/transaction_spec.rb +41 -0
- metadata +285 -0
data/lib/qiwi/config.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Qiwi
|
4
|
+
|
5
|
+
class Config
|
6
|
+
attr_accessor :login, :password, :endpoint, :logger, :transaction_handler
|
7
|
+
|
8
|
+
def logger
|
9
|
+
@logger ||= Logger.new(STDERR)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.configure
|
14
|
+
yield config
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.config
|
18
|
+
@config ||= Config.new
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/qiwi/engine.rb
ADDED
data/lib/qiwi/handler.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'qiwi/transaction'
|
2
|
+
|
3
|
+
module Qiwi
|
4
|
+
class Handler
|
5
|
+
def self.call(txn, status)
|
6
|
+
new(txn, status).handle
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :txn, :status
|
10
|
+
def initialize(txn, status)
|
11
|
+
@txn = Transaction.new(txn)
|
12
|
+
@status = status
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle
|
16
|
+
check_transaction
|
17
|
+
end
|
18
|
+
|
19
|
+
def check_transaction
|
20
|
+
unless txn.exists?
|
21
|
+
logger.error "Transaction doesn't exist: #{txn.txn}"
|
22
|
+
return 210
|
23
|
+
end
|
24
|
+
|
25
|
+
if status != txn.remote_status
|
26
|
+
logger.error "Stati don't match: #{txn.status} vs. #{status}"
|
27
|
+
return 300
|
28
|
+
end
|
29
|
+
|
30
|
+
unless txn.valid_amount?
|
31
|
+
logger.error "Incorrect amount: #{txn.amount}"
|
32
|
+
return 241
|
33
|
+
end
|
34
|
+
|
35
|
+
if txn.valid?
|
36
|
+
return 0
|
37
|
+
else
|
38
|
+
logger.error "Unknown error: #{txn.inspect}"
|
39
|
+
return 300
|
40
|
+
end
|
41
|
+
|
42
|
+
ensure
|
43
|
+
txn.commit!
|
44
|
+
end
|
45
|
+
|
46
|
+
def logger
|
47
|
+
Qiwi.logger
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/qiwi/request.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'active_model/validations'
|
3
|
+
|
4
|
+
module Qiwi
|
5
|
+
module Request
|
6
|
+
|
7
|
+
class Base
|
8
|
+
include ActiveModel::Validations
|
9
|
+
|
10
|
+
def self.inherited(klass)
|
11
|
+
klass.attributes :login, :password
|
12
|
+
klass.validates_presence_of :login, :password
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.attributes(*attrs)
|
16
|
+
if attrs.empty?
|
17
|
+
@_attributes
|
18
|
+
else
|
19
|
+
@_attributes ||= []
|
20
|
+
@_attributes += attrs
|
21
|
+
attr_accessor(*attrs)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# See concrete classes for parameters description.
|
26
|
+
#
|
27
|
+
# @param [Qiwi::Client] client
|
28
|
+
# @param [Hash] params
|
29
|
+
def initialize(client, params)
|
30
|
+
self.class.attributes.each { |attr| send(:"#{attr}=", params[attr]) }
|
31
|
+
@login, @password = client.login, client.password
|
32
|
+
end
|
33
|
+
|
34
|
+
def body
|
35
|
+
with_envelope { |xml| soap_body(xml) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def with_envelope(&block)
|
39
|
+
Nokogiri::XML::Builder.new do |xml|
|
40
|
+
xml.Envelope("xmlns:soapenv" => "http://www.w3.org/2003/05/soap-envelope",
|
41
|
+
"xmlns:tns" => "http://server.ishop.mw.ru/") do
|
42
|
+
xml.parent.namespace = xml.parent.namespace_definitions.first
|
43
|
+
xml.Header
|
44
|
+
xml.Body(&block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def soap_body(xml)
|
50
|
+
xml['tns'].send(method) do
|
51
|
+
self.class.attributes.each do |attr|
|
52
|
+
# Underscore, so 'comment' is used as a parameter
|
53
|
+
# some ugly way to remove the namespace
|
54
|
+
xml.send("#{attr}_", send(attr)).instance_variable_get(:@node).namespace = nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# The SOAP method name
|
60
|
+
def method
|
61
|
+
@method ||= self.class.to_s.split('::').last.camelize(:lower)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Make sense of what is returned by the server
|
65
|
+
#
|
66
|
+
# @param [Nokogiri::XML::Document] xml
|
67
|
+
def result_from_xml(xml)
|
68
|
+
xml.xpath("//#{method}Response/#{method}Result").text.to_i
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class CreateBill < Base
|
73
|
+
attributes :user, :amount, :comment, :txn, :lifetime, :alarm, :create
|
74
|
+
|
75
|
+
validates_presence_of :user, :amount, :txn
|
76
|
+
validates_numericality_of :amount
|
77
|
+
validates_length_of :comment, :maximum => 255
|
78
|
+
validates_length_of :txn, :maximum => 30
|
79
|
+
validates_format_of :lifetime, :with => /^\d{2}\.\d{2}\.\d{4}\s\d{2}:\d{2}:\d{2}$/, :allow_nil => true
|
80
|
+
|
81
|
+
# @param [Hash] params
|
82
|
+
# @option params [String] :user e.g. a phone number
|
83
|
+
# @option params [Float] :amount
|
84
|
+
# @option params [String] :comment
|
85
|
+
# @option params [String] :txn unique bill identifier
|
86
|
+
# @option params [String, Time] :lifetime in "dd.MM.yyyy HH:mm:ss" format
|
87
|
+
# @option params [Fixnum] :alarm
|
88
|
+
# @option params [Boolean] :create
|
89
|
+
def initialize(client, params)
|
90
|
+
super
|
91
|
+
@alarm ||= 0
|
92
|
+
@create = true if @create.nil?
|
93
|
+
@lifetime = @lifetime.strftime('%d.%m.%Y %H:%M:%S') if @lifetime.respond_to?(:strftime)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class CancelBill < Base
|
98
|
+
attributes :txn
|
99
|
+
end
|
100
|
+
|
101
|
+
class CheckBill < Base
|
102
|
+
attributes :txn
|
103
|
+
|
104
|
+
# @example
|
105
|
+
# {:user=>"name", :amount=>1000.0, :date=>"07.09.2012 13:33", :status=>60}
|
106
|
+
def result_from_xml(xml)
|
107
|
+
el = xml.xpath("//checkBillResponse")
|
108
|
+
OpenStruct.new({
|
109
|
+
user: el.at('user').text,
|
110
|
+
amount: el.at('amount').text.to_f,
|
111
|
+
date: el.at('date').text,
|
112
|
+
lifetime: el.at('lifetime').text,
|
113
|
+
status: el.at('status').text.to_i
|
114
|
+
})
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class GetBillList < Base
|
119
|
+
attributes :dateFrom, :dateTo, :status
|
120
|
+
|
121
|
+
validates_presence_of :status
|
122
|
+
validates_numericality_of :status
|
123
|
+
|
124
|
+
# @param [Hash] params
|
125
|
+
# @option params [String] :user e.g. a phone number
|
126
|
+
# @option params [Time] :date_from
|
127
|
+
# @option params [Time] :date_to
|
128
|
+
# @option params [Fixnum] :status
|
129
|
+
def initialize(client, hash)
|
130
|
+
super
|
131
|
+
@dateFrom = hash[:date_from].strftime('%d.%m.%Y %H:%M:%S') if hash[:date_from]
|
132
|
+
@dateTo = hash[:date_to].strftime('%d.%m.%Y %H:%M:%S') if hash[:date_to]
|
133
|
+
end
|
134
|
+
|
135
|
+
def result_from_xml(xml)
|
136
|
+
el = xml.xpath("//getBillListResponse")
|
137
|
+
OpenStruct.new({
|
138
|
+
txns: el.at('txns').text,
|
139
|
+
count: el.at('count').text.to_i
|
140
|
+
})
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Qiwi
|
2
|
+
module Response
|
3
|
+
STATUS = {
|
4
|
+
50 => :set,
|
5
|
+
52 => :being_processed,
|
6
|
+
60 => :paid,
|
7
|
+
150 => :cancelled_by_terminal,
|
8
|
+
151 => :cancelled_no_auth,
|
9
|
+
160 => :cancelled,
|
10
|
+
161 => :cancelled_expired
|
11
|
+
}
|
12
|
+
STATUS.default_proc = lambda do |hash, status|
|
13
|
+
if status >= 51 and status <= 59
|
14
|
+
hash[52]
|
15
|
+
elsif status < 50
|
16
|
+
hash[50]
|
17
|
+
elsif status > 100
|
18
|
+
hash[160]
|
19
|
+
else
|
20
|
+
:unknown
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/qiwi/server.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'qiwi/handler'
|
4
|
+
|
5
|
+
module Qiwi
|
6
|
+
class Server
|
7
|
+
attr_accessor :handler
|
8
|
+
|
9
|
+
# Creates a new instance of Rack application
|
10
|
+
#
|
11
|
+
# @param [String] login
|
12
|
+
# @param [String] password
|
13
|
+
# @param [Proc] handler must return 0 on success
|
14
|
+
def initialize(login = nil, password = nil, handler = nil)
|
15
|
+
@login = login || Qiwi.config.login
|
16
|
+
@password = password || Qiwi.config.password
|
17
|
+
@handler = handler || Qiwi::Handler
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
@logger = env['rack.logger']
|
22
|
+
|
23
|
+
if env['QUERY_STRING'] == 'wsdl'
|
24
|
+
body = File.read(File.join(File.dirname(__FILE__), 'IShopClientWS.wsdl'))
|
25
|
+
else
|
26
|
+
body = env['rack.input'].read
|
27
|
+
result = handle_soap_body(body)
|
28
|
+
body = <<-EOF
|
29
|
+
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:cli="http://client.ishop.mw.ru/">
|
30
|
+
<soap:Body>
|
31
|
+
<cli:updateBillResponse>
|
32
|
+
<updateBillResult>#{result}</updateBillResult>
|
33
|
+
</cli:updateBillResponse>
|
34
|
+
</soap:Body>
|
35
|
+
</soap:Envelope>
|
36
|
+
EOF
|
37
|
+
end
|
38
|
+
headers = {'Content-Type' => 'application/soap+xml', 'Cache-Control' => 'no-cache'}
|
39
|
+
[200, headers, [body]]
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def handle_soap_body(body)
|
44
|
+
xml = Nokogiri::XML(body).remove_namespaces!
|
45
|
+
nodeset = xml.xpath('//updateBill')
|
46
|
+
return 300 if nodeset.empty?
|
47
|
+
|
48
|
+
params = %w[login password txn status].each_with_object({}) do |field, h|
|
49
|
+
h[field.to_sym] = nodeset.at(field).text
|
50
|
+
end
|
51
|
+
|
52
|
+
unless authorized?(params)
|
53
|
+
logger.info "Unauthorized: #{params.inspect}"
|
54
|
+
return 150
|
55
|
+
end
|
56
|
+
|
57
|
+
txn, status = params.values_at(:txn, :status)
|
58
|
+
handler.call(txn, status.to_i).tap do |res|
|
59
|
+
logger.info "Qiwi handler returned #{res}"
|
60
|
+
end
|
61
|
+
rescue => e
|
62
|
+
# Unknown error
|
63
|
+
logger.error "Error: #{e.message}\n#{e.backtrace.slice(0,2).join("\n")}"
|
64
|
+
return 300
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check if the password matches
|
68
|
+
def authorized?(params)
|
69
|
+
params[:login] == @login and
|
70
|
+
params[:password] == Digest::MD5.hexdigest(params[:txn] + Digest::MD5.hexdigest(@password).upcase).upcase
|
71
|
+
end
|
72
|
+
|
73
|
+
def logger
|
74
|
+
@logger ||= Logger.new(STDERR)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'qiwi/client'
|
2
|
+
require 'observer'
|
3
|
+
require 'active_model'
|
4
|
+
require 'active_support/core_ext/module/delegation'
|
5
|
+
|
6
|
+
module Qiwi
|
7
|
+
class Transaction
|
8
|
+
include Observable
|
9
|
+
include ActiveModel::Validations
|
10
|
+
|
11
|
+
class CorrectAmountValidator < ActiveModel::Validator
|
12
|
+
def validate(txn)
|
13
|
+
unless txn.remote_amount.to_i == txn.amount.to_i
|
14
|
+
txn.errors.add(:amount, :invalid)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
validates_presence_of :txn
|
20
|
+
validates_presence_of :persisted, :remote, :message => 'transaction not found'
|
21
|
+
validates :amount, :numericality => true, :correct_amount => true
|
22
|
+
|
23
|
+
delegate :amount, :status, :to => :remote, :prefix => true, :allow_nil => true
|
24
|
+
|
25
|
+
# Transaction id
|
26
|
+
attr_reader :txn
|
27
|
+
|
28
|
+
# Finder should respond_to?(:find_by_txn) and return an object,
|
29
|
+
# which can respond_to?(:amount)
|
30
|
+
attr_accessor :finder
|
31
|
+
|
32
|
+
def initialize(txn)
|
33
|
+
@txn = txn
|
34
|
+
|
35
|
+
# A logging observer
|
36
|
+
add_observer(self, :log_transaction)
|
37
|
+
|
38
|
+
if block_given?
|
39
|
+
yield self
|
40
|
+
else
|
41
|
+
Qiwi.config.transaction_handler.call(self) if Qiwi.config.transaction_handler
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
error_msgs = errors.full_messages.join(', ')
|
47
|
+
%{<Qiwi::Transaction id: #{txn}, remote: #{remote.inspect} persisted: #{persisted.inspect} errors: #{error_msgs}}
|
48
|
+
end
|
49
|
+
|
50
|
+
def commit!
|
51
|
+
changed
|
52
|
+
notify_observers(self)
|
53
|
+
end
|
54
|
+
|
55
|
+
def exists?
|
56
|
+
!!persisted
|
57
|
+
end
|
58
|
+
|
59
|
+
def valid_amount?
|
60
|
+
valid?
|
61
|
+
errors[:amount].empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
def amount
|
65
|
+
persisted.amount if exists?
|
66
|
+
end
|
67
|
+
|
68
|
+
def log_transaction(transaction)
|
69
|
+
Qiwi.logger.info "Transaction update: #{transaction.inspect}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def remote
|
73
|
+
@remote ||= Qiwi::Client.new.check_bill(txn: txn)
|
74
|
+
end
|
75
|
+
|
76
|
+
def persisted
|
77
|
+
@persisted ||= find(txn)
|
78
|
+
end
|
79
|
+
|
80
|
+
def find(txn)
|
81
|
+
finder.find_by_txn(txn) if finder
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# # Example:
|
88
|
+
# Qiwi.configure do |config|
|
89
|
+
# config.transaction_handler = lambda do |txn|
|
90
|
+
# txn.finder = PendingTransactions
|
91
|
+
# txn.add_observer(TransactionHandler.new)
|
92
|
+
# end
|
93
|
+
# end
|
data/qiwi.gemspec
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "qiwi"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Roman Shterenzon"]
|
12
|
+
s.date = "2012-10-31"
|
13
|
+
s.description = "Qiwi payments solution"
|
14
|
+
s.email = "romanbsd@yahoo.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".rspec",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"Guardfile",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"config/routes.rb",
|
29
|
+
"lib/qiwi.rb",
|
30
|
+
"lib/qiwi/IShopClientWS.wsdl",
|
31
|
+
"lib/qiwi/client.rb",
|
32
|
+
"lib/qiwi/config.rb",
|
33
|
+
"lib/qiwi/engine.rb",
|
34
|
+
"lib/qiwi/handler.rb",
|
35
|
+
"lib/qiwi/request.rb",
|
36
|
+
"lib/qiwi/response.rb",
|
37
|
+
"lib/qiwi/server.rb",
|
38
|
+
"lib/qiwi/transaction.rb",
|
39
|
+
"qiwi.gemspec",
|
40
|
+
"spec/client_spec.rb",
|
41
|
+
"spec/handler_spec.rb",
|
42
|
+
"spec/qiwi_spec.rb",
|
43
|
+
"spec/request_spec.rb",
|
44
|
+
"spec/server_spec.rb",
|
45
|
+
"spec/spec_helper.rb",
|
46
|
+
"spec/transaction_spec.rb"
|
47
|
+
]
|
48
|
+
s.homepage = "http://github.com/romanbsd/qiwi"
|
49
|
+
s.licenses = ["MIT"]
|
50
|
+
s.require_paths = ["lib"]
|
51
|
+
s.rubygems_version = "1.8.24"
|
52
|
+
s.summary = "Qiwi payments solution"
|
53
|
+
|
54
|
+
if s.respond_to? :specification_version then
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 0"])
|
59
|
+
s.add_runtime_dependency(%q<activemodel>, [">= 0"])
|
60
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
|
61
|
+
s.add_runtime_dependency(%q<faraday>, [">= 0"])
|
62
|
+
s.add_runtime_dependency(%q<rack>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<webmock>, [">= 0"])
|
64
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.11.0"])
|
65
|
+
s.add_development_dependency(%q<yard>, ["~> 0.7"])
|
66
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
67
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
68
|
+
s.add_development_dependency(%q<guard-rspec>, [">= 0"])
|
69
|
+
s.add_development_dependency(%q<rb-fsevent>, ["~> 0.9.1"])
|
70
|
+
s.add_development_dependency(%q<growl>, [">= 0"])
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
73
|
+
s.add_dependency(%q<activemodel>, [">= 0"])
|
74
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
75
|
+
s.add_dependency(%q<faraday>, [">= 0"])
|
76
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
77
|
+
s.add_dependency(%q<webmock>, [">= 0"])
|
78
|
+
s.add_dependency(%q<rspec>, ["~> 2.11.0"])
|
79
|
+
s.add_dependency(%q<yard>, ["~> 0.7"])
|
80
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
81
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
82
|
+
s.add_dependency(%q<guard-rspec>, [">= 0"])
|
83
|
+
s.add_dependency(%q<rb-fsevent>, ["~> 0.9.1"])
|
84
|
+
s.add_dependency(%q<growl>, [">= 0"])
|
85
|
+
end
|
86
|
+
else
|
87
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
88
|
+
s.add_dependency(%q<activemodel>, [">= 0"])
|
89
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
90
|
+
s.add_dependency(%q<faraday>, [">= 0"])
|
91
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
92
|
+
s.add_dependency(%q<webmock>, [">= 0"])
|
93
|
+
s.add_dependency(%q<rspec>, ["~> 2.11.0"])
|
94
|
+
s.add_dependency(%q<yard>, ["~> 0.7"])
|
95
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
96
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
97
|
+
s.add_dependency(%q<guard-rspec>, [">= 0"])
|
98
|
+
s.add_dependency(%q<rb-fsevent>, ["~> 0.9.1"])
|
99
|
+
s.add_dependency(%q<growl>, [">= 0"])
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|