daisybill_api 0.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 (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