fishbowl 0.0.1

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