fishbowl 0.0.1

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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@fishbowl --create
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - jruby-19mode # JRuby in 1.9 mode
5
+ - rbx-19mode
6
+ jdk:
7
+ - openjdk7
8
+ - oraclejdk7
9
+ - openjdk6
10
+ matrix:
11
+ exclude:
12
+ - rvm: 1.9.3
13
+ jdk: oraclejdk7
14
+ - rvm: 1.9.3
15
+ jdk: openjdk6
16
+ - rvm: rbx-19mode
17
+ jdk: oraclejdk7
18
+ - rvm: rbx-19mode
19
+ jdk: openjdk6
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'simplecov'
8
+
9
+ gem 'equivalent-xml'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 The Ready Project, LLC
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,37 @@
1
+ # Fishbowl
2
+
3
+ Provides an interface to the Fishbowl Inventory API.
4
+
5
+ [![Build Status](https://travis-ci.org/readyproject/fishbowl.png)](https://travis-ci.org/readyproject/fishbowl)
6
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/readyproject/fishbowl)
7
+ [![Dependency Status](https://gemnasium.com/readyproject/fishbowl.png)](https://gemnasium.com/readyproject/fishbowl)
8
+
9
+ ## Requirements
10
+
11
+ A Ruby 1.9 compatible interpreter
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'fishbowl'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install fishbowl
26
+
27
+ ## Usage
28
+
29
+ TODO: Write usage instructions here
30
+
31
+ ## Contributing
32
+
33
+ 1. Fork it
34
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
35
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
36
+ 4. Push to the branch (`git push origin my-new-feature`)
37
+ 5. Create new Pull Request
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Default: run specs.'
6
+ task :default => :spec
7
+
8
+ desc 'Run specs'
9
+ RSpec::Core::RakeTask.new
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fishbowl/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "fishbowl"
8
+ gem.version = Fishbowl::VERSION
9
+ gem.authors = ["James Thompson"]
10
+ gem.email = ["james@plainprograms.com"]
11
+ gem.description = %q{Provides an interface to the Fishbowl Inventory API.}
12
+ gem.summary = %q{Fishbowl Inventory API}
13
+ gem.homepage = "https://github.com/readyproject/fishbowl"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'nokogiri', '~> 1.5.5'
21
+ end
@@ -0,0 +1,110 @@
1
+ require 'socket'
2
+ require 'base64'
3
+ require 'singleton'
4
+
5
+ require 'nokogiri'
6
+
7
+ require 'fishbowl/version'
8
+ require 'fishbowl/errors'
9
+ require 'fishbowl/objects'
10
+
11
+ module Fishbowl # :nodoc:
12
+ class Connection
13
+ include Singleton
14
+
15
+ def self.connect(options = {})
16
+ raise Fishbowl::Errors::MissingHost if options[:host].nil?
17
+
18
+ @host = options[:host]
19
+ @port = options[:port].nil? ? 28192 : options[:port]
20
+
21
+ @connection = TCPSocket.new @host, @port
22
+
23
+ self.instance
24
+ end
25
+
26
+ def self.login(options = {})
27
+ raise Fishbowl::Errors::ConnectionNotEstablished if @connection.nil?
28
+ raise Fishbowl::Errors::MissingUsername if options[:username].nil?
29
+ raise Fishbowl::Errors::MissingPassword if options[:password].nil?
30
+
31
+ @username, @password = options[:username], options[:password]
32
+
33
+ Fishbowl::Objects::BaseObject.new.send_request(login_request, true)
34
+
35
+ code, message, _ = get_response('LoginRs')
36
+
37
+ Fishbowl::Errors.confirm_success_or_raise(code, message)
38
+
39
+ self.instance
40
+ end
41
+
42
+ def self.host
43
+ @host
44
+ end
45
+
46
+ def self.port
47
+ @port
48
+ end
49
+
50
+ def self.username
51
+ @username
52
+ end
53
+
54
+ def self.password
55
+ @password
56
+ end
57
+
58
+ def self.send(request)
59
+ write(request)
60
+ end
61
+
62
+ def self.close
63
+ @connection.close
64
+ @connection = nil
65
+ end
66
+
67
+ def self.get_response(expectation = 'FbiMsgRs')
68
+ response = read
69
+
70
+ status_code = response.xpath("//#{expectation}/@statusCode").first.value
71
+ status_message = response.xpath("//#{expectation}/@statusMessage").first.value
72
+
73
+ [status_code, status_message, response]
74
+ end
75
+
76
+ private
77
+
78
+ def self.login_request
79
+ Nokogiri::XML::Builder.new do |xml|
80
+ xml.request {
81
+ xml.LoginRq {
82
+ xml.IAID "fishbowl-ruby"
83
+ xml.IAName "Fishbowl Ruby Gem"
84
+ xml.IADescription "Fishbowl Ruby Gem"
85
+ xml.UserName @username
86
+ xml.UserPassword encoded_password
87
+ }
88
+ }
89
+ end
90
+ end
91
+
92
+ def self.encoded_password
93
+ Base64.encode64(@password)
94
+ end
95
+
96
+ def self.write(request)
97
+ body = request.to_xml
98
+ size = [body.size].pack("L>")
99
+
100
+ @connection.write(size)
101
+ @connection.write(body)
102
+ end
103
+
104
+ def self.read
105
+ length = @connection.recv(3).unpack('L>').join('').to_i
106
+ Nokogiri::XML.parse(@connection.recv(length))
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,12 @@
1
+ module Fishbowl::Errors
2
+ class ConnectionNotEstablished < RuntimeError; end;
3
+ class MissingHost < ArgumentError; end;
4
+ class MissingUsername < ArgumentError; end;
5
+ class MissingPassword < ArgumentError; end;
6
+
7
+ class StatusError < RuntimeError; end;
8
+
9
+ def self.confirm_success_or_raise(code, message)
10
+ code.to_i == 1000 ? true : raise(StatusError, message)
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ module Fishbowl
2
+ module Objects; end; # :nodoc:
3
+ end
4
+
5
+ require 'fishbowl/objects/base_object'
6
+ require 'fishbowl/objects/account'
@@ -0,0 +1,19 @@
1
+ module Fishbowl::Objects
2
+ class Account < BaseObject
3
+ def self.get_list
4
+ self.new.send_request("GetAccountListRq")
5
+ end
6
+
7
+ def self.get_balance(account)
8
+ builder = Nokogiri::XML::Builder.new do |xml|
9
+ xml.request {
10
+ xml.GetAccountBalanceRq {
11
+ xml.Account (account.is_a?(Account) ? account.name : account)
12
+ }
13
+ }
14
+ end
15
+
16
+ self.new.send_request(builder, true)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module Fishbowl::Objects
2
+ class BaseObject
3
+ attr_accessor :ticket
4
+
5
+ def send_request(request, fragment = false)
6
+ Fishbowl::Connection.send(build_request(request, fragment))
7
+ end
8
+
9
+ private
10
+
11
+ def build_request(request, fragment = false)
12
+ Nokogiri::XML::Builder.new do |xml|
13
+ xml.FbiXml {
14
+ if @ticket.nil?
15
+ xml.Ticket
16
+ else
17
+ xml.Ticket @ticket
18
+ end
19
+
20
+ xml.FbiMsgsRq {
21
+ xml << request.doc.xpath("request/*").to_xml if fragment
22
+ xml.send(request.to_s) unless fragment
23
+ }
24
+ }
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Fishbowl # :nodoc:
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fishbowl::Connection do
4
+ before :each do
5
+ mock_tcp_connection
6
+ end
7
+
8
+ after :each do
9
+ unmock_tcp
10
+ end
11
+
12
+ describe '.connect' do
13
+ it 'should require a host' do
14
+ lambda {
15
+ Fishbowl::Connection.connect
16
+ }.should raise_error(Fishbowl::Errors::MissingHost)
17
+ end
18
+
19
+ it 'should default to port 28192' do
20
+ Fishbowl::Connection.connect(host: 'localhost')
21
+ Fishbowl::Connection.port.should eq(28192)
22
+ end
23
+ end
24
+
25
+ describe '.login' do
26
+ before :each do
27
+ Fishbowl::Connection.connect(host: 'localhost')
28
+ end
29
+
30
+ it 'should require the connection to be established' do
31
+ lambda {
32
+ Fishbowl::Connection.close
33
+ Fishbowl::Connection.login
34
+ }.should raise_error(Fishbowl::Errors::ConnectionNotEstablished)
35
+ end
36
+
37
+ it 'should require a username' do
38
+ lambda {
39
+ Fishbowl::Connection.login(password: 'secret')
40
+ }.should raise_error(Fishbowl::Errors::MissingUsername)
41
+ end
42
+
43
+ it 'should require a password' do
44
+ lambda {
45
+ Fishbowl::Connection.login(username: 'johndoe')
46
+ }.should raise_error(Fishbowl::Errors::MissingPassword)
47
+ end
48
+
49
+ it 'should connect to Fishbowl API' do
50
+ mock_login_response
51
+ Fishbowl::Connection.login(username: 'johndoe', password: 'secret')
52
+ end
53
+ end
54
+
55
+ describe '.close' do
56
+ it "should close the connection" do
57
+ lambda {
58
+ mock_login_response
59
+ Fishbowl::Connection.connect(host: 'localhost')
60
+ Fishbowl::Connection.login(username: 'johndoe', password: 'secret')
61
+
62
+ Fishbowl::Connection.close
63
+ Fishbowl::Connection.login
64
+ }.should raise_error(Fishbowl::Errors::ConnectionNotEstablished)
65
+ end
66
+ end
67
+
68
+ describe '.host' do
69
+ it 'should return the host value' do
70
+ Fishbowl::Connection.connect(host: 'localhost')
71
+ Fishbowl::Connection.host.should eq('localhost')
72
+ end
73
+ end
74
+
75
+ describe '.port' do
76
+ it "should return the port value" do
77
+ Fishbowl::Connection.connect(host: 'localhost', port: 1234)
78
+ Fishbowl::Connection.port.should eq(1234)
79
+ end
80
+ end
81
+
82
+ describe '.username' do
83
+ it 'should return the username value' do
84
+ mock_login_response
85
+ Fishbowl::Connection.connect(host: 'localhost')
86
+ Fishbowl::Connection.login(username: 'johndoe', password: 'secret')
87
+ Fishbowl::Connection.username.should eq('johndoe')
88
+ end
89
+ end
90
+
91
+ describe '.password' do
92
+ it 'should return the password value' do
93
+ mock_login_response
94
+ Fishbowl::Connection.connect(host: 'localhost')
95
+ Fishbowl::Connection.login(username: 'johndoe', password: 'secret')
96
+ Fishbowl::Connection.password.should eq('secret')
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fishbowl::Errors do
4
+ describe '.confirm_success_or_raise' do
5
+ it "should return true is code is 1000" do
6
+ Fishbowl::Errors.confirm_success_or_raise('1000', '').should be_true
7
+ end
8
+
9
+ it "should raise an error on any other code" do
10
+ 10.times do
11
+ code = rand(3004) + 1000
12
+ lambda {
13
+ Fishbowl::Errors.confirm_success_or_raise(code, '')
14
+ }.should raise_error(Fishbowl::Errors::StatusError)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fishbowl::Objects::Account do
4
+ before :each do
5
+ mock_tcp_connection
6
+ mock_login_response
7
+ Fishbowl::Connection.connect(host: 'localhost')
8
+ Fishbowl::Connection.login(username: 'johndoe', password: 'secret')
9
+ end
10
+
11
+ after :each do
12
+ unmock_tcp
13
+ end
14
+
15
+ let(:connection) { FakeTCPSocket.instance }
16
+
17
+ describe ".get_list" do
18
+ let(:proper_request) do
19
+ Nokogiri::XML::Builder.new do |xml|
20
+ xml.FbiXml {
21
+ xml.Ticket
22
+ xml.FbiMsgsRq {
23
+ xml.GetAccountListRq
24
+ }
25
+ }
26
+ end
27
+ end
28
+
29
+ let(:canned_response) do
30
+
31
+ end
32
+
33
+ it "should properly format the request" do
34
+ Fishbowl::Objects::Account.get_list
35
+ connection.last_write.should be_equivalent_to(proper_request.to_xml)
36
+ end
37
+
38
+ it "should return array of Accounts"
39
+
40
+ it "should return empty array when no accounts" do
41
+
42
+ end
43
+ end
44
+
45
+ describe ".get_balance" do
46
+ let(:proper_request) do
47
+ Nokogiri::XML::Builder.new do |xml|
48
+ xml.FbiXml {
49
+ xml.Ticket
50
+ xml.FbiMsgsRq {
51
+ xml.GetAccountBalanceRq {
52
+ xml.Account "General Account"
53
+ }
54
+ }
55
+ }
56
+ end
57
+ end
58
+
59
+ it "should properly format the request" do
60
+ Fishbowl::Objects::Account.get_balance("General Account")
61
+ connection.last_write.should be_equivalent_to(proper_request.to_xml)
62
+ end
63
+
64
+ it "should return the balance for the requested Account"
65
+ end
66
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe Fishbowl::Objects::BaseObject do
4
+ let(:ticket) { "thisisasample" }
5
+ let(:base_object) { Fishbowl::Objects::BaseObject.new }
6
+
7
+ let(:empty_ticket_builder) do
8
+ Nokogiri::XML::Builder.new do |xml|
9
+ xml.FbiXml {
10
+ xml.Ticket
11
+
12
+ xml.FbiMsgsRq {
13
+ xml.SampleRequest
14
+ }
15
+ }
16
+ end
17
+ end
18
+
19
+ let(:ticket_builder) do
20
+ Nokogiri::XML::Builder.new do |xml|
21
+ xml.FbiXml {
22
+ xml.Ticket ticket
23
+
24
+ xml.FbiMsgsRq {
25
+ xml.SampleRequest
26
+ }
27
+ }
28
+ end
29
+ end
30
+
31
+ context "Protected Methods" do
32
+ describe "#send_request" do
33
+
34
+ end
35
+ end
36
+
37
+ context "Private Methods" do
38
+ describe "#build_request" do
39
+ it "should build a request document" do
40
+ base_object.send(:build_request, "SampleRequest").to_xml.should be_equivalent_to(empty_ticket_builder.to_xml)
41
+ end
42
+
43
+ it "should accept an XML Builder" do
44
+ builder = Nokogiri::XML::Builder.new { |xml| xml.request { xml.SampleRequest } }
45
+ base_object.send(:build_request, builder, true).to_xml.should be_equivalent_to(empty_ticket_builder.to_xml)
46
+ end
47
+
48
+ context "when ticket is empty" do
49
+ it "should return an empty Nokogiri::XML::Builder" do
50
+ base_object.send(:build_request, "SampleRequest").to_xml.should be_equivalent_to(empty_ticket_builder.to_xml)
51
+ end
52
+ end
53
+
54
+ context "when ticket is set" do
55
+ before :each do
56
+ base_object.ticket = ticket
57
+ end
58
+
59
+ it "should return the ticket wrapped in a Nokogiri::XML::Builder" do
60
+ base_object.send(:build_request, "SampleRequest").to_xml.should be_equivalent_to(ticket_builder.to_xml)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'simplecov'
5
+ SimpleCov.start do
6
+ add_filter "/spec/"
7
+ end
8
+
9
+ require 'equivalent-xml/rspec_matchers'
10
+
11
+ require 'fishbowl'
12
+
13
+ require 'support/fake_socket'
14
+ require 'support/fake_login'
15
+
16
+ RSpec.configure do |config|
17
+ # some (optional) config here
18
+ end
@@ -0,0 +1,15 @@
1
+ require 'support/fake_socket'
2
+
3
+ def mock_login_response
4
+ fake_login_response = Nokogiri::XML::Builder.new do |xml|
5
+ xml.FbiXml {
6
+ xml.Ticket
7
+
8
+ xml.FbiMsgsRs(statusCode: '1000', statusMessage: "Success!") {
9
+ xml.LoginRs(statusCode: '1000', statusMessage: "Success!")
10
+ }
11
+ }
12
+ end
13
+
14
+ FakeTCPSocket.instance.set_canned(fake_login_response.to_xml)
15
+ end
@@ -0,0 +1,46 @@
1
+ require 'socket'
2
+ require 'singleton'
3
+ require 'rspec/mocks'
4
+
5
+ TCP_NEW = TCPSocket.method(:new) unless defined? TCP_NEW
6
+
7
+ class FakeTCPSocket
8
+ include Singleton
9
+
10
+ attr_reader :last_write
11
+
12
+ def flush; end
13
+
14
+ def write(some_text = nil)
15
+ @last_write = some_text
16
+ end
17
+
18
+ def readchar
19
+ 6
20
+ end
21
+
22
+ def read(num)
23
+ set_canned('') if @canned_response.nil?
24
+ num > @canned_response.size ? @canned_response : @canned_response.slice!(0..num)
25
+ end
26
+ alias_method :recv, :read
27
+
28
+ def set_canned(response)
29
+ body = response
30
+ size = [body.size].pack("L>")
31
+
32
+ @canned_response = size + body
33
+ end
34
+
35
+ def close; end
36
+ end
37
+
38
+ def mock_tcp_connection
39
+ TCPSocket.stub(:new).and_return {
40
+ FakeTCPSocket.instance
41
+ }
42
+ end
43
+
44
+ def unmock_tcp
45
+ TCPSocket.stub(:new).and_return { TCP_NEW.call }
46
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fishbowl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James Thompson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.5.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.5.5
30
+ description: Provides an interface to the Fishbowl Inventory API.
31
+ email:
32
+ - james@plainprograms.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - .rspec
39
+ - .rvmrc
40
+ - .travis.yml
41
+ - Gemfile
42
+ - LICENSE.txt
43
+ - README.md
44
+ - Rakefile
45
+ - fishbowl.gemspec
46
+ - lib/fishbowl.rb
47
+ - lib/fishbowl/errors.rb
48
+ - lib/fishbowl/objects.rb
49
+ - lib/fishbowl/objects/account.rb
50
+ - lib/fishbowl/objects/base_object.rb
51
+ - lib/fishbowl/version.rb
52
+ - spec/connection_spec.rb
53
+ - spec/errors_spec.rb
54
+ - spec/objects/account_spec.rb
55
+ - spec/objects/base_object_spec.rb
56
+ - spec/spec_helper.rb
57
+ - spec/support/fake_login.rb
58
+ - spec/support/fake_socket.rb
59
+ homepage: https://github.com/readyproject/fishbowl
60
+ licenses: []
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ segments:
72
+ - 0
73
+ hash: -3228914196506736409
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ segments:
81
+ - 0
82
+ hash: -3228914196506736409
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 1.8.24
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Fishbowl Inventory API
89
+ test_files:
90
+ - spec/connection_spec.rb
91
+ - spec/errors_spec.rb
92
+ - spec/objects/account_spec.rb
93
+ - spec/objects/base_object_spec.rb
94
+ - spec/spec_helper.rb
95
+ - spec/support/fake_login.rb
96
+ - spec/support/fake_socket.rb