elibri_api_client 1.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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "httparty", "~> 0.7.8"
4
+ gem "nokogiri", "~> 1.4.6"
5
+
6
+ group :development do
7
+ gem "ruby-debug"
8
+ gem "mocha"
9
+ gem "minitest", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.2"
12
+ gem "rcov", ">= 0"
13
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ columnize (0.3.3)
5
+ crack (0.1.8)
6
+ git (1.2.5)
7
+ httparty (0.7.8)
8
+ crack (= 0.1.8)
9
+ jeweler (1.6.2)
10
+ bundler (~> 1.0)
11
+ git (>= 1.2.5)
12
+ rake
13
+ linecache (0.46)
14
+ rbx-require-relative (> 0.0.4)
15
+ minitest (2.2.2)
16
+ mocha (0.9.12)
17
+ nokogiri (1.4.6)
18
+ rake (0.9.0)
19
+ rbx-require-relative (0.0.5)
20
+ rcov (0.9.9)
21
+ ruby-debug (0.10.4)
22
+ columnize (>= 0.1)
23
+ ruby-debug-base (~> 0.10.4.0)
24
+ ruby-debug-base (0.10.4)
25
+ linecache (>= 0.3)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ bundler (~> 1.0.0)
32
+ httparty (~> 0.7.8)
33
+ jeweler (~> 1.6.2)
34
+ minitest
35
+ mocha
36
+ nokogiri (~> 1.4.6)
37
+ rcov
38
+ ruby-debug
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Marcin Urbanski
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,24 @@
1
+ = elibri_api_client
2
+
3
+ require 'elibri_api_client'
4
+ cli = Elibri::ApiClient.new(:host_uri => 'http://localhost:3010', :login => '1b20fa9d72234423979c', :password => '2847cbf4f15a4057e2ab')
5
+
6
+ cli.refill_all_queues!
7
+ cli.pending_data?
8
+ cli.last_pickups
9
+
10
+ cli.pending_queues.each do |pending_queue|
11
+ named_queue = pending_queue.pick_up!
12
+
13
+ named_queue.each_product do |product_xml, product_no|
14
+ # [...]
15
+ end
16
+ end
17
+
18
+
19
+
20
+ == Copyright
21
+
22
+ Copyright (c) 2011 elibri.com.pl. See LICENSE.txt for
23
+ further details.
24
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ require './lib/elibri_api_client/version.rb'
16
+ Jeweler::Tasks.new do |gem|
17
+ gem.name = "elibri_api_client"
18
+ gem.version = Elibri::ApiClient::Version::STRING
19
+ gem.homepage = "http://github.com/elibri/elibri_api_client"
20
+ gem.license = "MIT"
21
+ gem.summary = %Q{API client for elibri.com.pl publishing system}
22
+ gem.description = %Q{API client for elibri.com.pl publishing system}
23
+ gem.email = "marcin@urbanski.vdl.pl"
24
+ gem.authors = ["Marcin Urbański"]
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/*_test.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = Elibri::ApiClient::Version::STRING
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "elibri_api_client #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
@@ -0,0 +1,171 @@
1
+ require 'elibri_api_client/api_adapters/v1_helpers'
2
+
3
+ module Elibri
4
+ class ApiClient
5
+ module ApiAdapters
6
+
7
+ # Adapter dla 1 wersji API
8
+ class V1
9
+ URI_PREFIX = '/api/v1'
10
+
11
+ include HTTParty
12
+ debug_output $stderr
13
+
14
+
15
+ def initialize(host_uri, login, password)
16
+ @host_uri = host_uri
17
+ @auth = {:username => login, :password => password}
18
+ end
19
+
20
+
21
+ # Wypełnij wszystkie kolejki oczekujące, wszystkimi dostępnymi danymi. Przydatne przy wykonywaniu
22
+ # pełnej synchronizacji pomiędzy naszą aplikacją a Elibri.
23
+ def refill_all_queues!
24
+ # Dla POST musi być jakieś 'body' requestu, bo serwery często rzucają wyjątkami (WEBrick w szczególności).
25
+ post '/queues/refill_all', :body => ' '
26
+ end
27
+
28
+
29
+ # Zwróć listę kolejek z oczekującymi danymi.
30
+ def pending_queues
31
+ resp = get '/queues/pending_data'
32
+
33
+ pending_queues = []
34
+ resp.parsed_response.css('queue').each do |queue_xml|
35
+ queue = Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(self, queue_xml)
36
+ pending_queues << queue if queue.items_total.nonzero?
37
+ end
38
+ pending_queues
39
+ end
40
+
41
+
42
+ # Czy są jakieś oczekujące dane w Elibri?
43
+ def pending_data?
44
+ !pending_queues.empty?
45
+ end
46
+
47
+
48
+ # Utwórz z danych oczekujących w kolejce np. 'pending_meta', kolejkę nazwaną.
49
+ # Tylko z kolejek nazwanych można pobierać dane. Jako argument przyjmuje nazwę kolejki (np. 'pending_meta')
50
+ # lub odpowiednią instancję Elibri::ApiClient::ApiAdapters::V1::Queue.
51
+ def pick_up_queue!(queue)
52
+ case queue
53
+ when Elibri::ApiClient::ApiAdapters::V1::Queue
54
+ queue_name = queue.name
55
+ when String
56
+ queue_name = queue
57
+ else
58
+ raise 'Specify queue as name or Elibri::ApiClient::ApiAdapters::V1::Queue instance'
59
+ end
60
+
61
+ response = post "/queues/#{queue_name}/pick_up", :body => ' '
62
+ picked_up_queue_xml = response.parsed_response.css('pick_up queue').first
63
+ Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(self, picked_up_queue_xml)
64
+ end
65
+
66
+
67
+ def each_page(queue, &block)
68
+ raise 'Need a Elibri::ApiClient::ApiAdapters::V1::Queue instance' unless queue.kind_of? Elibri::ApiClient::ApiAdapters::V1::Queue
69
+
70
+ page_no = 1
71
+ response = get "/queues/#{queue.name}/#{queue.queue_id}"
72
+ yield response.parsed_response.css('current_page content'), page_no
73
+ while next_page = response.parsed_response.css('next_page').first
74
+ response = get next_page['url']
75
+ page_no += 1
76
+ yield response.parsed_response.css('current_page content').first, page_no
77
+ end
78
+ end
79
+
80
+
81
+ def each_product(queue, &block)
82
+ raise 'Need a Elibri::ApiClient::ApiAdapters::V1::Queue instance' unless queue.kind_of? Elibri::ApiClient::ApiAdapters::V1::Queue
83
+
84
+ product_no = 1
85
+ each_page(queue) do |products_page_xml, page_no|
86
+ products_page_xml.css('Product').each do |product_xml|
87
+ block.call(product_xml, product_no)
88
+ product_no += 1
89
+ end
90
+ end
91
+ end
92
+
93
+
94
+ # Ostatnio utworzone nazwane kolejki. Gdy wysypie nam się aplikacja, można przeglądać ostatnie pickupy.
95
+ def last_pickups
96
+ last_pickups = []
97
+ %w{meta stocks}.each do |queue_name|
98
+ begin
99
+ response = get "/queues/#{queue_name}/last_pick_up"
100
+ queue_xml = response.parsed_response.css('queue').first
101
+ last_pickups << Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(self, queue_xml)
102
+ rescue NoRecentlyPickedUpQueues # Ignoruj błędy o braku ostatnich pickupów.
103
+ end
104
+ end
105
+
106
+ last_pickups
107
+ end
108
+
109
+
110
+ private
111
+
112
+ # http://api.elibri.com.pl:80/api/v1
113
+ def full_api_uri
114
+ @host_uri + URI_PREFIX
115
+ end
116
+
117
+
118
+ def get(request_uri, options = {})
119
+ options.merge!({:digest_auth => @auth})
120
+ request_uri = normalise_request_uri(request_uri)
121
+
122
+ response = self.class.get(request_uri, options)
123
+ raise_if_error_present_in response
124
+ response
125
+ end
126
+
127
+
128
+ def post(request_uri, options = {})
129
+ options.merge!({:digest_auth => @auth})
130
+ request_uri = normalise_request_uri(request_uri)
131
+
132
+ response = self.class.post(request_uri, options)
133
+ raise_if_error_present_in response
134
+ response
135
+ end
136
+
137
+
138
+ def normalise_request_uri(request_uri)
139
+ if request_uri.start_with? 'http://'
140
+ request_uri
141
+ elsif !request_uri.start_with? '/'
142
+ full_api_uri + '/' + request_uri
143
+ else
144
+ full_api_uri + request_uri
145
+ end
146
+ end
147
+
148
+
149
+ def raise_if_error_present_in(response)
150
+ raise Unauthorized, 'Bad login or password' if response.code == 401
151
+
152
+ response_xml = response.parsed_response
153
+ if response_xml && !response_xml.css('error').empty?
154
+ error_id = response_xml.css('error').first['id']
155
+ error_message = response_xml.css('error message').first.text
156
+
157
+ # Rozpoznajemy ten kod błędu i możemy rzucić określoną klasą wyjątku:
158
+ if exception_class = EXCEPTION_CLASSES[error_id.to_s]
159
+ raise exception_class, error_message
160
+ else
161
+ # Jakiś nieznany błąd - rzucamy chociaż stringiem
162
+ raise UnknownError, "ELIBRI_API ERROR #{error_id}: #{error_message}"
163
+ end
164
+ end
165
+ end
166
+
167
+ end
168
+
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,90 @@
1
+
2
+ # Klasy pomocnicze dla API w wersji 1
3
+ module Elibri
4
+ class ApiClient
5
+ module ApiAdapters
6
+ class V1
7
+
8
+ class UnknownError < RuntimeError; end
9
+ class Unauthorized < RuntimeError; end
10
+ class NotFound < RuntimeError; end
11
+ class Forbidden < RuntimeError; end
12
+ class ServerError < RuntimeError; end
13
+ class NoPendingData < RuntimeError; end
14
+ class NoRecentlyPickedUpQueues < RuntimeError; end
15
+ class QueueDoesNotExists < RuntimeError; end
16
+ class InvalidPageNumber < RuntimeError; end
17
+
18
+ # Klasy wyjątków rzucanych, gdy elibri zwróci określony błąd. Np. gdy dostaniemy:
19
+ # <error id="1001">
20
+ # <message>Queue does not exist</message>
21
+ # </error>
22
+ # Biblioteka rzuca wyjątkiem QueueDoesNotExists.
23
+ EXCEPTION_CLASSES = {
24
+ '404' => NotFound,
25
+ '403' => Forbidden,
26
+ '500' => ServerError,
27
+ '1001' => QueueDoesNotExists,
28
+ '1002' => NoPendingData,
29
+ '1003' => NoRecentlyPickedUpQueues,
30
+ '1004' => InvalidPageNumber
31
+ }.freeze
32
+
33
+
34
+ # Zamiast rzeźbić ciągle w XML`u, tworzymy instancje kolejek.
35
+ class Queue
36
+
37
+ attr_reader :name, :items_total, :picked_up_at, :last_insert_at, :queue_id, :url
38
+
39
+ def initialize(api_adapter, attributes = {})
40
+ @api_adapter = api_adapter
41
+ @name = attributes[:name]
42
+ @items_total = attributes[:items_total].to_i
43
+ @queue_id = attributes[:queue_id]
44
+ @url = attributes[:url]
45
+
46
+ @last_insert_at = Time.parse(attributes[:last_insert_at]) if attributes[:last_insert_at].present?
47
+ @picked_up_at = Time.parse(attributes[:picked_up_at]) if attributes[:picked_up_at].present?
48
+ end
49
+
50
+
51
+ # Przekonwertuj kolejkę z danymi oczekującymi (np. 'pending_meta') na kolejkę nazwaną.
52
+ def pick_up!
53
+ @api_adapter.pick_up_queue!(self) unless picked_up?
54
+ end
55
+
56
+
57
+ # Hermetyzujemy stronicowanie danych. Programistę interesują tylko kolejne rekordy <Product>
58
+ def each_product(&block)
59
+ @api_adapter.each_product(self, &block)
60
+ end
61
+
62
+
63
+ # Czy to jest kolejka nazwana, czy oczekująca? Wszystkie kolejki z danymi oczekującymi mają w nazwie
64
+ # przedrostek 'pending_'. Np. 'pending_meta', 'pending_stocks'.
65
+ def picked_up?
66
+ !self.name.start_with?('pending_')
67
+ end
68
+
69
+
70
+ # Zbuduj instancję kolejki na podstawie XML`a.
71
+ def self.build_from_xml(api_adapter, queue_xml)
72
+ queue_xml = Nokogiri::XML(queue_xml).css('queue').first if queue_xml.is_a? String
73
+ Queue.new(api_adapter,
74
+ :name => queue_xml['name'],
75
+ :items_total => queue_xml['items_total'].to_i,
76
+ :last_insert_at => queue_xml['last_insert_at'],
77
+ :url => queue_xml['url'],
78
+ :queue_id => queue_xml['id'],
79
+ :picked_up_at => queue_xml['picked_up_at']
80
+ )
81
+ end
82
+
83
+
84
+ end
85
+
86
+
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,16 @@
1
+
2
+ require 'elibri_api_client/api_adapters/v1'
3
+
4
+ module Elibri
5
+ class ApiClient
6
+
7
+ # Moduł zawierający adaptery dla poszczególnych wersji API. Dzięki takiej architekturze
8
+ # dodanie kolejnej wersji API sprowadza się do utworzenia nowej klasy w tym module, np.
9
+ # Elibri::ApiClient::ApiAdapters::V3
10
+ module ApiAdapters
11
+
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,60 @@
1
+
2
+
3
+ # HTTParty ma parsować XML za pomocą Nokogiri. CRACK jest zbyt prosty do naszych zastosowań.
4
+ module HTTParty
5
+ class Parser
6
+
7
+ def xml
8
+ @parsed_xml ||= Nokogiri::XML(body)
9
+ end
10
+
11
+ end
12
+ end
13
+
14
+
15
+ # Ukradzione z ActiveSupport:
16
+ class Object
17
+ def blank?
18
+ respond_to?(:empty?) ? empty? : !self
19
+ end
20
+
21
+ def present?
22
+ !blank?
23
+ end
24
+
25
+ def presence
26
+ self if present?
27
+ end
28
+ end
29
+
30
+ class NilClass
31
+ def blank?
32
+ true
33
+ end
34
+ end
35
+
36
+ class FalseClass
37
+ def blank?
38
+ true
39
+ end
40
+ end
41
+
42
+ class TrueClass
43
+ def blank?
44
+ false
45
+ end
46
+ end
47
+
48
+
49
+ class String
50
+ def blank?
51
+ self !~ /\S/
52
+ end
53
+ end
54
+
55
+
56
+ class Numeric #:nodoc:
57
+ def blank?
58
+ false
59
+ end
60
+ end
@@ -0,0 +1,13 @@
1
+
2
+ module Elibri
3
+ class ApiClient
4
+ module Version
5
+ MAJOR = 1
6
+ MINOR = 0
7
+ PATCH = 1
8
+
9
+ STRING = [MAJOR, MINOR, PATCH].compact.join('.')
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+
2
+ require 'time'
3
+ require 'httparty'
4
+ require 'nokogiri'
5
+ require 'forwardable'
6
+ require 'elibri_api_client/core_extensions'
7
+ require 'elibri_api_client/version'
8
+ require 'elibri_api_client/api_adapters'
9
+
10
+ module Elibri
11
+ class ApiClient
12
+ extend Forwardable
13
+
14
+ DEFAULT_API_HOST_URI = 'http://api.elibri.com.pl:80'
15
+ attr_reader :host_uri
16
+
17
+
18
+ def initialize(options = {})
19
+ @login = options[:login]
20
+ @password = options[:password]
21
+ @host_uri = options[:host_uri] || DEFAULT_API_HOST_URI
22
+
23
+ # W przyszłości mogą być nowe wersje API, więc zostawiamy sobie furtkę w postaci adapterów:
24
+ api_version_str = options[:api_version] || 'v1'
25
+ adapter_class = Elibri::ApiClient::ApiAdapters.const_get(api_version_str.upcase) # Elibri::ApiClient::ApiAdapters::V1
26
+ @api_adapter = adapter_class.new(@host_uri, @login, @password)
27
+ end
28
+
29
+
30
+ # Metody API delegujemy do odpowiedniego adaptera:
31
+ def_delegators :@api_adapter,
32
+ :refill_all_queues!, :pending_data?, :pending_queues, :pick_up_queue!, :last_pickups, :each_product, :each_page
33
+
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ require 'helper'
2
+
3
+
4
+ # Testowy adapter
5
+ module Elibri
6
+ class ApiClient
7
+ module ApiAdapters
8
+ # Testowy adapter.
9
+ class V999
10
+ def initialize(host_uri, login, password)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+
18
+ describe Elibri::ApiClient do
19
+
20
+ it "should be able to establish its version" do
21
+ assert_match /\d+\.\d+\.\d+/, Elibri::ApiClient::Version::STRING
22
+ end
23
+
24
+
25
+ it "should take some defaults in constructor" do
26
+ client = Elibri::ApiClient.new(:login => 'elibri_login', :password => 'pass')
27
+ assert_equal Elibri::ApiClient::DEFAULT_API_HOST_URI, client.host_uri
28
+
29
+ client = Elibri::ApiClient.new(:login => 'elibri_login', :password => 'pass', :host_uri => 'http://localhost:3000')
30
+ assert_equal 'http://localhost:3000', client.host_uri
31
+ end
32
+
33
+
34
+ it "should enable user to pick a version of API" do
35
+ client = Elibri::ApiClient.new(:login => 'elibri_login', :password => 'pass', :api_version => 'v999')
36
+ assert_kind_of Elibri::ApiClient::ApiAdapters::V999, client.instance_variable_get('@api_adapter')
37
+ end
38
+
39
+
40
+ it "should delegate several methods to apropriate API adapter" do
41
+ client = Elibri::ApiClient.new(:login => 'elibri_login', :password => 'pass')
42
+
43
+ delegated_methods = %w{refill_all_queues! pending_data? pending_queues pick_up_queue! last_pickups each_product each_page}
44
+ delegated_methods.each {|method_name| client.instance_variable_get('@api_adapter').expects(method_name) }
45
+ delegated_methods.each {|method_name| client.send(method_name) }
46
+ end
47
+
48
+
49
+ end
50
+
@@ -0,0 +1,86 @@
1
+ require 'helper'
2
+
3
+
4
+ describe Elibri::ApiClient::ApiAdapters::V1::Queue do
5
+
6
+ before do
7
+ @api_adapter = mock('api_adapter')
8
+ end
9
+
10
+
11
+ it "should have several attributes" do
12
+ time = Time.now
13
+ queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter,
14
+ :name => 'meta',
15
+ :items_total => 120,
16
+ :queue_id => '192f134e666df34464bcc14d0413',
17
+ :url => 'http://api.elibri.com.pl/api/v1/queues/meta/192f134e666df34464bcc14d0413',
18
+ :last_insert_at => time.to_s,
19
+ :picked_up_at => time.to_s
20
+ )
21
+
22
+ assert_equal 'meta', queue.name
23
+ assert_equal 120, queue.items_total
24
+ assert_equal '192f134e666df34464bcc14d0413', queue.queue_id
25
+ assert_equal 'http://api.elibri.com.pl/api/v1/queues/meta/192f134e666df34464bcc14d0413', queue.url
26
+ assert_equal time.to_i, queue.last_insert_at.to_i
27
+ assert_equal time.to_i, queue.picked_up_at.to_i
28
+ end
29
+
30
+
31
+
32
+ it "should be able to build itself from provided XML" do
33
+ xml = %Q{<queue name="stocks" id="192f134e666df34464bcc14d04135cda2bd13a0c" url="http://api.elibri.com.pl/api/v1/queues/stocks/192f134e666df34464bcc14d04135cda2bd13a0c" items_total="1500" last_insert_at="2011-02-05 21:02:22 UTC" picked_up_at="2011-02-05 22:02:22 UTC"/>}
34
+
35
+ queue = Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(@api_adapter, xml)
36
+ assert_equal 'stocks', queue.name
37
+ assert_equal 1500, queue.items_total
38
+ assert_equal '192f134e666df34464bcc14d04135cda2bd13a0c', queue.queue_id
39
+ assert_equal 'http://api.elibri.com.pl/api/v1/queues/stocks/192f134e666df34464bcc14d04135cda2bd13a0c', queue.url
40
+ assert_equal Time.parse("2011-02-05 21:02:22 UTC"), queue.last_insert_at
41
+ assert_equal Time.parse("2011-02-05 22:02:22 UTC"), queue.picked_up_at
42
+
43
+ xml = %Q{<queue name="pending_stocks" items_total="1500" last_insert_at="2011-02-05 21:02:22 UTC"/>}
44
+ queue = Elibri::ApiClient::ApiAdapters::V1::Queue.build_from_xml(@api_adapter, xml)
45
+ assert_nil queue.queue_id
46
+ assert_nil queue.picked_up_at
47
+ assert_nil queue.url
48
+ end
49
+
50
+
51
+ it "should be able to establish if its a picked up queue" do
52
+ queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'pending_meta')
53
+ assert !queue.picked_up?
54
+
55
+ queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'meta')
56
+ assert queue.picked_up?
57
+ end
58
+
59
+
60
+
61
+
62
+ it "should be able to pick up itself when its a pending queue" do
63
+ queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'pending_meta')
64
+ @api_adapter.expects(:pick_up_queue!).with(queue)
65
+ queue.pick_up!
66
+
67
+ queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'meta')
68
+ @api_adapter.expects(:pick_up_queue!).with(queue).never
69
+ queue.pick_up!
70
+ end
71
+
72
+
73
+
74
+ it "should provide iterator for traversing products list" do
75
+ block = lambda {|product_xml| product_xml.css('RecordReference') }
76
+
77
+ queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'meta')
78
+ @api_adapter.expects(:each_product).with(queue)
79
+ queue.each_product(&block)
80
+ end
81
+
82
+
83
+
84
+
85
+
86
+ end
@@ -0,0 +1,309 @@
1
+ require 'helper'
2
+
3
+
4
+ describe Elibri::ApiClient::ApiAdapters::V1 do
5
+
6
+ FAKE_API_HOST = 'http://localhost:5000'
7
+
8
+ def get_request_expected(request_uri)
9
+ Elibri::ApiClient::ApiAdapters::V1.expects(:get).with(
10
+ request_uri,
11
+ {:digest_auth => {:username => 'login', :password => 'password'}}
12
+ )
13
+ end
14
+
15
+
16
+ def post_request_expected(request_uri)
17
+ Elibri::ApiClient::ApiAdapters::V1.expects(:post).with(
18
+ request_uri,
19
+ {:body => ' ', :digest_auth => {:username => 'login', :password => 'password'}}
20
+ )
21
+ end
22
+
23
+
24
+ before do
25
+ @adapter = Elibri::ApiClient::ApiAdapters::V1.new(FAKE_API_HOST, 'login', 'password')
26
+ end
27
+
28
+
29
+ it "should define several exception classes" do
30
+ exception_classes = %w{Unauthorized NotFound Forbidden ServerError NoPendingData NoRecentlyPickedUpQueues QueueDoesNotExists InvalidPageNumber}
31
+ exception_classes.each do |exception_class|
32
+ assert(Elibri::ApiClient::ApiAdapters::V1.const_get(exception_class) < RuntimeError)
33
+ end
34
+ end
35
+
36
+
37
+ it "should be able to refill all pending queues" do
38
+ response_stub = stub('response_stub', :code => 200, :parsed_response => nil)
39
+ post_request_expected("#{FAKE_API_HOST}/api/v1/queues/refill_all").returns(response_stub)
40
+
41
+ @adapter.refill_all_queues!
42
+ end
43
+
44
+
45
+ describe "when asked to establish pending queues list" do
46
+
47
+ describe "and there is pending data awaiting for pull" do
48
+ before do
49
+ xml = <<-XML
50
+ <pending_data>
51
+ <queue name="pending_meta" items_total="24" last_insert_at="2011-02-05 21:02:22 UTC"/>
52
+ <queue name="pending_stocks" items_total="1500" last_insert_at="2011-02-05 21:02:22 UTC"/>
53
+ </pending_data>
54
+ XML
55
+
56
+ response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(xml))
57
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").at_least_once.returns(response_stub)
58
+ end
59
+
60
+
61
+ it "should return Queue instances with attributes filled from returned XML" do
62
+ assert @adapter.pending_data?
63
+ pending_queues = @adapter.pending_queues
64
+ assert_equal 2, pending_queues.size
65
+
66
+ pending_meta = pending_queues.find {|queue| queue.name == 'pending_meta' }
67
+ pending_stocks = pending_queues.find {|queue| queue.name == 'pending_stocks' }
68
+
69
+ assert_equal 24, pending_meta.items_total
70
+ assert_kind_of Time, pending_meta.last_insert_at
71
+
72
+ assert_equal 1500, pending_stocks.items_total
73
+ assert_kind_of Time, pending_stocks.last_insert_at
74
+ end
75
+ end
76
+
77
+
78
+ describe "and there is no pending data awaiting for pull" do
79
+ before do
80
+ xml = <<-XML
81
+ <pending_data>
82
+ <queue name="pending_meta" items_total="0" />
83
+ <queue name="pending_stocks" items_total="0" />
84
+ </pending_data>
85
+ XML
86
+
87
+ response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(xml))
88
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").at_least_once.returns(response_stub)
89
+ end
90
+
91
+
92
+ it "should return empty array and claim that there is no pending data" do
93
+ assert @adapter.pending_queues.empty?
94
+ assert !@adapter.pending_data?
95
+ end
96
+ end
97
+ end
98
+
99
+
100
+ describe "when asked to pick up queue" do
101
+ before do
102
+ xml = <<-XML
103
+ <pick_up>
104
+ <queue name="meta" id="192f134e666df34464bcc14d04135cda2bd13a0c" url="#{FAKE_API_HOST}/api/v1/queues/meta/192f134e666df34464bcc14d04135cda2bd13a0c" items_total="24" last_insert_at="2011-02-05 21:02:22 UTC" picked_up_at="2011-02-05 21:02:22 UTC"/>
105
+ </pick_up>
106
+ XML
107
+
108
+ @response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(xml))
109
+ end
110
+
111
+
112
+ it "should be able to pick it up by name" do
113
+ post_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_meta/pick_up").at_least_once.returns(@response_stub)
114
+ picked_up_queue = @adapter.pick_up_queue!('pending_meta')
115
+ assert_equal 24, picked_up_queue.items_total
116
+ assert_kind_of Time, picked_up_queue.picked_up_at
117
+ end
118
+
119
+
120
+ it "should be able to pick it up by Queue instance" do
121
+ post_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_meta/pick_up").at_least_once.returns(@response_stub)
122
+ queue_to_pick_up = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@api_adapter, :name => 'pending_meta')
123
+ picked_up_queue = @adapter.pick_up_queue!(queue_to_pick_up)
124
+ assert_equal 24, picked_up_queue.items_total
125
+ assert_kind_of Time, picked_up_queue.picked_up_at
126
+ end
127
+
128
+
129
+ it "should raise error when cannot infer queue name from argument" do
130
+ post_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_meta/pick_up").never
131
+ post_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_stocks/pick_up").never
132
+
133
+ assert_raises(RuntimeError) { @adapter.pick_up_queue!(12345) }
134
+ assert_raises(RuntimeError) { @adapter.pick_up_queue!(Array.new) }
135
+ end
136
+ end
137
+
138
+
139
+ it "should normalise request URI before performing real request" do
140
+ response_stub = stub('response_stub', :code => 200, :parsed_response => nil)
141
+
142
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/192f134e666df34464bcc14d0413").once.returns(response_stub)
143
+ @adapter.send(:get, "#{FAKE_API_HOST}/api/v1/queues/meta/192f134e666df34464bcc14d0413")
144
+
145
+ get_request_expected("#{FAKE_API_HOST}/api/v1/api_entrypoint").once.returns(response_stub)
146
+ @adapter.send(:get, '/api_entrypoint')
147
+
148
+ get_request_expected("#{FAKE_API_HOST}/api/v1/api_entrypoint_without_leading_slash").once.returns(response_stub)
149
+ @adapter.send(:get, 'api_entrypoint_without_leading_slash')
150
+ end
151
+
152
+
153
+ describe "when there was error reported" do
154
+ before do
155
+ @xml = %q{<error id="ERROR_CODE"> <message>Error message for error ERROR_CODE</message> </error>}
156
+ @exception_classes = {
157
+ '404' => Elibri::ApiClient::ApiAdapters::V1::NotFound,
158
+ '403' => Elibri::ApiClient::ApiAdapters::V1::Forbidden,
159
+ '500' => Elibri::ApiClient::ApiAdapters::V1::ServerError,
160
+ '1001' => Elibri::ApiClient::ApiAdapters::V1::QueueDoesNotExists,
161
+ '1002' => Elibri::ApiClient::ApiAdapters::V1::NoPendingData,
162
+ '1003' => Elibri::ApiClient::ApiAdapters::V1::NoRecentlyPickedUpQueues,
163
+ '1004' => Elibri::ApiClient::ApiAdapters::V1::InvalidPageNumber
164
+ }
165
+ end
166
+
167
+
168
+ it "should raise proper exception class" do
169
+ @exception_classes.each do |error_code, exception_class|
170
+ response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML( @xml.gsub('ERROR_CODE', error_code) ))
171
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").once.returns(response_stub)
172
+ assert_raises(exception_class) { @adapter.pending_queues }
173
+ end
174
+ end
175
+
176
+
177
+ it "should raise Unauthorized, when there is 401 response code" do
178
+ response_stub = stub('response_stub', :code => 401, :parsed_response => nil)
179
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").once.returns(response_stub)
180
+ assert_raises(Elibri::ApiClient::ApiAdapters::V1::Unauthorized) { @adapter.pending_queues }
181
+ end
182
+
183
+
184
+ it "should raise RuntimeError on unknown error code" do
185
+ response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML( @xml.gsub('ERROR_CODE', 'UNKNOWN_ERROR_CODE') ))
186
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/pending_data").once.returns(response_stub)
187
+ assert_raises(Elibri::ApiClient::ApiAdapters::V1::UnknownError) { @adapter.pending_queues }
188
+ end
189
+
190
+ end
191
+
192
+
193
+ describe "when asked to get latest pickups" do
194
+ describe "and there is no recently picked up queues" do
195
+ before do
196
+ xml = %q{<error id='1003'> <message>There is no recently picked up queue</message> </error>}
197
+ response_stub = stub('response_stub', :code => 412, :parsed_response => Nokogiri::XML(xml))
198
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/last_pick_up").once.returns(response_stub)
199
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/stocks/last_pick_up").once.returns(response_stub)
200
+ end
201
+
202
+ it "should return empty latest pickups list" do
203
+ assert @adapter.last_pickups.empty?
204
+ end
205
+ end
206
+
207
+
208
+ describe "and there were recently picked up queues" do
209
+ before do
210
+ meta_xml = <<-XML
211
+ <pick_up>
212
+ <queue name="meta" id="192f134e666df34464bcc14d04135cda2bd13a0c" url="#{FAKE_API_HOST}/api/v1/queues/meta/192f134e666df34464bcc14d04135cda2bd13a0c" items_total="24" last_insert_at="2011-02-05 21:02:22 UTC" picked_up_at="2011-02-05 21:02:22 UTC"/>
213
+ </pick_up>
214
+ XML
215
+
216
+ stocks_xml = <<-XML
217
+ <pick_up>
218
+ <queue name="stocks" id="192f134e666df34464bcc14d04135cda2bd13a0d" url="#{FAKE_API_HOST}/api/v1/queues/stocks/192f134e666df34464bcc14d04135cda2bd13a0d" items_total="50" last_insert_at="2011-02-05 21:02:22 UTC" picked_up_at="2011-02-05 21:02:22 UTC"/>
219
+ </pick_up>
220
+ XML
221
+
222
+ meta_response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(meta_xml))
223
+ stocks_response_stub = stub('response_stub', :code => 200, :parsed_response => Nokogiri::XML(stocks_xml))
224
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/last_pick_up").once.returns(meta_response_stub)
225
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/stocks/last_pick_up").once.returns(stocks_response_stub)
226
+ end
227
+
228
+
229
+ it "should return latest pickups as Queue instances" do
230
+ last_pickups = @adapter.last_pickups
231
+ assert_equal 2, last_pickups.size
232
+ assert(last_pickups.all? { |pickup| pickup.is_a? Elibri::ApiClient::ApiAdapters::V1::Queue })
233
+ end
234
+ end
235
+ end
236
+
237
+
238
+ describe "when asked to iterate by products" do
239
+
240
+ before do
241
+ @queue = Elibri::ApiClient::ApiAdapters::V1::Queue.new(@adapter, :name => 'meta', :queue_id => 'QUEUE_ID')
242
+
243
+ first_page_xml = <<-XML
244
+ <queue name="meta" id="QUEUE_ID" items_total="54" picked_up_at="2011-02-05 21:02:22 UTC">
245
+ <items paginated="true">
246
+ <pages total="3" items_per_page="20" >
247
+ <next_page page_no="2" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/2" />
248
+ <current_page page_no="1" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID" >
249
+ <content><Product>PRODUCT_FROM_PAGE_1</Product></content>
250
+ </current_page>
251
+ </pages>
252
+ </items>
253
+ </queue>
254
+ XML
255
+
256
+ second_page_xml = <<-XML
257
+ <queue name="meta" id="QUEUE_ID" items_total="54" picked_up_at="2011-02-05 21:02:22 UTC">
258
+ <items paginated="true">
259
+ <pages total="3" items_per_page="20" >
260
+ <previous_page page_no="1" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID" />
261
+ <next_page page_no="3" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/3" />
262
+ <current_page page_no="2" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/2" >
263
+ <content><Product>PRODUCT_FROM_PAGE_2</Product></content>
264
+ </current_page>
265
+ </pages>
266
+ </items>
267
+ </queue>
268
+ XML
269
+
270
+ third_page_xml = <<-XML
271
+ <queue name="meta" id="QUEUE_ID" items_total="54" picked_up_at="2011-02-05 21:02:22 UTC">
272
+ <items paginated="true">
273
+ <pages total="3" items_per_page="20" >
274
+ <previous_page page_no="2" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/2" />
275
+ <current_page page_no="3" url="#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/3" >
276
+ <content><Product>PRODUCT_FROM_PAGE_3</Product></content>
277
+ </current_page>
278
+ </pages>
279
+ </items>
280
+ </queue>
281
+ XML
282
+
283
+ first_page_response_stub = stub('response1_stub', :code => 200, :parsed_response => Nokogiri::XML(first_page_xml))
284
+ second_page_response_stub = stub('response2_stub', :code => 200, :parsed_response => Nokogiri::XML(second_page_xml))
285
+ third_page_response_stub = stub('response3_stub', :code => 200, :parsed_response => Nokogiri::XML(third_page_xml))
286
+
287
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID").once.returns(first_page_response_stub)
288
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/2").once.returns(second_page_response_stub)
289
+ get_request_expected("#{FAKE_API_HOST}/api/v1/queues/meta/QUEUE_ID/3").once.returns(third_page_response_stub)
290
+ end
291
+
292
+
293
+ it "should be able to iterate through page products" do
294
+ expected_content = %w{<Product>PRODUCT_FROM_PAGE_1</Product> <Product>PRODUCT_FROM_PAGE_2</Product> <Product>PRODUCT_FROM_PAGE_3</Product>}
295
+ @adapter.each_page(@queue) do |page_content, page_no|
296
+ assert_equal expected_content[page_no-1], page_content.children.to_s
297
+ end
298
+ end
299
+
300
+
301
+ it "should be able to iterate through all product records" do
302
+ expected_records = %w{PRODUCT_FROM_PAGE_1 PRODUCT_FROM_PAGE_2 PRODUCT_FROM_PAGE_3}
303
+ @adapter.each_product(@queue) do |product_xml, product_no|
304
+ assert_equal expected_records[product_no-1], product_xml.text
305
+ end
306
+ end
307
+ end
308
+
309
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,21 @@
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 'minitest/autorun'
12
+ require 'mocha'
13
+ require 'ruby-debug'
14
+ Debugger.settings[:autoeval] = true rescue nil
15
+
16
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
17
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
18
+ require 'elibri_api_client'
19
+
20
+ class Test::Unit::TestCase
21
+ end
metadata ADDED
@@ -0,0 +1,202 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elibri_api_client
3
+ version: !ruby/object:Gem::Version
4
+ hash: 21
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 1
10
+ version: 1.0.1
11
+ platform: ruby
12
+ authors:
13
+ - "Marcin Urba\xC5\x84ski"
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-06 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ type: :runtime
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 19
29
+ segments:
30
+ - 0
31
+ - 7
32
+ - 8
33
+ version: 0.7.8
34
+ name: httparty
35
+ version_requirements: *id001
36
+ prerelease: false
37
+ - !ruby/object:Gem::Dependency
38
+ type: :runtime
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 11
45
+ segments:
46
+ - 1
47
+ - 4
48
+ - 6
49
+ version: 1.4.6
50
+ name: nokogiri
51
+ version_requirements: *id002
52
+ prerelease: false
53
+ - !ruby/object:Gem::Dependency
54
+ type: :development
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ name: ruby-debug
65
+ version_requirements: *id003
66
+ prerelease: false
67
+ - !ruby/object:Gem::Dependency
68
+ type: :development
69
+ requirement: &id004 !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 3
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ name: mocha
79
+ version_requirements: *id004
80
+ prerelease: false
81
+ - !ruby/object:Gem::Dependency
82
+ type: :development
83
+ requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ name: minitest
93
+ version_requirements: *id005
94
+ prerelease: false
95
+ - !ruby/object:Gem::Dependency
96
+ type: :development
97
+ requirement: &id006 !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ~>
101
+ - !ruby/object:Gem::Version
102
+ hash: 23
103
+ segments:
104
+ - 1
105
+ - 0
106
+ - 0
107
+ version: 1.0.0
108
+ name: bundler
109
+ version_requirements: *id006
110
+ prerelease: false
111
+ - !ruby/object:Gem::Dependency
112
+ type: :development
113
+ requirement: &id007 !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ~>
117
+ - !ruby/object:Gem::Version
118
+ hash: 11
119
+ segments:
120
+ - 1
121
+ - 6
122
+ - 2
123
+ version: 1.6.2
124
+ name: jeweler
125
+ version_requirements: *id007
126
+ prerelease: false
127
+ - !ruby/object:Gem::Dependency
128
+ type: :development
129
+ requirement: &id008 !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ hash: 3
135
+ segments:
136
+ - 0
137
+ version: "0"
138
+ name: rcov
139
+ version_requirements: *id008
140
+ prerelease: false
141
+ description: API client for elibri.com.pl publishing system
142
+ email: marcin@urbanski.vdl.pl
143
+ executables: []
144
+
145
+ extensions: []
146
+
147
+ extra_rdoc_files:
148
+ - LICENSE.txt
149
+ - README.rdoc
150
+ files:
151
+ - .document
152
+ - Gemfile
153
+ - Gemfile.lock
154
+ - LICENSE.txt
155
+ - README.rdoc
156
+ - Rakefile
157
+ - lib/elibri_api_client.rb
158
+ - lib/elibri_api_client/api_adapters.rb
159
+ - lib/elibri_api_client/api_adapters/v1.rb
160
+ - lib/elibri_api_client/api_adapters/v1_helpers.rb
161
+ - lib/elibri_api_client/core_extensions.rb
162
+ - lib/elibri_api_client/version.rb
163
+ - test/elibri_api_client_test.rb
164
+ - test/elibri_api_v1_adapter_queue_test.rb
165
+ - test/elibri_api_v1_adapter_test.rb
166
+ - test/helper.rb
167
+ has_rdoc: true
168
+ homepage: http://github.com/elibri/elibri_api_client
169
+ licenses:
170
+ - MIT
171
+ post_install_message:
172
+ rdoc_options: []
173
+
174
+ require_paths:
175
+ - lib
176
+ required_ruby_version: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ hash: 3
182
+ segments:
183
+ - 0
184
+ version: "0"
185
+ required_rubygems_version: !ruby/object:Gem::Requirement
186
+ none: false
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ hash: 3
191
+ segments:
192
+ - 0
193
+ version: "0"
194
+ requirements: []
195
+
196
+ rubyforge_project:
197
+ rubygems_version: 1.6.2
198
+ signing_key:
199
+ specification_version: 3
200
+ summary: API client for elibri.com.pl publishing system
201
+ test_files: []
202
+