blockscore 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +58 -0
  3. data/.hound.yml +217 -0
  4. data/Gemfile +19 -7
  5. data/LICENSE +21 -0
  6. data/README.md +37 -33
  7. data/Rakefile +1 -15
  8. data/blockscore.gemspec +30 -81
  9. data/circle.yml +13 -0
  10. data/lib/blockscore.rb +43 -4
  11. data/lib/blockscore/actions/all.rb +27 -0
  12. data/lib/blockscore/actions/create.rb +34 -0
  13. data/lib/blockscore/actions/delete.rb +31 -0
  14. data/lib/blockscore/actions/retrieve.rb +24 -0
  15. data/lib/blockscore/actions/update.rb +46 -0
  16. data/lib/blockscore/base.rb +117 -0
  17. data/lib/blockscore/candidate.rb +32 -0
  18. data/lib/blockscore/collection.rb +12 -0
  19. data/lib/blockscore/company.rb +7 -0
  20. data/lib/blockscore/connection.rb +65 -0
  21. data/lib/blockscore/dispatch.rb +26 -0
  22. data/lib/blockscore/errors/api_connection_error.rb +6 -0
  23. data/lib/blockscore/errors/api_error.rb +33 -0
  24. data/lib/blockscore/errors/authentication_error.rb +4 -0
  25. data/lib/blockscore/errors/error.rb +4 -0
  26. data/lib/blockscore/errors/invalid_request_error.rb +29 -0
  27. data/lib/blockscore/errors/no_api_key_error.rb +4 -0
  28. data/lib/blockscore/errors/not_found_error.rb +4 -0
  29. data/lib/blockscore/fingerprint.rb +46 -0
  30. data/lib/blockscore/person.rb +14 -0
  31. data/lib/blockscore/question_set.rb +26 -0
  32. data/lib/blockscore/response.rb +29 -0
  33. data/lib/blockscore/util.rb +80 -0
  34. data/lib/blockscore/version.rb +3 -0
  35. data/lib/blockscore/watchlist_hit.rb +4 -0
  36. metadata +82 -48
  37. data/LICENSE.txt +0 -20
  38. data/VERSION +0 -1
  39. data/blockscore-ruby.sublime-project +0 -21
  40. data/lib/blockscore/candidates.rb +0 -49
  41. data/lib/blockscore/client.rb +0 -81
  42. data/lib/blockscore/companies.rb +0 -36
  43. data/lib/blockscore/error/authorization_error.rb +0 -13
  44. data/lib/blockscore/error/blockscore_error.rb +0 -26
  45. data/lib/blockscore/error/error_handler.rb +0 -141
  46. data/lib/blockscore/error/internal_server_error.rb +0 -19
  47. data/lib/blockscore/error/not_found_error.rb +0 -12
  48. data/lib/blockscore/error/parameter_error.rb +0 -12
  49. data/lib/blockscore/error/validation_error.rb +0 -28
  50. data/lib/blockscore/errors.rb +0 -3
  51. data/lib/blockscore/people.rb +0 -37
  52. data/lib/blockscore/question_sets.rb +0 -49
  53. data/lib/blockscore/watchlists.rb +0 -18
  54. data/test/helper.rb +0 -37
  55. data/test/test_blockscore.rb +0 -226
