dimelo_ccp_api 0.4.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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +15 -0
  5. data/CHANGELOG.md +29 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +59 -0
  9. data/Rakefile +23 -0
  10. data/VERSION +1 -0
  11. data/dimelo_ccp_api.gemspec +26 -0
  12. data/examples/dimelo_api_test +34 -0
  13. data/gemfiles/Gemfile.activesupport-3.2.x +5 -0
  14. data/gemfiles/Gemfile.activesupport-4.0.x +5 -0
  15. data/gemfiles/Gemfile.activesupport-4.1.x +5 -0
  16. data/gemfiles/Gemfile.activesupport-head +5 -0
  17. data/lib/dimelo/ccp/api/basic_object.rb +17 -0
  18. data/lib/dimelo/ccp/api/client.rb +46 -0
  19. data/lib/dimelo/ccp/api/common/openable.rb +23 -0
  20. data/lib/dimelo/ccp/api/common/publishable.rb +23 -0
  21. data/lib/dimelo/ccp/api/common/starrable.rb +23 -0
  22. data/lib/dimelo/ccp/api/connection.rb +86 -0
  23. data/lib/dimelo/ccp/api/error.rb +71 -0
  24. data/lib/dimelo/ccp/api/lazzy_collection.rb +114 -0
  25. data/lib/dimelo/ccp/api/model/answer.rb +39 -0
  26. data/lib/dimelo/ccp/api/model/attachment.rb +44 -0
  27. data/lib/dimelo/ccp/api/model/category.rb +12 -0
  28. data/lib/dimelo/ccp/api/model/category_group.rb +12 -0
  29. data/lib/dimelo/ccp/api/model/feedback.rb +20 -0
  30. data/lib/dimelo/ccp/api/model/feedback_comment.rb +31 -0
  31. data/lib/dimelo/ccp/api/model/membership.rb +12 -0
  32. data/lib/dimelo/ccp/api/model/private_message.rb +8 -0
  33. data/lib/dimelo/ccp/api/model/question.rb +20 -0
  34. data/lib/dimelo/ccp/api/model/role.rb +8 -0
  35. data/lib/dimelo/ccp/api/model/user.rb +37 -0
  36. data/lib/dimelo/ccp/api/model/webhook.rb +9 -0
  37. data/lib/dimelo/ccp/api/model.rb +209 -0
  38. data/lib/dimelo/ccp/api/version.rb +7 -0
  39. data/lib/dimelo/ccp/api.rb +62 -0
  40. data/lib/dimelo_ccp_api.rb +1 -0
  41. data/spec/examples/openable_examples.rb +37 -0
  42. data/spec/examples/starrable_example.rb +57 -0
  43. data/spec/fixtures/files/logo.jpg +0 -0
  44. data/spec/lib/dimelo/ccp/api/client_spec.rb +111 -0
  45. data/spec/lib/dimelo/ccp/api/connection_spec.rb +129 -0
  46. data/spec/lib/dimelo/ccp/api/model/attachment_spec.rb +26 -0
  47. data/spec/lib/dimelo/ccp/api/model/feedback_spec.rb +10 -0
  48. data/spec/lib/dimelo/ccp/api/model/question_spec.rb +10 -0
  49. data/spec/lib/dimelo/ccp/api/model_spec.rb +162 -0
  50. data/spec/spec_helper.rb +7 -0
  51. metadata +174 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4962e1ddaa81b7ef970c119cbd677c7fc3a59fc1
