eligible 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/CONTRIBUTORS ADDED
@@ -0,0 +1 @@
1
+ Andy Brett <andy@andybrett.com>
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in eligible.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Eligible
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,126 @@
1
+ # Eligible
2
+
3
+ Ruby bindings for the [Eligible API](https://eligibleapi.com/rest-api-v1)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'eligible'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself with:
16
+
17
+ $ gem install eligible
18
+
19
+ ## Usage
20
+
21
+ ### Setup
22
+ require eligible
23
+ Eligible.api_key = YOUR_KEY
24
+
25
+ ### Retrieve Plan object and query it
26
+ params = {
27
+ :payer_name => "Aetna",
28
+ :payer_id => "000001",
29
+ :service_provider_last_name => "Last",
30
+ :service_provider_first_name => "First",
31
+ :service_provider_NPI => "1928384219",
32
+ :subscriber_id => "W120923801",
33
+ :subscriber_last_name => "Austen",
34
+ :subscriber_first_name => "Jane",
35
+ :subscriber_dob => "1955-12-14"
36
+ }
37
+
38
+ plan = Eligible::Plan.get(params)
39
+ plan.all # returns all fields on the plan, per the plan/all endpoint
40
+ plan.status # returns status fields on the plan, per the plan/status endpoint
41
+ ## Etc.: plan.deductible, plan.dates, plan.balance, plan.stop_loss
42
+
43
+ ### Retrieve Service object and query it
44
+ params = {
45
+ :payer_name => "Aetna",
46
+ :payer_id => "000001",
47
+ :service_provider_last_name => "Last",
48
+ :service_provider_first_name => "First",
49
+ :service_provider_NPI => "1928384219",
50
+ :subscriber_id => "W120923801",
51
+ :subscriber_last_name => "Austen",
52
+ :subscriber_first_name => "Jane",
53
+ :subscriber_dob => "1955-12-14"
54
+ }
55
+
56
+ service = Eligible::Service.get(params)
57
+ service.all # returns all fields for the service, per service/all
58
+ service.visits # returns the visits for the service, per service/visits
59
+ ## Etc.: service.copayment, service.coinsurance, service.deductible
60
+
61
+ ### Retrieve Demographic object and query it
62
+ params = {
63
+ :payer_name => "Aetna",
64
+ :payer_id => "000001",
65
+ :service_provider_last_name => "Last",
66
+ :service_provider_first_name => "First",
67
+ :service_provider_NPI => "1928384219",
68
+ :subscriber_id => "W120923801",
69
+ :subscriber_last_name => "Austen",
70
+ :subscriber_first_name => "Jane",
71
+ :subscriber_dob => "1955-12-14"
72
+ }
73
+
74
+ demographic = Eligible::Demographic.get(params)
75
+ demographic.all # returns all fields for the demographic, per demographic/all
76
+ demographic.zip # returns the patient's zip code, per demographic/zip
77
+ ## Etc.: demographic.employer, demographic.address, demographic.dob
78
+
79
+ ### Retrieve Claim object
80
+
81
+ params = {
82
+ :payer_name => "Aetna",
83
+ :payer_id => "000001",
84
+ :information_receiver_organization_name => "Organization",
85
+ :information_receiver_last_name => "Last",
86
+ :information_receiver_first_name => "First",
87
+ :information_receiver_etin => "1386332",
88
+ :service_provider_organization_name => "Marshall Group",
89
+ :service_provider_last_name => "Last",
90
+ :service_provider_first_name => "First",
91
+ :service_provider_npi => "1928384219",
92
+ :service_provider_tax_id => "1386332",
93
+ :subscriber_id => "W120923801",
94
+ :subscriber_last_name => "Last",
95
+ :subscriber_first_name => "First",
96
+ :subscriber_dob => "1955-12-14",
97
+ :dependent_last_name => "Last",
98
+ :dependent_first_name => "First",
99
+ :dependent_dob => "1975-12-14",
100
+ :dependent_gender => "M",
101
+ :trace_number => "12345",
102
+ :claim_control_number => "67890",
103
+ :claim_charge_amount => "45.00",
104
+ :claim_start_date => "2013-01-05",
105
+ :claim_end_date => "2013-01-05"
106
+ }
107
+
108
+ claim = Eligible::Claim.get(params)
109
+ claim.status # Returns in real time the status (paid, not paid, rejected, denied, etc) of claim specified.
110
+
111
+ ## Tests
112
+
113
+ You can run tests with
114
+
115
+ rake test
116
+
117
+ If you do send a pull request, please add passing tests for the new feature/fix.
118
+
119
+ ## Contributing
120
+
121
+ 1. Fork it
122
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
123
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
124
+ 4. Run tests (see above)
125
+ 5. Push to the branch (`git push origin my-new-feature`)
126
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test)
data/eligible.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'eligible/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "eligible"
8
+ gem.version = Eligible::VERSION
9
+ gem.authors = ["Andy Brett"]
10
+ gem.email = ["andy@andybrett.com"]
11
+ gem.description = 'Eligible is a developer-friendly way to process health care eligibility checks. Learn more at https://eligibleapi.com'
12
+ gem.summary = 'Ruby wrapper for the Eligible API'
13
+ gem.homepage = "https://eligibleapi.com/"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ # gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ gem.test_files = `git ls-files -- test/*`.split("\n")
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_development_dependency('mocha')
22
+ gem.add_development_dependency('shoulda')
23
+ gem.add_development_dependency('test-unit')
24
+ gem.add_development_dependency('rake')
25
+
26
+ gem.add_dependency('rest-client', '~> 1.4')
27
+ gem.add_dependency('multi_json', '>= 1.0.4', '< 2')
28
+ end
data/lib/eligible.rb ADDED
@@ -0,0 +1,215 @@
1
+ require 'cgi'
2
+ require 'set'
3
+ require 'rubygems'
4
+ require 'openssl'
5
+
6
+ gem 'rest-client', '~> 1.4'
7
+ require 'rest_client'
8
+ require 'multi_json'
9
+
10
+ require 'eligible/version'
11
+ require 'eligible/util'
12
+ require 'eligible/json'
13
+ require 'eligible/eligible_object'
14
+ require 'eligible/api_resource'
15
+ require 'eligible/plan'
16
+ require 'eligible/service'
17
+ require 'eligible/demographic'
18
+ require 'eligible/claim'
19
+
20
+ # Errors
21
+ require 'eligible/errors/eligible_error'
22
+ require 'eligible/errors/api_connection_error'
23
+ require 'eligible/errors/authentication_error'
24
+ require 'eligible/errors/api_error'
25
+
26
+ module Eligible
27
+ @@api_key = nil
28
+ @@api_base = "https://v1.eligibleapi.net"
29
+ @@api_version = 1
30
+
31
+ def self.api_url(url='')
32
+ @@api_base + url
33
+ end
34
+
35
+ def self.api_key
36
+ @@api_key
37
+ end
38
+
39
+ def self.api_key=(api_key)
40
+ @@api_key = api_key
41
+ end
42
+
43
+ def self.api_version=(version)
44
+ @@api_version = version
45
+ end
46
+
47
+ def self.api_version
48
+ @@api_version
49
+ end
50
+
51
+ def self.request(method, url, api_key, params={}, headers={})
52
+ api_key ||= @@api_key
53
+ raise AuthenticationError.new('No API key provided. (HINT: set your API key using "Eligible.api_key = <API-KEY>".') unless api_key
54
+
55
+ # if !verify_ssl_certs
56
+ # unless @no_verify
57
+ # $stderr.puts "WARNING: Running without SSL cert verification. Execute 'Eligible.verify_ssl_certs = true' to enable verification."
58
+ # @no_verify = true
59
+ # end
60
+ # ssl_opts = { :verify_ssl => false }
61
+ # elsif !Util.file_readable(@@ssl_bundle_path)
62
+ # unless @no_bundle
63
+ # $stderr.puts "WARNING: Running without SSL cert verification because #{@@ssl_bundle_path} isn't readable"
64
+ # @no_bundle = true
65
+ # end
66
+ # ssl_opts = { :verify_ssl => false }
67
+ # else
68
+ # ssl_opts = {
69
+ # :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
70
+ # :ssl_ca_file => @@ssl_bundle_path
71
+ # }
72
+ # end
73
+ uname = (@@uname ||= RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
74
+ lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
75
+ ua = {
76
+ :bindings_version => Eligible::VERSION,
77
+ :lang => 'ruby',
78
+ :lang_version => lang_version,
79
+ :platform => RUBY_PLATFORM,
80
+ :publisher => 'eligible',
81
+ :uname => uname
82
+ }
83
+
84
+ # params = Util.objects_to_ids(params)
85
+ url = self.api_url(url)
86
+ case method.to_s.downcase.to_sym
87
+ when :get, :head, :delete
88
+ # Make params into GET parameters
89
+ url += "?api_key=#{api_key}"
90
+ if params && params.count > 0
91
+ query_string = Util.flatten_params(params).collect{|key, value| "#{key}=#{Util.url_encode(value)}"}.join('&')
92
+ url += "&#{query_string}"
93
+ end
94
+ payload = nil
95
+ else
96
+ payload = Util.flatten_params(params).collect{|(key, value)| "#{key}=#{Util.url_encode(value)}"}.join('&')
97
+ end
98
+
99
+ begin
100
+ headers = { :x_eligible_client_user_agent => Eligible::JSON.dump(ua) }.merge(headers)
101
+ rescue => e
102
+ headers = {
103
+ :x_eligible_client_raw_user_agent => ua.inspect,
104
+ :error => "#{e} (#{e.class})"
105
+ }.merge(headers)
106
+ end
107
+
108
+ headers = {
109
+ :user_agent => "Eligible/v1 RubyBindings/#{Eligible::VERSION}",
110
+ :authorization => "Bearer #{api_key}",
111
+ :content_type => 'application/x-www-form-urlencoded'
112
+ }.merge(headers)
113
+
114
+ if self.api_version
115
+ headers[:eligible_version] = self.api_version
116
+ end
117
+
118
+ opts = {
119
+ :method => method,
120
+ :url => url,
121
+ :headers => headers,
122
+ :open_timeout => 30,
123
+ :payload => payload,
124
+ :timeout => 80
125
+ }#.merge(ssl_opts)
126
+
127
+ begin
128
+ response = execute_request(opts)
129
+ rescue SocketError => e
130
+ self.handle_restclient_error(e)
131
+ rescue NoMethodError => e
132
+ # Work around RestClient bug
133
+ if e.message =~ /\WRequestFailed\W/
134
+ e = APIConnectionError.new('Unexpected HTTP response code')
135
+ self.handle_restclient_error(e)
136
+ else
137
+ raise
138
+ end
139
+ rescue RestClient::ExceptionWithResponse => e
140
+ if rcode = e.http_code and rbody = e.http_body
141
+ self.handle_api_error(rcode, rbody)
142
+ else
143
+ self.handle_restclient_error(e)
144
+ end
145
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
146
+ self.handle_restclient_error(e)
147
+ end
148
+
149
+ rbody = response.body
150
+ rcode = response.code
151
+ begin
152
+ # Would use :symbolize_names => true, but apparently there is
153
+ # some library out there that makes symbolize_names not work.
154
+ resp = Eligible::JSON.load(rbody)
155
+ rescue MultiJson::DecodeError
156
+ raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
157
+ end
158
+
159
+ resp = Util.symbolize_names(resp)
160
+ [resp, api_key]
161
+ end
162
+
163
+ private
164
+
165
+ def self.execute_request(opts)
166
+ RestClient::Request.execute(opts)
167
+ end
168
+
169
+ def self.handle_api_error(rcode, rbody)
170
+ begin
171
+ error_obj = Eligible::JSON.load(rbody)
172
+ error_obj = Util.symbolize_names(error_obj)
173
+ error = error_obj[:error] or raise EligibleError.new # escape from parsing
174
+ rescue MultiJson::DecodeError, EligibleError
175
+ raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
176
+ end
177
+
178
+ case rcode
179
+ when 400, 404 then
180
+ raise invalid_request_error(error, rcode, rbody, error_obj)
181
+ when 401
182
+ raise authentication_error(error, rcode, rbody, error_obj)
183
+ else
184
+ raise api_error(error, rcode, rbody, error_obj)
185
+ end
186
+ end
187
+
188
+ def self.invalid_request_error(error, rcode, rbody, error_obj)
189
+ InvalidRequestError.new(error[0][:message], error[:param], rcode, rbody, error_obj)
190
+ end
191
+
192
+ def self.authentication_error(error, rcode, rbody, error_obj)
193
+ AuthenticationError.new(error[0][:message], rcode, rbody, error_obj)
194
+ end
195
+
196
+ def self.api_error(error, rcode, rbody, error_obj)
197
+ APIError.new(error[0][:message], rcode, rbody, error_obj)
198
+ end
199
+
200
+ def self.handle_restclient_error(e)
201
+ case e
202
+ when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
203
+ message = "Could not connect to Eligible (#{@@api_base}). Please check your internet connection and try again."
204
+ when RestClient::SSLCertificateNotVerified
205
+ message = "Could not verify Eligible's SSL certificate."
206
+ when SocketError
207
+ message = "Unexpected error communicating when trying to connect to Eligible."
208
+ else
209
+ message = "Unexpected error communicating with Eligible. If this problem persists, let us know at support@eligible.com."
210
+ end
211
+ message += "\n\n(Network error: #{e.message})"
212
+ raise APIConnectionError.new(message)
213
+ end
214
+
215
+ end
@@ -0,0 +1,14 @@
1
+ module Eligible
2
+ class APIResource < EligibleObject
3
+ def self.class_name
4
+ self.name.split('::')[-1]
5
+ end
6
+
7
+ def self.url()
8
+ if self == APIResource
9
+ raise NotImplementedError.new('APIResource is an abstract class. You should perform actions on its subclasses (Plan, Service, etc.)')
10
+ end
11
+ "/#{CGI.escape(class_name.downcase)}/all.json"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ module Eligible
2
+ class Claim < APIResource
3
+ def self.get(params, api_key=nil)
4
+ response, api_key = Eligible.request(:get, url, api_key, params)
5
+ Util.convert_to_eligible_object(response, api_key)
6
+ end
7
+
8
+ def status
9
+ error ? nil : to_hash
10
+ end
11
+ end
12
+ end