daisybill_api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +20 -0
  4. data/lib/daisybill_api.rb +53 -0
  5. data/lib/daisybill_api/configuration.rb +46 -0
  6. data/lib/daisybill_api/data.rb +7 -0
  7. data/lib/daisybill_api/data/client.rb +66 -0
  8. data/lib/daisybill_api/data/rest_client/payload.rb +30 -0
  9. data/lib/daisybill_api/data/url.rb +26 -0
  10. data/lib/daisybill_api/ext.rb +20 -0
  11. data/lib/daisybill_api/ext/associations.rb +39 -0
  12. data/lib/daisybill_api/ext/attributes.rb +103 -0
  13. data/lib/daisybill_api/ext/attributes/attribute.rb +60 -0
  14. data/lib/daisybill_api/ext/attributes/type_castings.rb +78 -0
  15. data/lib/daisybill_api/ext/crud.rb +147 -0
  16. data/lib/daisybill_api/ext/crud/create.rb +107 -0
  17. data/lib/daisybill_api/ext/crud/destroy.rb +28 -0
  18. data/lib/daisybill_api/ext/crud/index.rb +41 -0
  19. data/lib/daisybill_api/ext/crud/show.rb +22 -0
  20. data/lib/daisybill_api/ext/crud/update.rb +37 -0
  21. data/lib/daisybill_api/ext/links.rb +65 -0
  22. data/lib/daisybill_api/ext/links/link.rb +35 -0
  23. data/lib/daisybill_api/ext/pageable_collection.rb +124 -0
  24. data/lib/daisybill_api/models.rb +22 -0
  25. data/lib/daisybill_api/models/address.rb +16 -0
  26. data/lib/daisybill_api/models/attachment.rb +22 -0
  27. data/lib/daisybill_api/models/base.rb +29 -0
  28. data/lib/daisybill_api/models/bill.rb +42 -0
  29. data/lib/daisybill_api/models/bill_payment.rb +22 -0
  30. data/lib/daisybill_api/models/bill_submission.rb +18 -0
  31. data/lib/daisybill_api/models/billing_provider.rb +25 -0
  32. data/lib/daisybill_api/models/claims_administrator.rb +13 -0
  33. data/lib/daisybill_api/models/contact.rb +19 -0
  34. data/lib/daisybill_api/models/employer.rb +13 -0
  35. data/lib/daisybill_api/models/injury.rb +30 -0
  36. data/lib/daisybill_api/models/patient.rb +32 -0
  37. data/lib/daisybill_api/models/place_of_service.rb +29 -0
  38. data/lib/daisybill_api/models/referring_provider.rb +21 -0
  39. data/lib/daisybill_api/models/rendering_provider.rb +23 -0
  40. data/lib/daisybill_api/models/service_line_item.rb +27 -0
  41. data/lib/daisybill_api/version.rb +3 -0
  42. data/spec/daisybill_api/configuration_spec.rb +16 -0
  43. data/spec/daisybill_api/data/client_spec.rb +47 -0
  44. data/spec/daisybill_api/data/rest_client/payload_spec.rb +17 -0
  45. data/spec/daisybill_api/data/url_spec.rb +13 -0
  46. data/spec/daisybill_api/ext/attributes/attribute_spec.rb +120 -0
  47. data/spec/daisybill_api/ext/attributes/type_castings_spec.rb +50 -0
  48. data/spec/daisybill_api/ext/links/link_spec.rb +50 -0
  49. data/spec/daisybill_api/ext/pageable_collection_spec.rb +145 -0
  50. data/spec/daisybill_api/models/address_spec.rb +7 -0
  51. data/spec/daisybill_api/models/attachment_spec.rb +9 -0
  52. data/spec/daisybill_api/models/bill_payment_spec.rb +11 -0
  53. data/spec/daisybill_api/models/bill_spec.rb +22 -0
  54. data/spec/daisybill_api/models/bill_submission_spec.rb +9 -0
  55. data/spec/daisybill_api/models/billing_provider_spec.rb +13 -0
  56. data/spec/daisybill_api/models/claims_administrator_spec.rb +7 -0
  57. data/spec/daisybill_api/models/contact_spec.rb +6 -0
  58. data/spec/daisybill_api/models/employer_spec.rb +7 -0
  59. data/spec/daisybill_api/models/injury_spec.rb +13 -0
  60. data/spec/daisybill_api/models/patient_spec.rb +14 -0
  61. data/spec/daisybill_api/models/place_of_service_spec.rb +14 -0
  62. data/spec/daisybill_api/models/referring_provider_spec.rb +11 -0
  63. data/spec/daisybill_api/models/rendering_provider_spec.rb +12 -0
  64. data/spec/daisybill_api/models/service_line_item_spec.rb +11 -0
  65. data/spec/daisybill_api_spec.rb +35 -0
  66. metadata +289 -0