@@ -0,0 +1,32 @@
1
+ module BlockScore
2
+ class Candidate < BlockScore::Base
3
+ extend Forwardable
4
+
5
+ include BlockScore::Actions::Create
6
+ include BlockScore::Actions::Retrieve
7
+ include BlockScore::Actions::Update
8
+ include BlockScore::Actions::Delete
9
+ include BlockScore::Actions::All
10
+
11
+ def_delegators 'self.class', :api_url, :endpoint, :get, :post
12
+
13
+ def history
14
+ resource_member 'history'
15
+ end
16
+
17
+ def hits
18
+ resource_member 'hits'
19
+ end
20
+
21
+ def search(options = {})
22
+ options[:candidate_id] = id
23
+ post "#{api_url}watchlists", options
24
+ end
25
+
26
+ private
27
+
28
+ def resource_member(member)
29
+ get "#{endpoint}/#{id}/#{member}", {}
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ module BlockScore
2
+ class Collection < Array
3
+ def initialize(target)
4
+ super()
5
+ @target = target
6
+ end
7
+
8
+ def method_missing(method, *args, &block)
9
+ @target.public_send(method, *args, &block)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module BlockScore
2
+ class Company < Base
3
+ include BlockScore::Actions::Create
4
+ include BlockScore::Actions::Retrieve
5
+ include BlockScore::Actions::All
6
+ end
7
+ end
@@ -0,0 +1,65 @@
1
+ module BlockScore
2
+ module Connection
3
+ def get(path, params)
4
+ request :get, path, params
5
+ end
6
+
7
+ def post(path, params)
8
+ request(:post, path, params)
9
+ end
10
+
11
+ def patch(path, params)
12
+ request(:patch, path, params)
13
+ end
14
+
15
+ def put(path, params)
16
+ request(:put, path, params)
17
+ end
18
+
19
+ def delete(path, params)
20
+ request(:delete, path, params)
21
+ end
22
+
23
+ private
24
+
25
+ def headers
26
+ @headers ||= {
27
+ 'Accept' => "application/vnd.blockscore+json;version=#{BlockScore::VERSION.to_i}",
28
+ 'User-Agent' => "blockscore-ruby/#{BlockScore::VERSION} (https://github.com/BlockScore/blockscore-ruby)",
29
+ 'Content-Type' => 'application/json'
30
+ }
31
+ end
32
+
33
+ def request(method, path, params)
34
+ fail NoAPIKeyError, 'No API key was provided.' unless BlockScore.api_key
35
+
36
+ begin
37
+ response = execute_request(method, path, params)
38
+ rescue SocketError, Errno::ECONNREFUSED => e
39
+ fail APIConnectionError, e.message
40
+ end
41
+
42
+ Response.handle_response(resource, response)
43
+ end
44
+
45
+ def execute_request(method, path, params)
46
+ auth = { :username => BlockScore.api_key, :password => '' }
47
+
48
+ if method == :get
49
+ path = encode_path_params(path, params)
50
+ params = nil
51
+ else
52
+ params = params.to_json
53
+ end
54
+
55
+ options = { :basic_auth => auth, :headers => headers, :body => params }
56
+
57
+ HTTParty.send(method, path, options)
58
+ end
59
+
60
+ def encode_path_params(path, params)
61
+ encoded = URI.encode_www_form(params)
62
+ [path, encoded].join("?")
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,26 @@
1
+ module BlockScore
2
+ class Dispatch
3
+ extend Forwardable
4
+
5
+ def_delegators :@fingerprint, :builder, :data, :resource
6
+
7
+ def initialize(resource, response)
8
+ @fingerprint = Fingerprint.new(resource, response.body)
9
+ end
10
+
11
+ def call
12
+ Util.send(builder, resource, data)
13
+ end
14
+
15
+ private
16
+
17
+ def builder
18
+ resource_array? ? :create_array : :create_object
19
+ end
20
+
21
+ # array formatted response
22
+ def resource_array?
23
+ data.is_a?(Array)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,6 @@
1
+ require 'blockscore/errors/error'
2
+
3
+ module BlockScore
4
+ class APIConnectionError < Error
5
+ end
6
+ end
@@ -0,0 +1,33 @@
1
+ module BlockScore
2
+ class APIError < Error
3
+ attr_reader :http_status
4
+ attr_reader :error_type
5
+ attr_reader :http_body
6
+
7
+ # Public: Creates a new instance of BlockScore::Error.
8
+ #
9
+ # rbody - The HTTP response body from HTTParty.
10
+ # rcode - The HTTP response code from HTTParty.
11
+ #
12
+ # While BlockScore::Error can be instantiated, the more meaningful
13
+ # error classes are its subclasses:
14
+ # InvalidRequestError - Indicates a malformed request (HTTP 400 or 404)
15
+ # APIError - Indicates an error on the server side (HTTP 5xx)
16
+ # AuthenticationError - Indicates an authentication error (HTTP 401)
17
+ def initialize(response)
18
+ body = JSON.parse(response.body, :symbolize_names => true)
19
+
20
+ @message = body[:error][:message]
21
+ @http_status = response.code
22
+ @error_type = body[:error][:type]
23
+ @http_body = body
24
+ end
25
+
26
+ def to_s
27
+ status_string = @http_status ? "(Status: #{@http_status})" : ""
28
+ type_string = @error_type ? "(Type: #{@error_type})" : ""
29
+
30
+ "#{type_string} #{@message} #{status_string}"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,4 @@
1
+ module BlockScore
2
+ class AuthenticationError < APIError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module BlockScore
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,29 @@
1
+ module BlockScore
2
+ class InvalidRequestError < APIError
3
+ attr_accessor :param
4
+
5
+ # Public: Creates a new instance of BlockScore::InvalidRequestError.
6
+ #
7
+ # responses - The HTTP response body from HTTParty.
8
+ #
9
+ # Examples
10
+ #
11
+ # begin
12
+ # response = BlockScore::Person.create(...)
13
+ # rescue BlockScore::InvalidRequestError => e
14
+ # puts "ERROR: #{e.message} with code #{e.http_status}"
15
+ # end
16
+ def initialize(response)
17
+ super
18
+ @param = @http_body[:error][:param]
19
+ end
20
+
21
+ def to_s
22
+ status_string = @http_status ? "(Status: #{@http_status})" : ""
23
+ type_string = @error_type ? "(Type: #{@error_type})" : ""
24
+ param_string = @param ? "(#{@param})" : ""
25
+
26
+ "#{type_string} #{@message} #{param_string} #{status_string}"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ module BlockScore
2
+ class NoAPIKeyError < Error
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module BlockScore
2
+ class NotFoundError < APIError
3
+ end
4
+ end
@@ -0,0 +1,46 @@
1
+ module BlockScore
2
+ class Fingerprint
3
+ def initialize(resource, body)
4
+ @resource = resource
5
+ @body = Util.parse_json(body)
6
+ end
7
+
8
+ def data
9
+ @data ||= begin
10
+ if watchlist_search?
11
+ body[:matches]
12
+ elsif resource_index?
13
+ body[:data]
14
+ else
15
+ body
16
+ end
17
+ end
18
+ end
19
+
20
+ def resource
21
+ if watchlist_search? || watchlist_hits?
22
+ 'watchlist_hit'
23
+ else
24
+ @resource
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :body
31
+
32
+ # candidates#search endpoint
33
+ def watchlist_search?
34
+ body.respond_to?(:key?) && body.key?(:matches)
35
+ end
36
+
37
+ # hash style list format
38
+ def resource_index?
39
+ body.is_a?(Hash) && body[:object] == 'list'
40
+ end
41
+
42
+ def watchlist_hits?
43
+ data.first.is_a?(Hash) && data.first.key?(:matching_info)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,14 @@
1
+ module BlockScore
2
+ class Person < Base
3
+ include BlockScore::Actions::Create
4
+ include BlockScore::Actions::Retrieve
5
+ include BlockScore::Actions::All
6
+
7
+ attr_reader :question_sets
8
+
9
+ def initialize(options = {})
10
+ super
11
+ @question_sets = Collection.new(QuestionSet.new(:person => self))
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ module BlockScore
2
+ class QuestionSet < Base
3
+ extend Forwardable
4
+
5
+ include BlockScore::Actions::Create
6
+ include BlockScore::Actions::Retrieve
7
+ include BlockScore::Actions::All
8
+
9
+ def_delegators 'self.class', :retrieve, :all, :post, :endpoint
10
+
11
+ def create
12
+ result = self.class.create(:person_id => person.id)
13
+ person.question_sets << result.id
14
+
15
+ result
16
+ end
17
+
18
+ def score(answers = nil)
19
+ if answers.nil? && attributes
20
+ attributes[:score]
21
+ else
22
+ post "#{endpoint}/#{id}/score", :answers => answers
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ module BlockScore
2
+ module Response
3
+ extend self
4
+
5
+ def handle_response(resource, response)
6
+ case response.code
7
+ when 200, 201
8
+ Dispatch.new(resource, response).call
9
+ else
10
+ api_error(response)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def api_error(response)
17
+ case response.code
18
+ when 400
19
+ fail InvalidRequestError.new(response)
20
+ when 401
21
+ fail AuthenticationError.new(response)
22
+ when 404
23
+ fail NotFoundError.new(response)
24
+ else
25
+ fail APIError.new(response)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,80 @@
1
+ module BlockScore
2
+ module Util
3
+ extend self
4
+
5
+ def parse_json!(json_obj)
6
+ JSON.parse(json_obj, :symbolize_names => true)
7
+ end
8
+
9
+ def parse_json(json_obj)
10
+ parse_json! json_obj
11
+ rescue JSON::ParserError
12
+ fail Error, "An error has occurred. If this problem persists, please message support@blockscore.com."
13
+ end
14
+
15
+ def create_object(resource, options = {})
16
+ to_constant("BlockScore::#{to_camelcase(resource)}").new(options)
17
+ end
18
+
19
+ def create_array(resource, arr)
20
+ arr.map { |obj| create_object(resource, obj) }
21
+ end
22
+
23
+ def to_plural(str)
24
+ if str.end_with? 'y'
25
+ str[0..-2] + 'ies'
26
+ elsif str.end_with? 'h'
27
+ str + 'es'
28
+ elsif str == 'person'
29
+ 'people'
30
+ else
31
+ str + 's'
32
+ end
33
+ end
34
+
35
+ # Taken from activesupport: http://git.io/vkWtR
36
+ def to_constant(camel_cased_word)
37
+ names = camel_cased_word.split('::')
38
+
39
+ # Trigger a built-in NameError exception including the ill-formed constant in the message.
40
+ Object.const_get(camel_cased_word) if names.empty?
41
+
42
+ # Remove the first blank element in case of '::ClassName' notation.
43
+ names.shift if names.size > 1 && names.first.empty?
44
+
45
+ names.inject(Object) do |constant, name|
46
+ if constant == Object
47
+ constant.const_get(name)
48
+ else
49
+ candidate = constant.const_get(name)
50
+ next candidate if constant.const_defined?(name, false)
51
+ next candidate unless Object.const_defined?(name)
52
+
53
+ # Go down the ancestors to check if it is owned directly. The check
54
+ # stops when we reach Object or the end of ancestors tree.
55
+ constant = constant.ancestors.inject do |const, ancestor|
56
+ break const if ancestor == Object
57
+ break ancestor if ancestor.const_defined?(name, false)
58
+ const
59
+ end
60
+
61
+ # owner is in Object, so raise
62
+ constant.const_get(name, false)
63
+ end
64
+ end
65
+ end
66
+
67
+ def to_camelcase(str)
68
+ str.split('_').map { |i| i.capitalize }.join('')
69
+ end
70
+
71
+ # Taken from Rulers: http://git.io/vkWqf
72
+ def to_underscore(str)
73
+ str.gsub(/::/, '/').
74
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
75
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
76
+ tr("-", "_").
77
+ downcase
78
+ end
79
+ end
80
+ end