butterware-kashflow 0.0.9
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/.document +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +58 -0
- data/LICENSE.txt +20 -0
- data/README.mkd +56 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/butterware-kashflow.gemspec +74 -0
- data/config/kf_api_methods.yml +1359 -0
- data/kashflow.gemspec +72 -0
- data/lib/butterware-kashflow.rb +1 -0
- data/lib/kashflow.rb +21 -0
- data/lib/kashflow/api.rb +64 -0
- data/lib/kashflow/api_method.rb +19 -0
- data/lib/kashflow/client.rb +133 -0
- data/test/helper.rb +18 -0
- data/test/test_kashflow.rb +7 -0
- metadata +180 -0
data/kashflow.gemspec
ADDED
@@ -0,0 +1,72 @@
|
|
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 = "kashflow"
|
8
|
+
s.version = "0.0.8"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Pogodan"]
|
12
|
+
s.date = "2012-03-30"
|
13
|
+
s.description = "A Ruby wrapper for the Kashflow Accouting SOAP API"
|
14
|
+
s.email = "dev@pogodan.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.mkd"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.mkd",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"config/kf_api_methods.yml",
|
28
|
+
"kashflow.gemspec",
|
29
|
+
"lib/kashflow.rb",
|
30
|
+
"lib/kashflow/api.rb",
|
31
|
+
"lib/kashflow/api_method.rb",
|
32
|
+
"lib/kashflow/client.rb",
|
33
|
+
"test/helper.rb",
|
34
|
+
"test/test_kashflow.rb"
|
35
|
+
]
|
36
|
+
s.homepage = "http://www.pogodan.com/projects"
|
37
|
+
s.licenses = ["MIT"]
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
s.rubygems_version = "1.8.19"
|
40
|
+
s.summary = "A Ruby wrapper for the Kashflow SOAP API"
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
s.specification_version = 3
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
46
|
+
s.add_runtime_dependency(%q<savon>, ["~> 0.9.9"])
|
47
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
50
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<i18n>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<activesupport>, [">= 0"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<savon>, ["~> 0.9.9"])
|
55
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
56
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
57
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
58
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
59
|
+
s.add_dependency(%q<i18n>, [">= 0"])
|
60
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
61
|
+
end
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<savon>, ["~> 0.9.9"])
|
64
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
65
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
66
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
67
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
68
|
+
s.add_dependency(%q<i18n>, [">= 0"])
|
69
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'kashflow'
|
data/lib/kashflow.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'savon'
|
3
|
+
|
4
|
+
require File.expand_path('../kashflow/client', __FILE__)
|
5
|
+
require File.expand_path('../kashflow/api_method', __FILE__)
|
6
|
+
require File.expand_path('../kashflow/api', __FILE__)
|
7
|
+
|
8
|
+
module Kashflow
|
9
|
+
def self.client(login, password)
|
10
|
+
@client ||= Kashflow::Client.new(login, password)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.method_missing(method, *args, &block)
|
14
|
+
return super unless client.respond_to?(method)
|
15
|
+
client.send(method, *args, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.root
|
19
|
+
File.dirname(File.expand_path('..', __FILE__))
|
20
|
+
end
|
21
|
+
end
|
data/lib/kashflow/api.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'active_support/core_ext/object'
|
4
|
+
require 'active_support/core_ext/hash'
|
5
|
+
|
6
|
+
## Kashflow.export_yaml will scrape the public API site for methods and
|
7
|
+
## dump them into the YAML config we use. we probably can/should just get this
|
8
|
+
## from the WSDL directly, but Savon wasn't working right, so this is the stopgap measure
|
9
|
+
module Kashflow
|
10
|
+
class Api
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def api_page(path)
|
14
|
+
cache_path = File.join(Kashflow.root, 'tmp', 'kf_api_pages', path)
|
15
|
+
|
16
|
+
unless File.exists?(cache_path)
|
17
|
+
File.open(cache_path, 'w'){ |f| f << open("http://accountingapi.com/#{path}").read }
|
18
|
+
end
|
19
|
+
|
20
|
+
Nokogiri::HTML(open(cache_path))
|
21
|
+
end
|
22
|
+
|
23
|
+
def apis_from_web
|
24
|
+
main_page = api_page('manual_class_customer.asp')
|
25
|
+
|
26
|
+
main_page.search('#mr_manual_inner a').select do |node|
|
27
|
+
node['href'] =~ /manual_methods/ and
|
28
|
+
node['href'] != 'manual_methods_overview.asp'
|
29
|
+
|
30
|
+
end.map{|n| new(n.content, n['href']) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def export_yaml
|
34
|
+
api_methods = apis_from_web.map { |a| ApiMethod.new(a.name, a.import) }
|
35
|
+
|
36
|
+
File.open(Client.yaml_path, 'w') do |f|
|
37
|
+
YAML.dump(api_methods, f)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :name, :page
|
44
|
+
def initialize(name, page)
|
45
|
+
@name = name
|
46
|
+
@page = page
|
47
|
+
end
|
48
|
+
|
49
|
+
def api_page
|
50
|
+
@api_page ||= self.class.api_page(page)
|
51
|
+
end
|
52
|
+
|
53
|
+
def import
|
54
|
+
@fields ||= api_page.search('.mnl_tbl1 tr').map do |row|
|
55
|
+
{
|
56
|
+
:name => row.search('span.mnl_varName').first.try(:content),
|
57
|
+
:type => row.search('span.mnl_varType').first.try(:content),
|
58
|
+
:direction => row.search('td strong').first.try(:content),
|
59
|
+
:desc => row.search('td').last.try(:content)
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Kashflow
|
2
|
+
class ApiMethod
|
3
|
+
attr_accessor :name, :request_attrs, :response_attrs
|
4
|
+
|
5
|
+
def initialize(name, fields)
|
6
|
+
@name = name
|
7
|
+
|
8
|
+
# split into request/response attrs
|
9
|
+
@request_attrs, @response_attrs = fields.partition{|f| f[:direction] == 'IN' }.map do |arr|
|
10
|
+
arr.map do |fields|
|
11
|
+
# get rid of the :direction and cleanup the description text
|
12
|
+
fields.slice!(:type, :desc, :name)
|
13
|
+
fields[:desc].try(:strip!)
|
14
|
+
fields
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module Kashflow
|
2
|
+
class Client
|
3
|
+
attr_reader :service
|
4
|
+
|
5
|
+
def self.yaml_path
|
6
|
+
File.join(Kashflow.root, 'config', 'kf_api_methods.yml')
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.api_methods
|
10
|
+
@@api_methods ||= YAML.load_file(yaml_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(login, password)
|
14
|
+
raise "missing login/password" unless login and password
|
15
|
+
|
16
|
+
@login, @password = login, password
|
17
|
+
@service = Savon::Client.new do |wsdl, http|
|
18
|
+
wsdl.document = "https://securedwebapp.com/api/service.asmx?wsdl"
|
19
|
+
http.auth.ssl.verify_mode = :none
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def lookup_api_method(name)
|
24
|
+
self.class.api_methods.detect{ |api_method| api_method.name.underscore == name.to_s }
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(m, *args, &block)
|
28
|
+
api_method = lookup_api_method(m)
|
29
|
+
|
30
|
+
if api_method
|
31
|
+
# puts "found api_method #{api_method.name} for #{m}: #{api_method.request_attrs.inspect}"
|
32
|
+
# puts "you're calling with #{args.inspect}"
|
33
|
+
|
34
|
+
api_call(m, api_method.name, args)
|
35
|
+
else
|
36
|
+
raise "method_missing: No method for #{m}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def api_call(name, method, args)
|
41
|
+
soap_return = soap_call(name, method, args)
|
42
|
+
response = soap_return["#{name}_response".to_sym]
|
43
|
+
# puts "got response: " + response.inspect
|
44
|
+
|
45
|
+
raise "API call failed: [#{response[:status_detail]}]\n\n #{response.inspect}" unless response[:status] == 'OK'
|
46
|
+
|
47
|
+
r = response["#{name}_result".to_sym]
|
48
|
+
if r.is_a?(String)
|
49
|
+
r
|
50
|
+
else
|
51
|
+
if r.is_a?(Enumerable)
|
52
|
+
if r.values.all?{|v| v.is_a?(Array) }# || r.keys.size == 1
|
53
|
+
object_type, attrs = r.first
|
54
|
+
else
|
55
|
+
# puts "arrayifying #{r.inspect}"
|
56
|
+
object_type = lookup_api_method(name).response_attrs.first[:type]
|
57
|
+
attrs = r.first.last.is_a?(Hash) ? [r.first.last] : [r]
|
58
|
+
end
|
59
|
+
|
60
|
+
# puts "it's an enumerable... #{object_type} | #{attrs.inspect}"
|
61
|
+
|
62
|
+
ostructs = attrs.map do |record_attrs|
|
63
|
+
# puts "making new ostruct with #{record_attrs.inspect}"
|
64
|
+
OpenStruct.new(record_attrs.merge(:object_type => object_type.to_s))
|
65
|
+
end
|
66
|
+
#r.first.last
|
67
|
+
else
|
68
|
+
#puts "it's a #{r.class}"
|
69
|
+
r
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def object_wrapper(name, params_xml)
|
75
|
+
object_alias = {:customer => "custr", :quote => "quote", :invoice => "Inv", :supplier => "supl", :receipt => "Inv", :line => "InvLine"}
|
76
|
+
needs_object = [ "insert", "update" ]
|
77
|
+
operation, object, line = name.to_s.split("_")
|
78
|
+
if needs_object.include? operation
|
79
|
+
text = object_alias[object.to_sym]
|
80
|
+
text = "sup" if operation == "update" and object == "supplier"
|
81
|
+
if line
|
82
|
+
text = object_alias[:line]
|
83
|
+
case name.to_s
|
84
|
+
when "insert_invoice_line_with_invoice_number"
|
85
|
+
line_id = "<InvoiceNumber>#{params_xml.match(/<InvoiceNumber>(.*?)<\/InvoiceNumber>/)[1]}</InvoiceNumber>\n\t\t"
|
86
|
+
else
|
87
|
+
line_id = "<ReceiptID>#{params_xml.match(/<ReceiptID>(.*?)<\/ReceiptID>/)[1]}</ReceiptID>\n\t\t" if object == "receipt"
|
88
|
+
line_id = "<InvoiceID>#{params_xml.match(/<InvoiceID>(.*?)<\/InvoiceID>/)[1]}</InvoiceID>\n\t\t" if object == "invoice"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
return ["#{line_id}<#{text}>", "</#{text}>"]
|
92
|
+
else
|
93
|
+
return ["",""]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# called with CamelCase version of method name
|
98
|
+
def soap_call(name, method, params = {})
|
99
|
+
# puts "name = #{name}, method = #{method}, params = #{params.inspect}"
|
100
|
+
begin
|
101
|
+
result = @service.request(name) do |soap|
|
102
|
+
# soap.action = "KashFlow/#{method}"
|
103
|
+
|
104
|
+
params = params.pop if params.is_a?(Array)
|
105
|
+
params_xml = params.map do |field, value|
|
106
|
+
xml_tag = field.to_s.camelize
|
107
|
+
"<#{xml_tag}>#{value}</#{xml_tag}>"
|
108
|
+
end.join("\n") unless params.blank?
|
109
|
+
|
110
|
+
params_xml = params_xml.gsub(/Id>/,"ID>") if params_xml
|
111
|
+
params_xml = params_xml.gsub(/Dbid>/,"DBID>") if params_xml
|
112
|
+
pretext, posttext = object_wrapper(name, params_xml)
|
113
|
+
|
114
|
+
soap.xml = %[<?xml version="1.0" encoding="utf-8"?>
|
115
|
+
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
116
|
+
<soap:Body>
|
117
|
+
<#{method} xmlns="KashFlow">
|
118
|
+
<UserName>#{@login}</UserName>
|
119
|
+
<Password>#{@password}</Password>
|
120
|
+
#{pretext}
|
121
|
+
#{params_xml}
|
122
|
+
#{posttext}
|
123
|
+
</#{method}>
|
124
|
+
</soap:Body>
|
125
|
+
</soap:Envelope>]
|
126
|
+
end.to_hash
|
127
|
+
rescue Savon::SOAP::Fault => e
|
128
|
+
puts "soap fault:" + e.inspect
|
129
|
+
return false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'kashflow'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: butterware-kashflow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.9
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Pogodan
|
9
|
+
- David Bourguignon
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-08-31 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: savon
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.9.9
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 0.9.9
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: nokogiri
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: shoulda
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: bundler
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: jeweler
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
type: :development
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: i18n
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: activesupport
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
description: A Ruby wrapper for the Kashflow Accouting SOAP API
|
128
|
+
email: david@butterware.co.uk
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files:
|
132
|
+
- LICENSE.txt
|
133
|
+
- README.mkd
|
134
|
+
files:
|
135
|
+
- .document
|
136
|
+
- Gemfile
|
137
|
+
- Gemfile.lock
|
138
|
+
- LICENSE.txt
|
139
|
+
- README.mkd
|
140
|
+
- Rakefile
|
141
|
+
- VERSION
|
142
|
+
- butterware-kashflow.gemspec
|
143
|
+
- config/kf_api_methods.yml
|
144
|
+
- kashflow.gemspec
|
145
|
+
- lib/butterware-kashflow.rb
|
146
|
+
- lib/kashflow.rb
|
147
|
+
- lib/kashflow/api.rb
|
148
|
+
- lib/kashflow/api_method.rb
|
149
|
+
- lib/kashflow/client.rb
|
150
|
+
- test/helper.rb
|
151
|
+
- test/test_kashflow.rb
|
152
|
+
homepage: https://github.com/butterware/kashflow
|
153
|
+
licenses:
|
154
|
+
- MIT
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
none: false
|
161
|
+
requirements:
|
162
|
+
- - ! '>='
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
segments:
|
166
|
+
- 0
|
167
|
+
hash: -2842399016050066755
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
requirements: []
|
175
|
+
rubyforge_project:
|
176
|
+
rubygems_version: 1.8.19
|
177
|
+
signing_key:
|
178
|
+
specification_version: 3
|
179
|
+
summary: A Ruby wrapper for the Kashflow SOAP API
|
180
|
+
test_files: []
|