@@ -0,0 +1,60 @@
1
+ module DaisybillApi
2
+ module Ext
3
+ # @private
4
+ module Attributes
5
+ class Attribute
6
+ attr_accessor :name, :type, :options
7
+ attr_reader :value
8
+
9
+ def initialize(name, type, options = {})
10
+ @name = name.to_s
11
+ @type = type
12
+ @options = options
13
+ @value = [] if collection?
14
+ end
15
+
16
+ def value=(value)
17
+ @value = TypeCastings.convert_to(value, type)
18
+ end
19
+
20
+ def readonly?
21
+ !!options[:readonly]
22
+ end
23
+
24
+ def param_name
25
+ if collection?
26
+ simple_collection? ? name : "#{name}_attributes"
27
+ else
28
+ simple_type? ? name : "#{name}_attributes"
29
+ end
30
+ end
31
+
32
+ def param_value
33
+ if collection?
34
+ simple_collection? ? value : value.map(&:to_params)
35
+ else
36
+ simple_type? ? value : (value && value.to_params)
37
+ end
38
+ end
39
+
40
+ def to_param
41
+ readonly? ? {} : { param_name => param_value }
42
+ end
43
+
44
+ private
45
+
46
+ def simple_type?
47
+ type.is_a? Symbol
48
+ end
49
+
50
+ def collection?
51
+ type.is_a? Array
52
+ end
53
+
54
+ def simple_collection?
55
+ collection? && type.first.is_a?(Symbol)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,78 @@
1
+ require 'open-uri'
2
+
3
+ module DaisybillApi
4
+ module Ext
5
+ module Attributes
6
+ module TypeCastings
7
+ class << self
8
+ def convert_to(value, type)
9
+ return to_array(value, type.first) if type.is_a? Array
10
+ return if value.nil? || (value.is_a?(String) && value.strip.empty?)
11
+ return to_class(value, type) if type.is_a? Class
12
+ case type
13
+ when :integer
14
+ to_integer(value)
15
+ when :string
16
+ to_string(value)
17
+ when :date
18
+ to_date(value)
19
+ when :datetime
20
+ to_datetime(value)
21
+ when :boolean
22
+ to_boolean(value)
23
+ when :attachment
24
+ to_attachment(value)
25
+ when :float
26
+ to_float(value)
27
+ else
28
+ raise 'Unknown Type'
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def to_date(value)
35
+ return value.to_date if value.respond_to? :to_date
36
+ Date.parse value rescue nil
37
+ end
38
+
39
+ def to_datetime(value)
40
+ return value.to_datetime if value.respond_to? :to_datetime
41
+ DateTime.iso8601 value rescue nil
42
+ end
43
+
44
+ def to_integer(value)
45
+ value.to_i rescue nil
46
+ end
47
+
48
+ def to_string(value)
49
+ value.to_s rescue nil
50
+ end
51
+
52
+ def to_boolean(value)
53
+ !!value
54
+ end
55
+
56
+ def to_float(value)
57
+ value.to_f rescue nil
58
+ end
59
+
60
+ def to_class(attributes, type)
61
+ return attributes if attributes.is_a? type
62
+ type.new attributes rescue nil
63
+ end
64
+
65
+ def to_array(values, type)
66
+ return [] if values.nil?
67
+ values.map { |value| convert_to value, type }
68
+ end
69
+
70
+ def to_attachment(value)
71
+ return open(value) if value.is_a? String
72
+ value if value.is_a?(StringIO) || value.kind_of?(IO)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,147 @@
1
+ require 'daisybill_api/ext/crud/index'
2
+ require 'daisybill_api/ext/crud/create'
3
+ require 'daisybill_api/ext/crud/show'
4
+ require 'daisybill_api/ext/crud/update'
5
+ require 'daisybill_api/ext/crud/destroy'
6
+
7
+ module DaisybillApi
8
+ module Ext
9
+ module CRUD
10
+ # @private
11
+ module ClassMethods
12
+ def rest_actions(*actions)
13
+ actions.each { |action|
14
+ name = action.capitalize
15
+ extend "DaisybillApi::Ext::CRUD::#{name}::ClassMethods".constantize
16
+ include "DaisybillApi::Ext::CRUD::#{name}::InstanceMethods".constantize
17
+ }
18
+ end
19
+
20
+ def path=(value)
21
+ @path = value
22
+ end
23
+
24
+ def path
25
+ @path ||= "/#{plural_key}"
26
+ end
27
+
28
+ def path_prefix(path, property)
29
+ @prefix_path = path
30
+ @prefix_property = property
31
+ attribute property, :integer, readonly: true
32
+ validates property, presence: true, if: :new_record?
33
+ end
34
+
35
+ def path_prefix?
36
+ !!(@prefix_path && @prefix_property)
37
+ end
38
+
39
+ def prefix_path
40
+ @prefix_path
41
+ end
42
+
43
+ def prefix_property
44
+ @prefix_property
45
+ end
46
+
47
+ def plural_key
48
+ @plural_key ||= demodulize self.model_name.plural
49
+ end
50
+
51
+ def singular_key
52
+ @singular_key ||= demodulize self.model_name.singular
53
+ end
54
+
55
+ def show_path(id)
56
+ "#{path}/#{id}"
57
+ end
58
+
59
+ def index_path(prefix_id)
60
+ if path_prefix?
61
+ index_path_with_prefix(prefix_id)
62
+ else
63
+ index_path_without_prefix
64
+ end
65
+ end
66
+
67
+ def client(method, path, params = {})
68
+ DaisybillApi::Data::Client.build method, path, params
69
+ end
70
+
71
+ private
72
+
73
+ PREFIX_LENGTH = 'daisybill_api_models_'.length
74
+
75
+ def demodulize(name)
76
+ name[PREFIX_LENGTH..name.length]
77
+ end
78
+
79
+ def index_path_with_prefix(prefix_id)
80
+ "#{prefix_path}/#{prefix_id}#{path}"
81
+ end
82
+
83
+ def index_path_without_prefix
84
+ path
85
+ end
86
+ end
87
+
88
+ # @private
89
+ module InstanceMethods
90
+ def new_record?
91
+ id.nil?
92
+ end
93
+
94
+ def destroyed?
95
+ !!@destroyed
96
+ end
97
+
98
+ protected
99
+
100
+ def index_path
101
+ params = self.class.path_prefix? ? [self.send(self.class.prefix_property)] : []
102
+ self.class.index_path *params
103
+ end
104
+
105
+ def show_path
106
+ self.class.show_path(id)
107
+ end
108
+
109
+ def data
110
+ { self.class.singular_key => to_params }
111
+ end
112
+
113
+ private
114
+
115
+ def client(method, path, params = {})
116
+ self.class.client method, path, params
117
+ end
118
+
119
+ def send_data(method, url)
120
+ self.external_errors = nil
121
+ valid? && process_response(client method, url, data)
122
+ end
123
+
124
+ def process_response(client)
125
+ if client.success?
126
+ response = client.response.clone
127
+ self.links = response.delete 'links'
128
+ self.attributes = response
129
+ true
130
+ elsif client.bad_request?
131
+ self.external_errors = client.response['errors']
132
+ self.valid?
133
+ else
134
+ false
135
+ end
136
+ end
137
+ end
138
+
139
+ def self.included(base)
140
+ base.class_eval do
141
+ include DaisybillApi::Ext::CRUD::InstanceMethods
142
+ extend DaisybillApi::Ext::CRUD::ClassMethods
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,107 @@
1
+ module DaisybillApi
2
+ module Ext
3
+ module CRUD
4
+ module Create
5
+ module ClassMethods
6
+ # === Creating a record
7
+ #
8
+ # DaisybillApi::Models::Patient.create(
9
+ # first_name: "Johnny",
10
+ # last_name: "Smith",
11
+ # billing_provider_id: 14
12
+ # )
13
+ # # => <DaisybillApi::Models::Patient billing_provider_id: 14 ...>
14
+ #
15
+ #
16
+ # === Creating a Bill
17
+ #
18
+ # bp = DaisybillApi::Models::BillingProvider.find(14)
19
+ # # => <DaisybillApi::Models::BillingProvider id: 14...>
20
+ # pos = bp.places_of_service.first
21
+ # # => <DaisybillApi::Models::PlaceOfService billing_provider_id: 14...>
22
+ # rp = bp.rendering_providers.first
23
+ # # => <DaisybillApi::Models::RenderingProvider billing_provider_id: 14...>
24
+ #
25
+ # sli = DaisybillApi::Models::ServiceLineItem.new(
26
+ # procedure_code: "S9981",
27
+ # units: 1,
28
+ # modifier_codes: ["93", "17"],
29
+ # diagnosis_code_1: "72700",
30
+ # custom_unit_charge_cents: 12345
31
+ # )
32
+ # # => <DaisybillApi::Models::ServiceLineItem...>
33
+ #
34
+ # bill = DaisybillApi::Models::Bill.new(
35
+ # injury_id: 345,
36
+ # date_of_service: '2015-01-01',
37
+ # rendering_provider_id: rp.id,
38
+ # place_of_service_id: pos.id,
39
+ # diagnosis_codes: ["72700"],
40
+ # diagnosis_type: "Icd10",
41
+ # service_line_items: [sli]
42
+ # )
43
+ # # => <DaisybillApi::Models::Bill...>
44
+ #
45
+ # bill.save
46
+ # # => true
47
+ #
48
+ # @param attributes [Hash]
49
+ # @return [Object]
50
+ def create(attributes = {})
51
+ instance = new(attributes)
52
+ instance.create
53
+ instance
54
+ end
55
+ end
56
+
57
+ module InstanceMethods
58
+ # == Creating or Updating a record
59
+ #
60
+ # === Creating a record
61
+ #
62
+ # pt = DaisyBillApi::Models::Patient.new(
63
+ # billing_provider_id: 14,
64
+ # first_name: "Johnny",
65
+ # last_name: "Smith",
66
+ # ssn: "123456789",
67
+ # gender: "Male"
68
+ # )
69
+ #
70
+ # pt.save # => true
71
+ #
72
+ # === Updating a record
73
+ #
74
+ # pt = DaisybillApi::Models::Patient.find(12) # => <DaisybillApi::Models::Patient id: 12...>
75
+ #
76
+ # pt.telephone # => "2138888888"
77
+ #
78
+ # pt.telephone = "2137777777" # => "2137777777"
79
+ #
80
+ # pt.telephone # => "2137777777"
81
+ #
82
+ # pt.save # => true
83
+ #
84
+ # === Inspecting errors
85
+ #
86
+ # pt = DaisybillApi::Models::Patient.new(billing_provider_id: 14, first_name: "Johnny")
87
+ #
88
+ # pt.save # => false
89
+ #
90
+ # pt.errors.full_messages # => ["Last name can't be blank"]
91
+ def save
92
+ return create if new_record?
93
+ return update if respond_to? :update
94
+ message = '#save method is not supported for saved record'
95
+ DaisybillApi.logger.error message
96
+ raise NotImplementedError.new message
97
+ end
98
+
99
+ # @private
100
+ def create
101
+ send_data :post, index_path
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,28 @@
1
+ module DaisybillApi
2
+ module Ext
3
+ module CRUD
4
+ module Destroy
5
+ # @private
6
+ module ClassMethods; end
7
+
8
+ module InstanceMethods
9
+ # === Deleting records
10
+ #
11
+ # bill = DaisybillApi::Models::Bill.find(12) # => <DaisybillApi::Models::Bill id: 12>
12
+ # bill.destroy # => true
13
+ # DaisybillApi::Models::Bill.find(12) # => nil
14
+ #
15
+ # Some records will not be deletable depending on their conditions:
16
+ #
17
+ # bill = DaisybillApi::Models::Bill.find(1) # => <DaisybillApi::Models::Bill id: 1...>
18
+ # bill.status # => "done"
19
+ # bill.destroy # => false
20
+ # bill.errors.full_messages # => ["Bill is not deletable"]
21
+ def destroy
22
+ @destroyed = send_data :delete, show_path
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,41 @@
1
+ module DaisybillApi
2
+ module Ext
3
+ module CRUD
4
+ module Index
5
+ module ClassMethods
6
+ # Retrieves a list of records
7
+ #
8
+ # @patients = DaisybillApi::Models::Patient.all(billing_provider_id: 2)
9
+ # # => #<DaisybillApi::Ext::PageableCollection>
10
+ #
11
+ # @param [Hash] options
12
+ # @return [Object] a {DaisybillApi::Ext::PageableCollection PageableCollection} that includes Enumerable
13
+ def all(options = {})
14
+ id = options[@prefix_property]
15
+ c = client(:get, index_path(id), options)
16
+
17
+ if c.success?
18
+ DaisybillApi::Ext::PageableCollection.new(
19
+ c.response[plural_key.to_s].map { |attributes|
20
+ instance = new(attributes)
21
+ instance.send("#{prefix_property}=", id) if path_prefix?
22
+ instance
23
+ },
24
+ {
25
+ headers: c.headers,
26
+ params: options,
27
+ resource_class: self
28
+ }
29
+ )
30
+ else
31
+ DaisybillApi::Ext::PageableCollection.new([])
32
+ end
33
+ end
34
+ end
35
+
36
+ # @private
37
+ module InstanceMethods; end
38
+ end
39
+ end
40
+ end
41
+ end