eligible 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ module Eligible
2
+ class Demographic < APIResource
3
+ COMMON_ATTRIBUTES = [:timestamp, :eligible_id, :mapping_version]
4
+ ZIP_ATTRIBUTES = [:zip]
5
+ EMPLOYER_ATTRIBUTES = [:group_id, :group_name]
6
+ ADDRESS_ATTRIBUTES = [:address]
7
+ DOB_ATTRIBUTES = [:dob]
8
+
9
+ def self.get(params, api_key=nil)
10
+ response, api_key = Eligible.request(:get, url, api_key, params)
11
+ Util.convert_to_eligible_object(response, api_key)
12
+ end
13
+
14
+ def all
15
+ error ? nil : to_hash
16
+ end
17
+
18
+ def zip
19
+ keys = COMMON_ATTRIBUTES
20
+ h = to_hash.select { |k, v| keys.include?(k) }
21
+ h[:zip] = to_hash[:address][:zip]
22
+ error ? nil : h
23
+ end
24
+
25
+ def employer
26
+ keys = COMMON_ATTRIBUTES + EMPLOYER_ATTRIBUTES
27
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
28
+ end
29
+
30
+ def address
31
+ keys = COMMON_ATTRIBUTES + ADDRESS_ATTRIBUTES
32
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
33
+ end
34
+
35
+ def dob
36
+ keys = COMMON_ATTRIBUTES + DOB_ATTRIBUTES
37
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,121 @@
1
+ module Eligible
2
+
3
+ class EligibleObject
4
+ include Enumerable
5
+
6
+ attr_accessor :api_key
7
+ attr_accessor :eligible_id
8
+ @@permanent_attributes = Set.new([:api_key, :error, :balance, :address, :dob])
9
+
10
+ # The default :id method is deprecated and isn't useful to us
11
+ if method_defined?(:id)
12
+ undef :id
13
+ end
14
+
15
+ def initialize(id=nil, api_key=nil)
16
+ @api_key = api_key
17
+ @values = {}
18
+ # This really belongs in APIResource, but not putting it there allows us
19
+ # to have a unified inspect method
20
+ @unsaved_values = Set.new
21
+ @transient_values = Set.new
22
+ self.eligible_id = id if id
23
+ end
24
+
25
+ def self.construct_from(values, api_key=nil)
26
+ obj = self.new(values[:eligible_id], api_key)
27
+ obj.refresh_from(values, api_key)
28
+ obj
29
+ end
30
+
31
+ def refresh_from(values, api_key, partial=false)
32
+ @api_key = api_key
33
+
34
+ removed = partial ? Set.new : Set.new(@values.keys - values.keys)
35
+ added = Set.new(values.keys - @values.keys)
36
+ # Wipe old state before setting new. This is useful for e.g. updating a
37
+ # customer, where there is no persistent card parameter. Mark those values
38
+ # which don't persist as transient
39
+
40
+ instance_eval do
41
+ remove_accessors(removed)
42
+ add_accessors(added)
43
+ end
44
+ removed.each do |k|
45
+ @values.delete(k)
46
+ @transient_values.add(k)
47
+ @unsaved_values.delete(k)
48
+ end
49
+ values.each do |k, v|
50
+ @values[k] = v#Util.convert_to_eligible_object(v, api_key)
51
+ @transient_values.delete(k)
52
+ @unsaved_values.delete(k)
53
+ end
54
+ end
55
+
56
+ def [](k)
57
+ k = k.to_sym if k.kind_of?(String)
58
+ @values[k]
59
+ end
60
+
61
+ def []=(k, v)
62
+ send(:"#{k}=", v)
63
+ end
64
+
65
+ def keys
66
+ @values.keys
67
+ end
68
+
69
+ def values
70
+ @values.values
71
+ end
72
+
73
+ def to_json(*a)
74
+ Eligible::JSON.dump(@values)
75
+ end
76
+
77
+ def to_hash
78
+ @values
79
+ end
80
+
81
+ def each(&blk)
82
+ @values.each(&blk)
83
+ end
84
+
85
+ def error
86
+ keys.include?(:error) ? @values[:error] : nil
87
+ end
88
+
89
+ protected
90
+
91
+ def metaclass
92
+ class << self; self; end
93
+ end
94
+
95
+ def remove_accessors(keys)
96
+ metaclass.instance_eval do
97
+ keys.each do |k|
98
+ next if @@permanent_attributes.include?(k)
99
+ k_eq = :"#{k}="
100
+ remove_method(k) if method_defined?(k)
101
+ remove_method(k_eq) if method_defined?(k_eq)
102
+ end
103
+ end
104
+ end
105
+
106
+ def add_accessors(keys)
107
+ metaclass.instance_eval do
108
+ keys.each do |k|
109
+ next if @@permanent_attributes.include?(k)
110
+ k_eq = :"#{k}="
111
+ define_method(k) { @values[k] }
112
+ define_method(k_eq) do |v|
113
+ @values[k] = v
114
+ @unsaved_values.add(k)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ end
@@ -0,0 +1,4 @@
1
+ module Eligible
2
+ class APIConnectionError < EligibleError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Eligible
2
+ class APIError < EligibleError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Eligible
2
+ class AuthenticationError < EligibleError
3
+ end
4
+ end
@@ -0,0 +1,20 @@
1
+ module Eligible
2
+ class EligibleError < StandardError
3
+ attr_reader :message
4
+ attr_reader :http_status
5
+ attr_reader :http_body
6
+ attr_reader :json_body
7
+
8
+ def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
9
+ @message = message
10
+ @http_status = http_status
11
+ @http_body = http_body
12
+ @json_body = json_body
13
+ end
14
+
15
+ def to_s
16
+ status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
17
+ "#{status_string}#{@message}"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module Eligible
2
+ module JSON
3
+ if MultiJson.respond_to?(:dump)
4
+ def self.dump(*args)
5
+ MultiJson.dump(*args)
6
+ end
7
+
8
+ def self.load(*args)
9
+ MultiJson.load(*args)
10
+ end
11
+ else
12
+ def self.dump(*args)
13
+ MultiJson.encode(*args)
14
+ end
15
+
16
+ def self.load(*args)
17
+ MultiJson.decode(*args)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,42 @@
1
+ module Eligible
2
+ COMMON_ATTRIBUTES = [:timestamp, :eligible_id, :mapping_version, :primary_insurance, :additional_insurance]
3
+ STATUS_ATTRIBUTES = [:type, :coverage_status]
4
+ BALANCE_ATTRIBUTES = [:balance, :comments]
5
+ STOP_LOSS_ATTRIBUTES = [:stop_loss_in_network, :stop_loss_out_network]
6
+
7
+ class Plan < APIResource
8
+ def self.get(params, api_key=nil)
9
+ response, api_key = Eligible.request(:get, url, api_key, params)
10
+ Util.convert_to_eligible_object(response, api_key)
11
+ end
12
+
13
+ def all
14
+ error ? nil : to_hash
15
+ end
16
+
17
+ def status
18
+ keys = COMMON_ATTRIBUTES + STATUS_ATTRIBUTES
19
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
20
+ end
21
+
22
+ def deductible
23
+ keys = COMMON_ATTRIBUTES + STATUS_ATTRIBUTES + [:deductible_in_network, :deductible_out_network]
24
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
25
+ end
26
+
27
+ def dates
28
+ keys = COMMON_ATTRIBUTES
29
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
30
+ end
31
+
32
+ def balance
33
+ keys = COMMON_ATTRIBUTES + BALANCE_ATTRIBUTES
34
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
35
+ end
36
+
37
+ def stop_loss
38
+ keys = COMMON_ATTRIBUTES + STOP_LOSS_ATTRIBUTES
39
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ module Eligible
2
+ class Service < APIResource
3
+ COMMON_ATTRIBUTES = [:timestamp, :eligible_id, :mapping_version, :additional_insurance]
4
+ STATUS_ATTRIBUTES = [:type, :coverage_status]
5
+ VISITS_ATTRIBUTES = [:not_covered, :comments, :precertification_needed, :visits_in_network, :visits_out_network]
6
+ COPAYMENT_ATTRIBUTES = [:copayment_in_network, :copayment_out_network]
7
+ COINSURANCE_ATTRIBUTES = [:coinsurance_in_network, :coinsurance_out_network]
8
+ DEDUCTIBLE_ATTRIBUTES = [:deductible_in_network, :deductible_out_network]
9
+
10
+ def self.get(params, api_key=nil)
11
+ response, api_key = Eligible.request(:get, url, api_key, params)
12
+ Util.convert_to_eligible_object(response, api_key)
13
+ end
14
+
15
+ def all
16
+ error ? nil : to_hash
17
+ end
18
+
19
+ def visits
20
+ keys = COMMON_ATTRIBUTES + STATUS_ATTRIBUTES + VISITS_ATTRIBUTES
21
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
22
+ end
23
+
24
+ def copayment
25
+ keys = COMMON_ATTRIBUTES + STATUS_ATTRIBUTES + COPAYMENT_ATTRIBUTES
26
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
27
+ end
28
+
29
+ def coinsurance
30
+ keys = COMMON_ATTRIBUTES + STATUS_ATTRIBUTES + COINSURANCE_ATTRIBUTES
31
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
32
+ end
33
+
34
+ def deductible
35
+ keys = COMMON_ATTRIBUTES + STATUS_ATTRIBUTES + DEDUCTIBLE_ATTRIBUTES
36
+ error ? nil : to_hash.select { |k, v| keys.include?(k) }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,99 @@
1
+ module Eligible
2
+ module Util
3
+ def self.objects_to_ids(h)
4
+ case h
5
+ when APIResource
6
+ h.id
7
+ when Hash
8
+ res = {}
9
+ h.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
10
+ res
11
+ when Array
12
+ h.map { |v| objects_to_ids(v) }
13
+ else
14
+ h
15
+ end
16
+ end
17
+
18
+ def self.convert_to_eligible_object(resp, api_key)
19
+ types = {
20
+ 'plan' => Plan,
21
+ 'service' => Service,
22
+ 'demographic' => Demographic,
23
+ 'claim' => Claim
24
+ }
25
+ case resp
26
+ when Array
27
+ resp.map { |i| convert_to_eligible_object(i, api_key) }
28
+ when Hash
29
+ # Try converting to a known object class. If none available, fall back to generic APIResource
30
+ if resp[:mapping_version] && klass_name = resp[:mapping_version].match(/^[^\/]*/)[0]
31
+ klass = types[klass_name]
32
+ end
33
+ klass ||= EligibleObject
34
+ klass.construct_from(resp, api_key)
35
+ else
36
+ resp
37
+ end
38
+ end
39
+
40
+ def self.file_readable(file)
41
+ begin
42
+ File.open(file) { |f| }
43
+ rescue
44
+ false
45
+ else
46
+ true
47
+ end
48
+ end
49
+
50
+ def self.symbolize_names(object)
51
+ case object
52
+ when Hash
53
+ new = {}
54
+ object.each do |key, value|
55
+ key = (key.to_sym rescue key) || key
56
+ new[key] = symbolize_names(value)
57
+ end
58
+ new
59
+ when Array
60
+ object.map { |value| symbolize_names(value) }
61
+ else
62
+ object
63
+ end
64
+ end
65
+
66
+ def self.url_encode(key)
67
+ URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
68
+ end
69
+
70
+ def self.flatten_params(params, parent_key=nil)
71
+ result = []
72
+ params.each do |key, value|
73
+ calculated_key = parent_key ? "#{parent_key}[#{url_encode(key)}]" : url_encode(key)
74
+ if value.is_a?(Hash)
75
+ result += flatten_params(value, calculated_key)
76
+ elsif value.is_a?(Array)
77
+ result += flatten_params_array(value, calculated_key)
78
+ else
79
+ result << [calculated_key, value]
80
+ end
81
+ end
82
+ result
83
+ end
84
+
85
+ def self.flatten_params_array(value, calculated_key)
86
+ result = []
87
+ value.each do |elem|
88
+ if elem.is_a?(Hash)
89
+ result += flatten_params(elem, calculated_key)
90
+ elsif elem.is_a?(Array)
91
+ result += flatten_params_array(elem, calculated_key)
92
+ else
93
+ result << ["#{calculated_key}[]", elem]
94
+ end
95
+ end
96
+ result
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ module Eligible
2
+ VERSION = "1.0"
3
+ end
@@ -0,0 +1,279 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'mocha'
5
+ require 'rest-client'
6
+
7
+ class TestEligible < Test::Unit::TestCase
8
+ include Mocha
9
+
10
+ context "Version" do
11
+ should "have a version number" do
12
+ assert_not_nil Eligible::VERSION
13
+ end
14
+ end
15
+
16
+ context "General API" do
17
+ setup do
18
+ Eligible.api_key = "TEST"
19
+ @mock = mock
20
+ Eligible.mock_rest_client = @mock
21
+ end
22
+
23
+ teardown do
24
+ Eligible.mock_rest_client = nil
25
+ Eligible.api_key = nil
26
+ end
27
+
28
+ should "not specifying api credentials should raise an exception" do
29
+ Eligible.api_key = nil
30
+ assert_raises Eligible::AuthenticationError do
31
+ Eligible::Plan.get({})
32
+ end
33
+ end
34
+
35
+ should "specifying invalid api credentials should raise an exception" do
36
+ Eligible.api_key = "invalid"
37
+ response = test_response(test_invalid_api_key_error, 401)
38
+ assert_raises Eligible::AuthenticationError do
39
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
40
+ Eligible::Plan.get({})
41
+ end
42
+ end
43
+ end
44
+
45
+ context "Plan" do
46
+ setup do
47
+ Eligible.api_key = "TEST"
48
+ @mock = mock
49
+ Eligible.mock_rest_client = @mock
50
+ end
51
+
52
+ teardown do
53
+ Eligible.mock_rest_client = nil
54
+ Eligible.api_key = nil
55
+ end
56
+
57
+ should "return an error if no params are supplied" do
58
+ params = {}
59
+ response = test_response(test_plan_missing_params)
60
+ @mock.expects(:get).returns(response)
61
+ plan = Eligible::Plan.get(params)
62
+ assert_not_nil plan.error
63
+ end
64
+
65
+ should "return plan information if valid params are supplied" do
66
+ params = {
67
+ :payer_name => "Aetna",
68
+ :payer_id => "000001",
69
+ :service_provider_last_name => "Last",
70
+ :service_provider_first_name => "First",
71
+ :service_provider_NPI => "1928384219",
72
+ :subscriber_id => "W120923801",
73
+ :subscriber_last_name => "Austen",
74
+ :subscriber_first_name => "Jane",
75
+ :subscriber_dob => "1955-12-14"
76
+ }
77
+ response = test_response(test_plan)
78
+ @mock.expects(:get).returns(response)
79
+ plan = Eligible::Plan.get(params)
80
+ assert_nil plan.error
81
+ assert_not_nil plan.all
82
+ end
83
+
84
+ should "return the right subsets of the data when requested" do
85
+ params = {
86
+ :payer_name => "Aetna",
87
+ :payer_id => "000001",
88
+ :service_provider_last_name => "Last",
89
+ :service_provider_first_name => "First",
90
+ :service_provider_NPI => "1928384219",
91
+ :subscriber_id => "W120923801",
92
+ :subscriber_last_name => "Austen",
93
+ :subscriber_first_name => "Jane",
94
+ :subscriber_dob => "1955-12-14"
95
+ }
96
+ response = test_response(test_plan)
97
+ @mock.expects(:get).returns(response)
98
+ plan = Eligible::Plan.get(params)
99
+
100
+ assert_not_nil plan.all[:primary_insurance]
101
+ assert_not_nil plan.status[:coverage_status]
102
+ assert_nil plan.status[:deductible_in_network]
103
+ assert_not_nil plan.deductible[:deductible_in_network]
104
+ assert_nil plan.deductible[:balance]
105
+ assert_not_nil plan.dates[:primary_insurance][:plan_begins]
106
+ assert_nil plan.dates[:deductible_in_network]
107
+ assert_not_nil plan.balance[:balance]
108
+ assert_nil plan.balance[:deductible_in_network]
109
+ assert_not_nil plan.stop_loss[:stop_loss_in_network]
110
+ assert_nil plan.stop_loss[:deductible_in_network]
111
+ end
112
+ end
113
+
114
+ context "Service" do
115
+ setup do
116
+ Eligible.api_key = "TEST"
117
+ @mock = mock
118
+ Eligible.mock_rest_client = @mock
119
+ end
120
+
121
+ teardown do
122
+ Eligible.mock_rest_client = nil
123
+ Eligible.api_key = nil
124
+ end
125
+
126
+ should "return an error if no params are supplied" do
127
+ params = {}
128
+ response = test_response(test_service_missing_params)
129
+ @mock.expects(:get).returns(response)
130
+ service = Eligible::Service.get(params)
131
+ assert_not_nil service.error
132
+ end
133
+
134
+ should "return eligibility information if valid params are supplied" do
135
+ params = {
136
+ :payer_name => "Aetna",
137
+ :payer_id => "000001",
138
+ :service_provider_last_name => "Last",
139
+ :service_provider_first_name => "First",
140
+ :service_provider_NPI => "1928384219",
141
+ :subscriber_id => "W120923801",
142
+ :subscriber_last_name => "Austen",
143
+ :subscriber_first_name => "Jane",
144
+ :subscriber_dob => "1955-12-14"
145
+ }
146
+ response = test_response(test_service)
147
+ @mock.expects(:get).returns(response)
148
+ service = Eligible::Service.get(params)
149
+ assert_nil service.error
150
+ assert_not_nil service.all
151
+ end
152
+
153
+ should "return the right subsets of the data when requested" do
154
+ params = {
155
+ :payer_name => "Aetna",
156
+ :payer_id => "000001",
157
+ :service_provider_last_name => "Last",
158
+ :service_provider_first_name => "First",
159
+ :service_provider_NPI => "1928384219",
160
+ :subscriber_id => "W120923801",
161
+ :subscriber_last_name => "Austen",
162
+ :subscriber_first_name => "Jane",
163
+ :subscriber_dob => "1955-12-14"
164
+ }
165
+ response = test_response(test_service)
166
+ @mock.expects(:get).returns(response)
167
+ service = Eligible::Service.get(params)
168
+
169
+ assert_not_nil service.all[:service_begins]
170
+ assert_not_nil service.visits[:visits_in_network]
171
+ assert_nil service.visits[:copayment_in_network]
172
+ assert_not_nil service.copayment[:copayment_in_network]
173
+ assert_nil service.copayment[:visits_in_network]
174
+ assert_not_nil service.coinsurance[:coinsurance_in_network]
175
+ assert_nil service.coinsurance[:visits_in_network]
176
+ assert_not_nil service.deductible[:deductible_in_network]
177
+ assert_nil service.deductible[:visits_in_network]
178
+ end
179
+ end
180
+
181
+ context "Demographic" do
182
+ setup do
183
+ Eligible.api_key = "TEST"
184
+ @mock = mock
185
+ Eligible.mock_rest_client = @mock
186
+ end
187
+
188
+ teardown do
189
+ Eligible.mock_rest_client = nil
190
+ Eligible.api_key = nil
191
+ end
192
+
193
+ should "return an error if no params are supplied" do
194
+ params = {}
195
+ response = test_response(test_demographic_missing_params)
196
+ @mock.expects(:get).returns(response)
197
+ demographic = Eligible::Demographic.get(params)
198
+ assert_not_nil demographic.error
199
+ end
200
+
201
+ should "return demographic information if valid params are supplied" do
202
+ params = {
203
+ :payer_name => "Aetna",
204
+ :payer_id => "000001",
205
+ :service_provider_last_name => "Last",
206
+ :service_provider_first_name => "First",
207
+ :service_provider_NPI => "1928384219",
208
+ :subscriber_id => "W120923801",
209
+ :subscriber_last_name => "Austen",
210
+ :subscriber_first_name => "Jane",
211
+ :subscriber_dob => "1955-12-14"
212
+ }
213
+ response = test_response(test_demographic)
214
+ @mock.expects(:get).returns(response)
215
+ demographic = Eligible::Demographic.get(params)
216
+ assert_nil demographic.error
217
+ assert_not_nil demographic.all
218
+ end
219
+
220
+ should "return the right subsets of the data when requested" do
221
+ params = {
222
+ :payer_name => "Aetna",
223
+ :payer_id => "000001",
224
+ :service_provider_last_name => "Last",
225
+ :service_provider_first_name => "First",
226
+ :service_provider_NPI => "1928384219",
227
+ :subscriber_id => "W120923801",
228
+ :subscriber_last_name => "Austen",
229
+ :subscriber_first_name => "Jane",
230
+ :subscriber_dob => "1955-12-14"
231
+ }
232
+ response = test_response(test_demographic)
233
+ @mock.expects(:get).returns(response)
234
+ demographic = Eligible::Demographic.get(params)
235
+
236
+ assert_not_nil demographic.all[:timestamp]
237
+ assert_not_nil demographic.zip[:zip]
238
+ assert_nil demographic.zip[:group_id]
239
+ assert_not_nil demographic.employer[:group_id]
240
+ assert_nil demographic.employer[:zip]
241
+ assert_not_nil demographic.address[:address]
242
+ assert_nil demographic.address[:group_id]
243
+ assert_not_nil demographic.dob[:dob]
244
+ assert_nil demographic.dob[:address]
245
+ end
246
+ end
247
+
248
+ context "Claim" do
249
+ setup do
250
+ Eligible.api_key = "TEST"
251
+ @mock = mock
252
+ Eligible.mock_rest_client = @mock
253
+ end
254
+
255
+ teardown do
256
+ Eligible.mock_rest_client = nil
257
+ Eligible.api_key = nil
258
+ end
259
+
260
+ should "return an error if no params are supplied" do
261
+ params = {}
262
+ response = test_response(test_claim_missing_params)
263
+ @mock.expects(:get).returns(response)
264
+ claim = Eligible::Claim.get(params)
265
+ assert_not_nil claim.error
266
+ end
267
+
268
+ should "return claim information if valid params are supplied" do
269
+ params = {
270
+ :tbd => true
271
+ }
272
+ response = test_response(test_claim)
273
+ @mock.expects(:get).returns(response)
274
+ claim = Eligible::Claim.get(params)
275
+ assert_nil claim.error
276
+ assert_not_nil claim.status
277
+ end
278
+ end
279
+ end