blockscore 4.0.0 → 4.1.0

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 (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