elibri_api_client 1.0.1

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