4
+ data.tar.gz: 34a481ab52fcea68f15dce8208b0c77b24ae51f2
5
+ SHA512:
6
+ metadata.gz: 0ed9b3e676d8ad3f03370e33099b8d93e12f04f6768e49fb067db1689073da37db7deed45f0a9e9fd7af7e3fc4d924bf94baefb1dc479c2349b2a3b337efa233
7
+ data.tar.gz: c6db38d30585780a991826a384532b8f89a049ed4c8a5f56cfc507b8034c63f1a61df1aebbc8782fe9efb92a3c0cc92bf4a4366a4931924614eb1bdb8d7698ad
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ .idea
4
+ .rbenv-version
5
+ Gemfile.lock
6
+ pkg/*
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.1
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2
4
+ - 2.1
5
+ - 2.0
6
+ - ruby-head
7
+ - jruby-head
8
+ gemfile:
9
+ - gemfiles/Gemfile.activesupport-3.2.x
10
+ - gemfiles/Gemfile.activesupport-4.0.x
11
+ - gemfiles/Gemfile.activesupport-4.1.x
12
+ - gemfiles/Gemfile.activesupport-edge
13
+ matrix:
14
+ allow_failures:
15
+ - gemfile: gemfiles/Gemfile.activesupport-edge
data/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # 0.3.3
2
+
3
+ Remove warning when attribute is present in api but not defined in model
4
+
5
+ # 0.3.2
6
+
7
+ Implement webhook as a standard resource instead of crappy built-in stuff
8
+
9
+ # 0.3.1
10
+
11
+ Add a feature to push a webhook API configuration through API
12
+
13
+ # 0.2.2
14
+
15
+ Fix handling of validation errors, they now return status 422 and were incorrectely raise.
16
+ This fix restores the previous behaviour: returning a Dimelo record with AR errors.
17
+
18
+ # 0.2.1
19
+
20
+ Attempt to fix segfault in model.rb:116 (#inspect)
21
+ Fix HTTPS spec with github
22
+
23
+ # 0.2.0
24
+
25
+ Added support for Role + upgrade to Rspec 3
26
+
27
+ # 0.0.4
28
+
29
+ Support http_options for Client.new to be able to set dedicated timeout
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in dimelo_api.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2006-2014 Dimelo SA
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.
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # Dimelo CCP API [![Build Status](https://travis-ci.org/dimelo/dimelo_ccp_api.svg?branch=master)](https://travis-ci.org/dimelo/dimelo_ccp_api) [![Code Climate](https://codeclimate.com/github/dimelo/dimelo_ccp_api.png)](https://codeclimate.com/github/dimelo/dimelo_ccp_api)
2
+
3
+ Ruby client for the Dimelo Customer Community Platform
4
+
5
+ This client support most of Dimelo CCP resources, can read and write them, paginates with cursor like interface, supports attachments and supports proper validation and error format.
6
+
7
+ This is heavily used internaly at Dimelo.
8
+
9
+ # Compatibility
10
+
11
+ Compatible and tested with:
12
+
13
+ - Ruby 2.0, 2.1, 2.2 and Jruby-head
14
+ - ActiveSupport 3.0+, 4.0.x and 4.1.x
15
+
16
+
17
+ ## Installation
18
+
19
+ Gemfile:
20
+
21
+ ```ruby
22
+ gem 'dimelo_ccp_api'
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```ruby
28
+ require 'dimelo_ccp_api'
29
+
30
+
31
+ users_client = Dimelo::CCP::API::Client.new('https://domain-test.api.users.dimelo.com/1.0', 'access_token' => ENV['DIMELO_CCP_TOKEN'])
32
+ answers_client = Dimelo::CCP::API::Client.new('https://domain-test.api.answers.dimelo.com/1.0', 'access_token' => ENV['DIMELO_CCP_TOKEN'])
33
+ feedbacks_client = Dimelo::CCP::API::Client.new('https://domain-test.api.ideas.dimelo.com/1.0', 'access_token' => ENV['DIMELO_CCP_TOKEN'])
34
+
35
+ user = Dimelo::CCP::User.find(1, users_client)
36
+ questions = user.questions(answers_client)
37
+ puts "question count: #{questions.count}"
38
+
39
+ questions.each do |question, i|
40
+ answers = question.answers
41
+ puts "#{i} of #{questions.count} => answer count: #{answers.count}"
42
+ answers.each do |answer|
43
+ answer.question_flow_state = "lol"
44
+ end
45
+ end
46
+
47
+ feedbacks = Dimelo::CCP::Feedback.find({ :order => 'updated_at.desc' }, feedbacks_client)
48
+ puts "feedbacks count: #{feedbacks.count}"
49
+ puts "feedbacks not by anonymous and superadmin: #{feedbacks.select{|f| f.user_id.present?}.count}"
50
+
51
+ ```
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it ( http://github.com/dimelo/dimelo_ccp_api/fork )
56
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
57
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
58
+ 4. Push to the branch (`git push origin my-new-feature`)
59
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ begin
5
+ require "spec/rake/spectask" # RSpec 1.3
6
+
7
+ desc 'Run all specs in spec directory.'
8
+ Spec::Rake::SpecTask.new(:spec) do |task|
9
+ task.libs = ['lib', 'spec']
10
+ task.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
12
+ rescue LoadError
13
+ require "rspec/core/rake_task" # RSpec 2.0
14
+
15
+ desc 'Run all specs in spec directory.'
16
+ RSpec::Core::RakeTask.new(:spec) do |t|
17
+ t.rspec_opts = %w{--colour --format progress}
18
+ t.pattern = 'spec/**/*_spec.rb'
19
+ end
20
+ end
21
+
22
+ desc 'Default: runs specs.'
23
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.1
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "dimelo/ccp/api/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dimelo_ccp_api"
7
+ s.version = Dimelo::CCP::API::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Jean Boussier", "Renaud Morvan"]
10
+ s.email = ["jean.boussier@dimelo.com", "nel@w3fu.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Dimelo CCP v2 API client}
13
+ s.description = %q{Rest API client for Dimelo CCP v2 plateform}
14
+
15
+ s.rubyforge_project = "dimelo_ccp_api"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ s.add_dependency('activesupport', '>= 3.0.0')
22
+ s.add_dependency('activemodel', '>= 3.0.0')
23
+ s.add_dependency('faraday')
24
+ s.add_development_dependency('rake')
25
+ s.add_development_dependency('rspec', '~> 3.0')
26
+ end
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
6
+ require 'dimelo_api'
7
+ require 'pp'
8
+
9
+ unless ENV['DIMELO_API_KEY'].present?
10
+ puts "Don't forget to set your api key"
11
+ exit 1
12
+ end
13
+
14
+
15
+ users_client = Dimelo::CCP::API::Client.new('https://domain-test.api.users.dimelo.com/1.0', 'access_token' => ENV['DIMELO_API_KEY'])
16
+ answers_client = Dimelo::CCP::API::Client.new('https://domain-test.api.answers.dimelo.com/1.0', 'access_token' => ENV['DIMELO_API_KEY'])
17
+ feedbacks_client = Dimelo::CCP::API::Client.new('https://domain-test.api.ideas.dimelo.com/1.0', 'access_token' => ENV['DIMELO_API_KEY'])
18
+
19
+ user = Dimelo::API::User.find(1, users_client)
20
+ questions = user.questions(answers_client)
21
+ puts "question count: #{questions.count}"
22
+
23
+ questions.each do |question, i|
24
+ answers = question.answers
25
+ puts "#{i} of #{questions.count} => answer count: #{answers.count}"
26
+ answers.each do |answer|
27
+ answer.question_flow_state = "lol"
28
+ end
29
+ end
30
+
31
+ feedbacks = Dimelo::API::Feedback.find({ :order => 'updated_at.desc' }, feedbacks_client)
32
+ puts "feedbacks count: #{feedbacks.count}"
33
+ puts "feedbacks not by anonymous and superadmin: #{feedbacks.select{|f| f.user_id.present?}.count}"
34
+
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem 'activesupport', '~> 3.2'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem 'activesupport', '~> 4.0'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem 'activesupport', '~> 4.1'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem 'activesupport', github: 'rails/rails'
@@ -0,0 +1,17 @@
1
+ module Dimelo::CCP
2
+ module API
3
+
4
+ class BasicObject
5
+ # http://ruby-doc.org/core-1.9/classes/BasicObject.html
6
+ # http://sequel.heroku.com/2010/03/31/sequelbasicobject-and-ruby-18/
7
+ KEEP_METHODS = %w(__id__ __send__ instance_eval == equal? initialize method_missing respond_to?)
8
+
9
+ def self.remove_methods!
10
+ m = (private_instance_methods + instance_methods) - KEEP_METHODS
11
+ m.each{|m| undef_method(m)}
12
+ end
13
+ remove_methods!
14
+ end if not defined?(BasicObject)
15
+
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ module Dimelo::CCP
2
+ module API
3
+ class Client
4
+
5
+ attr_accessor :base_uri, :default_parameters
6
+
7
+ def initialize(base_uri, options={})
8
+ @base_uri = base_uri.is_a?(URI) ? base_uri : URI.parse(base_uri)
9
+ options = options.with_indifferent_access
10
+ @http_options = options.delete(:http_options) || {}
11
+ @default_parameters = options
12
+ end
13
+
14
+ def check
15
+ Dimelo::CCP::API.decode_json(transport(:get, 'check'))
16
+ end
17
+
18
+ def config
19
+ Dimelo::CCP::API.decode_json(transport(:get, 'config'))
20
+ end
21
+
22
+ def transport(method, path, payload={})
23
+ response = connection.perform(method, path, default_parameters.merge(payload))
24
+
25
+ if response.success? or response.status == 422
26
+ response.body
27
+ else
28
+ raise Error.from(method, path, response.status, response.body)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def request_uri(path, params)
35
+ @base_uri.dup.tap do |uri|
36
+ uri.path = File.join(uri.path, path).chomp('/')
37
+ end.request_uri
38
+ end
39
+
40
+ def connection
41
+ @connection ||= Connection.from_uri(@base_uri, @http_options)
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,23 @@
1
+ module Dimelo::CCP
2
+ module API
3
+ module Common
4
+ module Openable
5
+
6
+ def open
7
+ path = "#{compute_path(attributes)}/open"
8
+ response = client.transport(:put, path)
9
+ self.attributes = Dimelo::CCP::API.decode_json(response)
10
+ errors.empty?
11
+ end
12
+
13
+ def close
14
+ path = "#{compute_path(attributes)}/close"
15
+ response = client.transport(:put, path)
16
+ self.attributes = Dimelo::CCP::API.decode_json(response)
17
+ errors.empty?
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Dimelo::CCP
2
+ module API
3
+ module Common
4
+ module Publishable
5
+
6
+ def publish
7
+ path = "#{compute_path(attributes)}/publish"
8
+ response = client.transport(:put, path)
9
+ self.attributes = Dimelo::CCP::API.decode_json(response)
10
+ errors.empty?
11
+ end
12
+
13
+ def unpublish
14
+ path = "#{compute_path(attributes)}/unpublish"
15
+ response = client.transport(:put, path)
16
+ self.attributes = Dimelo::CCP::API.decode_json(response)
17
+ errors.empty?
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Dimelo::CCP
2
+ module API
3
+ module Common
4
+ module Starrable
5
+
6
+ def star! #use method name with bang to differenciate from #star attribute
7
+ path = "#{compute_path(attributes)}/star"
8
+ response = client.transport(:put, path)
9
+ self.attributes = Dimelo::CCP::API.decode_json(response)
10
+ errors.empty?
11
+ end
12
+
13
+ def unstar!
14
+ path = "#{compute_path(attributes)}/unstar"
15
+ response = client.transport(:put, path)
16
+ self.attributes = Dimelo::CCP::API.decode_json(response)
17
+ errors.empty?
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,86 @@
1
+ require 'net/https'
2
+
3
+ module Dimelo::CCP
4
+ module API
5
+ class Connection
6
+
7
+ class << self
8
+
9
+ def from_uri(uri, options = {})
10
+ options.merge!(:use_ssl => uri.scheme == 'https')
11
+ pool[uri_key(uri)] ||= new(uri.to_s, options)
12
+ end
13
+
14
+ private
15
+
16
+ def uri_key(uri)
17
+ "#{uri.scheme}://#{uri.host}:#{uri.port}"
18
+ end
19
+
20
+ def pool
21
+ @pool ||= {}
22
+ end
23
+
24
+ end
25
+
26
+ def initialize(url, options={})
27
+ @url = url
28
+ @http_options = options
29
+ initialize_client
30
+ end
31
+
32
+ def perform(method, uri, payload={})
33
+ @client.send(method, uri, payload) do |req|
34
+ req.headers[:accept] = 'application/json'
35
+ req.headers[:user_agent] = user_agent
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def timeout
42
+ @http_options[:timeout] || 10
43
+ end
44
+
45
+ def user_agent_details
46
+ strip_non_ascii(@http_options[:user_agent] || '')
47
+ end
48
+
49
+ def strip_non_ascii(setting, replacement = '')
50
+ setting.gsub(/\P{ASCII}/, replacement)
51
+ end
52
+
53
+ def user_agent
54
+ "DimeloAPI/#{Dimelo::CCP::API::VERSION} " \
55
+ << (user_agent_details.present? ? "(#{user_agent_details}) " : '') \
56
+ << "Faraday/#{Faraday::VERSION} " \
57
+ << "Ruby/#{RUBY_VERSION}"
58
+ end
59
+
60
+ def client_options
61
+ {}.tap do |opts|
62
+ opts[:request] = request_options
63
+ opts[:ssl] = ssl_options if @http_options[:use_ssl]
64
+ end
65
+ end
66
+
67
+ def request_options
68
+ { timeout: timeout, open_timeout: timeout }
69
+ end
70
+
71
+ def ssl_options
72
+ { verify_mode: OpenSSL::SSL::VERIFY_NONE, verify_depth: 5 }
73
+ end
74
+
75
+ def initialize_client
76
+ @client = Faraday.new(@url, client_options) do |faraday|
77
+ faraday.request :multipart
78
+ faraday.request :url_encoded
79
+ faraday.adapter Faraday.default_adapter #adapter should be last in the list https://github.com/lostisland/faraday/issues/161
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,71 @@
1
+ module Dimelo::CCP
2
+ module API
3
+ class Error < StandardError
4
+ attr_accessor :original_exception
5
+
6
+ # Public : Return specific Exceptions if defined
7
+ #
8
+ # Returns :
9
+ # - DefinedError descendant exception (DomainNotFoundError,...) if status & name matches declared DefinedError child
10
+ # - BaseError if status and name does not match any declared Error
11
+ # - Error if body cannot be json parsed
12
+ def self.from(method, path, http_status, body)
13
+ json = Dimelo::CCP::API.decode_json(body).symbolize_keys!
14
+ name = json.delete(:error)
15
+ status = json.delete(:status)
16
+ message = json.delete(:message)
17
+
18
+ if klass = DefinedError.descendants.find { |error| error.status == status && error.name == name}
19
+ klass.new
20
+ else
21
+ BaseError.new(name, status, message)
22
+ end
23
+ rescue ActiveSupport::JSON.parse_error
24
+ new("#{method.to_s.upcase} #{path} - #{http_status} #{body}")
25
+ end
26
+ end
27
+
28
+ class BaseError < StandardError
29
+ def initialize(name, status, message='')
30
+ @name = name
31
+ @status = status
32
+ @message = message
33
+ super("error_type:#{name} - status:#{status} - body:#{message}")
34
+ end
35
+ end
36
+
37
+ class DefinedError < StandardError
38
+ class_attribute :status, :name
39
+ end
40
+
41
+ class DomainNotFoundError < DefinedError
42
+ self.status = 404
43
+ self.name = 'domain_not_found'
44
+ end
45
+
46
+ class InvalidAccessTokenError < DefinedError
47
+ self.status = 403
48
+ self.name = 'invalid_access_token'
49
+ end
50
+
51
+ class InvalidUserTypeError < DefinedError #happens only on POST /users
52
+ self.status = 400
53
+ self.name = 'invalid_user_type'
54
+ end
55
+
56
+ class NotEnabledError < DefinedError
57
+ self.status = 403
58
+ self.name = 'api_not_enabled'
59
+ end
60
+
61
+ class NotFoundError < DefinedError
62
+ self.status = 404
63
+ self.name = 'not_found'
64
+ end
65
+
66
+ class SslError < DefinedError
67
+ self.status = 412
68
+ self.name = 'routing_error'
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,114 @@
1
+ module Dimelo::CCP
2
+ module API
3
+
4
+ class LazzyCollection < BasicObject
5
+
6
+ include ::Enumerable
7
+
8
+ class << self
9
+
10
+ def new(params, &block)
11
+ if (params.has_key?(:offset) || params.has_key?('offset'))
12
+ yield params
13
+ else
14
+ instance = super(params, &block)
15
+ instance.paginator.first.is_a?(Enumerable) ? instance : instance.paginator.first
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ attr_reader :paginator
22
+
23
+ def initialize(params={}, &block)
24
+ @paginator = Paginator.new(params, &block)
25
+ end
26
+
27
+ def [](index)
28
+ paginator.get_item_at(index)
29
+ end
30
+
31
+ def to_a
32
+ @to_a ||= each
33
+ end
34
+ alias :to_ary :to_a
35
+
36
+ def respond_to?(method)
37
+ super || Array.public_instance_methods.include?(method.to_s)
38
+ end
39
+
40
+ def each(&block)
41
+ index = 0
42
+ while true
43
+ item = paginator.get_item_at(index)
44
+ break if item.nil?
45
+ yield item if block.present?
46
+ index += 1
47
+ end
48
+ paginator.flatten
49
+ end
50
+
51
+ def inspect
52
+ "<Dimelo::CCP::API::LazzyCollection instance>"
53
+ end
54
+
55
+ protected
56
+
57
+ # /!\ Fetch all API, please avoid this behavior
58
+ def method_missing(name, *args, &block)
59
+ super unless respond_to?(name)
60
+ warn %{WARNING: Method '#{name}` called on LazzyCollection object from #{Kernel.caller.first}.
61
+ All API items might be fetched, so please verify that you are a consenting adult.}
62
+ self.to_a.send(name, *args, &block)
63
+ end
64
+
65
+ def warn(message)
66
+ defined?(Rails) ? Rails.logger.warn(message) : STDERR.puts(message)
67
+ end
68
+
69
+ class Paginator
70
+
71
+ DEFAULT_PAGE_SIZE = 30
72
+
73
+ attr_reader :page_cache
74
+
75
+ def initialize(params, &block)
76
+ @params = params.dup
77
+ @block = block
78
+ @page_cache = []
79
+ end
80
+
81
+ def page_size
82
+ @page_size ||= (@params[:limit] || DEFAULT_PAGE_SIZE).to_i
83
+ end
84
+
85
+ def page_offset(page_index)
86
+ page_index * page_size
87
+ end
88
+
89
+ def [](index)
90
+ page_cache[index.to_i] ||= @block.call(params_for_page(index))
91
+ end
92
+
93
+ def params_for_page(index)
94
+ @params.merge(:offset => page_offset(index.to_i), :limit => page_size)
95
+ end
96
+
97
+ def get_item_at(position)
98
+ self[position / page_size][position % page_size]
99
+ end
100
+
101
+ def flatten
102
+ page_cache.flatten(1)
103
+ end
104
+
105
+ def first
106
+ self[0]
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+
113
+ end
114
+ end