elmas 1.0.0 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31ec8dd25ceb6a01b7d07b240c16b8d33616cb33
4
- data.tar.gz: 6877c0e58d2ba5c5ff77eba97a599e5ecf220ef7
3
+ metadata.gz: 4405c4e143b2a041bd2b2bda762788051e196143
4
+ data.tar.gz: 79f57ea2506232679f28c47c531041415f823765
5
5
  SHA512:
6
- metadata.gz: 2c9aecd1f8e78b21621aec927ea74ece5e3d2e55780e5b650eb4e2278a960a543d5a83c18148117156ee07e4ab717ba48708d299db33c61d543d7e1220e6e9cc
7
- data.tar.gz: e7047f922618c9efaf666e6ad2dd46a80381349c0fd9c038d25cfd462fdf076e736fea865d5abfac2684f60f5ebd44353ce2acc0ee736dc9eed6b7a0105ab7ee
6
+ metadata.gz: f2df0a83caef1ff642f07b7afa9fcdb2ae70c7d25f8f848bdcefe0f405d4b7ca0c22b92e9fdb11aa7b9ebc2f424fc75a92cbd709b4ab84e5033fcda54b102963
7
+ data.tar.gz: ab9a2b334311157f842e3a62e2effe555df9d1c6a860c38e48244f1a8c50465cf3a8e8c0c1cfa9da3f4b76edec390fce4f478b978a337ea8f02d0684cee719ef
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
  .env
12
12
  .log
13
13
  *.gem
14
+ *.swp
data/Changelog.md ADDED
@@ -0,0 +1,14 @@
1
+ 1.0.0
2
+
3
+ Use 'proper' date format for ExactOnline
4
+ Don't add quotes to integers when filtering
5
+ Raise error on invalid request
6
+ Add project and time tracking functionality
7
+
8
+ 0.2.0
9
+
10
+ First official release, used within Hoppinger, basic functionality and resources Hoppinger needed.
11
+
12
+ 0.1.0
13
+
14
+ Dev release
data/README.md CHANGED
@@ -82,79 +82,75 @@ unless Elmas.authorized?
82
82
  end
83
83
  ```
84
84
 
85
- ## GET, PUT, POST requests
85
+ ## Accessing the API
86
86
 
87
- For any of the following requests (GET, PUT, POST), to get the results do the following. (the exact API always returns a List)
88
- ```ruby
89
- contact = Elmas::Contact.new(id: "23445")
90
- response = contact.find
91
- puts response.first
92
- # Prints something like this
93
- # [<Elmas::Contact:0x007fb0a55782f0 @attributes={:__metadata=>{"uri"=>"https://start.exactonline.nl/api/v1/797636/crm/Contacts(guid'23445')", "type"=>"Exact.Web.Api.Models.Contact"}, :id=>"23445", :person=>"88380fa4-97bc-4ddf-90e3-b0e7befb112c"}>
94
-
95
- contact = Elmas::Contact.new.find_all
96
- response = contact.find_all
97
- puts response.results
98
- # Prints something like this
99
- # [<Elmas::Contact:0x007fb0a55782f0 @attributes={:__metadata=>{"uri"=>"https://start.exactonline.nl/api/v1/797636/crm/Contacts(guid'23445')", "type"=>"Exact.Web.Api.Models.Contact"}, :id=>"23445", :person=>"88380fa4-97bc-4ddf-90e3-b0e7befb112c"}>, <Elmas::Contact:0x007fb0a55782f0 @attributes={:__metadata=>{"uri"=>"https://start.exactonline.nl/api/v1/797636/crm/Contacts(guid'23445')", "type"=>"Exact.Web.Api.Models.Contact"}, :id=>"23445", :person=>"88380fa4-97bc-4ddf-90e3-b0e7befb112c"}>]
100
- ```
87
+ We can retrieve data from the API using the following syntax.
101
88
 
102
- To find a contact
89
+ The query will return an `Elmas::ResultSet` which contains up to 60 records and
90
+ a method to retrieve the next page of results. Unfortunately the ExactOnline API
91
+ does not allow us to retrieve a specific page or define how many records we want
92
+ to retrieve at a time.
103
93
 
104
94
  ```ruby
105
- contact = Elmas::Contact.new(id: "23445")
106
- contact.find
107
- # path = /crm/Contacts(guid'23445')
108
- ```
95
+ # Query the API and return an Elmas::ResultSet
96
+ accounts = Elmas::Account.new.find_all
109
97
 
110
- To find a contact with specific filters
111
- ```ruby
112
- contact = Elmas::Contact.new(first_name: "Karel", id: "23")
113
- contact.find_by(filters: [:first_name])
114
- # path = /crm/Contacts?$filter=first_name eq 'Karel'
98
+ # Return an array of accounts
99
+ accounts.records
115
100
  ```
116
101
 
117
- To find contacts with an order and a filter
102
+ If the query results in more than 60 records the next set can be retrieved using
103
+ the `next_page` method.
104
+
118
105
  ```ruby
119
- contact = Elmas::Contact.new(first_name: "Karel")
120
- contact.find_by(filters: [:first_name], order_by: :first_name)
121
- # path = /crm/Contacts?$order_by=first_name&$filter=first_name eq 'Karel'
106
+ # Return an Elmas::ResultSet containing the next page's records
107
+ accounts.next_page
122
108
  ```
123
109
 
124
- To find contacts with an order, a filter and selecting relationships
110
+ ### Filter results
111
+
112
+ Filtering result sets can be done by adding attributes to the initializer and then
113
+ using `find_by`. Filters accept a single value or an array of values.
114
+
115
+ So far only 'eq' has been implemented, so only direct matches will be returned.
116
+
125
117
  ```ruby
126
- contact = Elmas::Contact.new(first_name: "Karel")
127
- contact.find_by(filters: [:first_name], order_by: :first_name, select: [:last_name])
128
- # path = /crm/Contacts?$select=last_name&$order_by=first_name&$filter=first_name eq 'Karel'
118
+ # Find the account with code 123
119
+ accounts = Elmas::Account.new(code: '123').find_by(filter: [:code])
120
+
121
+ # Find the accounts with code 123 and 345
122
+ accounts = Elmas::Account.new(code: ['123', '345']).find_by(filter: [:code])
129
123
  ```
130
124
 
131
- So with find_by you can combine Filters, Select and OrderBy. For more information on this way of selecting data look here http://www.odata.org/
132
- There's also a method find_all, which does a get without filters. You can however set the select and order by params.
125
+ Results can be sorted in the same way
133
126
 
134
- To find all contacts
135
127
  ```ruby
136
- contact = Elmas::Contact.new
137
- contact.find_all
138
- # path = /crm/Contacts
128
+ # Return all accounts sorted by first name
129
+ accounts = Elmas::Account.new.find_all(order_by: :first_name)
139
130
  ```
140
131
 
141
- To find all contacts and order by first_name
132
+ Filters and sorting can also be combined
133
+
142
134
  ```ruby
143
- contact = Elmas::Contact.new
144
- contact.find_all(order_by: :first_name)
145
- # path = /crm/Contacts?$order_by=first_name
135
+ # Return accounts with code 123 and 345 sorted by first name
136
+ accounts = Elmas::Account.new(code: ['123', '345']).find_by(filter: [:code], order_by: :first_name)
146
137
  ```
147
138
 
148
- To find all contacts and select invoices and items
139
+ To find an individual record by its ID the `find` method can be used
140
+
149
141
  ```ruby
150
- contact = Elmas::Contact.new
151
- contact.find_all(select: [:last_name, :first_name])
152
- # path = /crm/Contacts?$select=last_name,first_name
142
+ # Return the account with guid
143
+ account = Elmas::Account.new(id: '9e3a078e-55dc-40f4-a490-1875400a3e10').find
153
144
  ```
154
145
 
155
- To create a new contact
146
+ For more information on this way of selecting data look here http://www.odata.org/
147
+
148
+ ### Creating new records
149
+
150
+ Use the initializer method followed by 'save' to create a new record:
156
151
 
157
152
  ```ruby
153
+ # Create a new contact
158
154
  contact = Elmas::Contact.new(first_name: "Karel", last_name: "Appel", account: "8d87c8c5-f1c6-495c-b6af-d5ba396873b5" )
159
155
  contact.save
160
156
  ```
data/lib/elmas/oauth.rb CHANGED
@@ -20,16 +20,14 @@ module Elmas
20
20
 
21
21
  def authorized?
22
22
  # Do a test call, return false if 401 or any error code
23
- begin
24
- response = get("/Current/Me", no_division: true)
25
- rescue BadRequestException => e
26
- Elmas.error "Not yet authorized"
27
- return false
28
- end
23
+ get("/Current/Me", no_division: true)
24
+ rescue BadRequestException
25
+ Elmas.error "Not yet authorized"
26
+ return false
29
27
  end
30
28
 
31
29
  def authorize_division
32
- get("/Current/Me", no_division: true).first.current_division
30
+ get("/Current/Me", no_division: true).results.first.current_division
33
31
  end
34
32
 
35
33
  def auto_authorize
data/lib/elmas/parser.rb CHANGED
@@ -5,28 +5,30 @@ module Elmas
5
5
  def initialize(json)
6
6
  @parsed_json = JSON.parse(json)
7
7
  rescue JSON::ParserError => e
8
- Elmas.error 'There was an error parsing the response'
8
+ Elmas.error "There was an error parsing the response"
9
9
  Elmas.error "#{e.class}: #{e.message}"
10
10
  @error_message = "#{e.class}: #{e.message}"
11
11
  end
12
12
 
13
13
  def results
14
- result['results'] if result && result['results']
14
+ result["results"] if result && result["results"]
15
15
  end
16
16
 
17
17
  def metadata
18
- result['__metadata'] if result && result['__metadata']
18
+ result["__metadata"] if result && result["__metadata"]
19
19
  end
20
20
 
21
21
  def result
22
- parsed_json['d']
22
+ parsed_json["d"]
23
+ end
24
+
25
+ def next_page_url
26
+ result && result["__next"]
23
27
  end
24
28
 
25
29
  def error_message
26
30
  @error_message ||= begin
27
- if parsed_json['error']
28
- parsed_json['error']['message']['value']
29
- end
31
+ parsed_json["error"]["message"]["value"] if parsed_json["error"]
30
32
  end
31
33
  end
32
34
 
data/lib/elmas/request.rb CHANGED
@@ -24,9 +24,11 @@ module Elmas
24
24
  private
25
25
 
26
26
  def build_path(path, options)
27
- path = "#{division}/#{path}" unless options[:no_division]
28
- path = "#{endpoint}/#{path}" unless options[:no_endpoint]
29
- path = "#{options[:url] || base_url}/#{path}"
27
+ unless options[:use_raw_path]
28
+ path = "#{division}/#{path}" unless options[:no_division]
29
+ path = "#{endpoint}/#{path}" unless options[:no_endpoint]
30
+ path = "#{options[:url] || base_url}/#{path}"
31
+ end
30
32
  path
31
33
  end
32
34
 
@@ -40,7 +40,7 @@ module Elmas
40
40
  def find
41
41
  return nil unless id?
42
42
  response = get(uri([:id]))
43
- response.result if response
43
+ response.results.first if response
44
44
  end
45
45
 
46
46
  # Normally use the url method (which applies the filters) but sometimes you only want to use the base path or other paths
@@ -0,0 +1,32 @@
1
+ module Elmas
2
+ # We can use the AgingReceivablesList to change the status of SalesInvoices from
3
+ # Open to 'Verwerkt' while at the same time sending a PDF of the invoice to the
4
+ # end user by e-mail.
5
+ #
6
+ # This endpoint only supports the POST method.
7
+ #
8
+ class AgingReceivablesList
9
+ include Elmas::Resource
10
+
11
+ def valid_actions
12
+ [:get]
13
+ end
14
+
15
+ def base_path
16
+ "financial/AgingReceivablesLists"
17
+ end
18
+
19
+ def mandatory_attributes
20
+ []
21
+ end
22
+
23
+ # https://start.exactonline.nl/docs/HlpRestAPIResourcesDetails.aspx?name=SalesInvoiceAgingReceivablesLists
24
+ def other_attributes
25
+ [
26
+ :account_id, :account_code, :account_name, :age_group1, :age_group1_amount, :age_group1_description,
27
+ :age_group2, :age_group2_amount, :age_group2_description, :age_group3, :age_group3_amount, :age_group3_description,
28
+ :age_group4, :age_group4_amount, :age_group4_description, :currency_code, :total_amount
29
+ ]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ module Elmas
2
+ class Layout
3
+ include Elmas::Resource
4
+
5
+ def valid_actions
6
+ [:get]
7
+ end
8
+
9
+ def base_path
10
+ "salesinvoice/Layouts"
11
+ end
12
+
13
+ def mandatory_attributes
14
+ []
15
+ end
16
+
17
+ def other_attributes
18
+ [
19
+ :id, :created, :creator, :creator_full_name, :division,
20
+ :modified, :modifier, :modifier_full_name, :subject, :type
21
+ ]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,35 @@
1
+ module Elmas
2
+ # We can use the PrintedSalesInvoice to change the status of SalesInvoices from
3
+ # Open to 'Verwerkt' while at the same time sending a PDF of the invoice to the
4
+ # end user by e-mail.
5
+ #
6
+ # This endpoint only supports the POST method.
7
+ #
8
+ class PrintedSalesInvoice
9
+ include Elmas::Resource
10
+
11
+ def valid_actions
12
+ [:post]
13
+ end
14
+
15
+ def base_path
16
+ "salesinvoice/PrintedSalesInvoices"
17
+ end
18
+
19
+ def mandatory_attributes
20
+ [:invoice_ID]
21
+ end
22
+
23
+ # https://start.exactonline.nl/docs/HlpRestAPIResourcesDetails.aspx?name=SalesInvoicePrintedSalesInvoices
24
+ def other_attributes
25
+ [
26
+ :division, :document, :document_creation_error, :document_creation_success,
27
+ :document_layout, :email_creation_error, :email_creation_success, :email_layout,
28
+ :extra_text, :invoice_date, :postbox_message_creation_error,
29
+ :postbox_message_creation_success, :postbox_sender, :reporting_period,
30
+ :reporting_year, :send_email_to_customer, :send_invoice_to_customer_postbox,
31
+ :send_output_based_on_account
32
+ ]
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ module Elmas
2
+ # We can use the AgingReceivablesList to change the status of SalesInvoices from
3
+ # Open to 'Verwerkt' while at the same time sending a PDF of the invoice to the
4
+ # end user by e-mail.
5
+ #
6
+ # This endpoint only supports the POST method.
7
+ #
8
+ class ReceivablesList
9
+ include Elmas::Resource
10
+
11
+ def valid_actions
12
+ [:get]
13
+ end
14
+
15
+ def base_path
16
+ "read/financial/ReceivablesList"
17
+ end
18
+
19
+ def mandatory_attributes
20
+ []
21
+ end
22
+
23
+ # https://start.exactonline.nl/docs/HlpRestAPIResourcesDetails.aspx?name=ReadFinancialReceivablesList
24
+ def other_attributes
25
+ [
26
+ :description, :hid, :account_code, :account_id, :account_name, :amount, :amount_in_transit,
27
+ :currency_code, :description, :due_date, :entry_number, :id, :invoice_date, :invoice_number,
28
+ :journal_code, :journal_description, :your_ref
29
+ ]
30
+ end
31
+ end
32
+ end
@@ -7,10 +7,7 @@ module Elmas
7
7
 
8
8
  def initialize(response)
9
9
  @response = response
10
- if fail?
11
- log_error
12
- raise BadRequestException.new(@response, parsed)
13
- end
10
+ raise_and_log_error if fail?
14
11
  end
15
12
 
16
13
  def success?
@@ -25,33 +22,12 @@ module Elmas
25
22
  Parser.new(body)
26
23
  end
27
24
 
28
- def results
29
- results = []
30
- if parsed.results
31
- parsed.results.each do |attributes|
32
- klass = resolve_class
33
- results << klass.send(:new, attributes)
34
- end
35
- end
36
- results
37
- end
38
-
39
25
  def result
40
- klass = resolve_class
41
- klass.send(:new, parsed.result)
26
+ Elmas::ResultSet.new(parsed)
42
27
  end
43
28
 
44
- def first
45
- results ? results.first : result
46
- end
47
-
48
- def type
49
- if parsed.metadata
50
- c_type = parsed.metadata["type"]
51
- elsif parsed.results.any?
52
- c_type = parsed.results.first["__metadata"]["type"]
53
- end
54
- c_type.split(".").last
29
+ def results
30
+ Elmas::ResultSet.new(parsed)
55
31
  end
56
32
 
57
33
  def status
@@ -85,12 +61,9 @@ module Elmas
85
61
 
86
62
  private
87
63
 
88
- def resolve_class
89
- constant_name = Utils.modulize(type)
90
- return Object.const_get(constant_name)
91
- rescue NameError
92
- Elmas.info("Unknown resource encountered, proceed as usual but further resource details might have to be implemented")
93
- return Class.new { include Elmas::Resource }
64
+ def raise_and_log_error
65
+ log_error
66
+ fail BadRequestException.new(@response, parsed)
94
67
  end
95
68
  end
96
69
  end
@@ -0,0 +1,55 @@
1
+ module Elmas
2
+ class ResultSet
3
+ attr_reader :records
4
+
5
+ def initialize(parsed_response)
6
+ @parsed_response = parsed_response
7
+
8
+ if @parsed_response.results
9
+ @records = @parsed_response.results.map do |attributes|
10
+ resource_class.send(:new, attributes)
11
+ end
12
+ else
13
+ @records = [resource_class.send(:new, @parsed_response.result)]
14
+ end
15
+ end
16
+
17
+ def next_page
18
+ return unless next_page_url
19
+ next_page = Elmas.get(next_page_url, use_raw_path: true)
20
+ return unless next_page
21
+
22
+ response = Elmas::Response.new(next_page)
23
+ response.results
24
+ end
25
+
26
+ def first
27
+ records.first
28
+ end
29
+
30
+ protected
31
+
32
+ def next_page_url
33
+ @parsed_response.next_page_url
34
+ end
35
+
36
+ def type
37
+ if @parsed_response.metadata
38
+ c_type = @parsed_response.metadata["type"]
39
+ elsif @parsed_response.results.any?
40
+ c_type = @parsed_response.results.first["__metadata"]["type"]
41
+ end
42
+ c_type.split(".").last
43
+ end
44
+
45
+ def resource_class
46
+ @resource_class ||= begin
47
+ constant_name = Utils.modulize(type)
48
+ Object.const_get(constant_name)
49
+ rescue NameError
50
+ Elmas.info("Unknown resource encountered, proceed as usual but further resource details might have to be implemented")
51
+ Class.new { include Elmas::Resource }
52
+ end
53
+ end
54
+ end
55
+ end
data/lib/elmas/uri.rb CHANGED
@@ -18,21 +18,36 @@ module Elmas
18
18
  uri
19
19
  end
20
20
 
21
- # ?$filter=ID eq guid'#{id}'
22
- def id_filter
23
- ["$filter", "ID eq guid'#{id}'"]
21
+ def base_filter(attribute)
22
+ values = @attributes[attribute]
23
+ values = [values] unless values.is_a?(Array)
24
+
25
+ filters = values.map do |value|
26
+ "#{query_attribute(attribute)} eq #{sanitize_value(value)}"
27
+ end
28
+
29
+ ["$filter", filters.join(" or ")]
24
30
  end
25
31
 
26
- def base_filter(attribute)
32
+ # Sanitize an attribute in symbol format to the ExactOnline style
33
+ def query_attribute(attribute)
27
34
  if attribute == :id
28
- return id_filter
35
+ attribute.to_s.upcase
29
36
  else
30
- if @attributes[attribute].is_a?(Fixnum)
31
- value = @attributes[attribute]
37
+ Utils.camelize(attribute)
38
+ end
39
+ end
40
+
41
+ # Convert a value to something usable in an ExactOnline request
42
+ def sanitize_value(value)
43
+ if value.is_a?(String)
44
+ if value =~ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
45
+ "guid'#{value}'"
32
46
  else
33
- value = "'#{@attributes[attribute]}'"
47
+ "'#{value}'"
34
48
  end
35
- return ["$filter", "#{Utils.camelize(attribute)} eq #{value}"]
49
+ else
50
+ value
36
51
  end
37
52
  end
38
53
 
data/lib/elmas/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Elmas
2
2
  class Version
3
- MAJOR = 1
3
+ MAJOR = 2
4
4
  MINOR = 0
5
5
  PATCH = 0
6
6
 
data/lib/elmas.rb CHANGED
@@ -5,17 +5,22 @@ require "elmas/response"
5
5
  require "elmas/client"
6
6
  require "elmas/log"
7
7
  require "elmas/resource"
8
+ require "elmas/result_set"
8
9
  require "elmas/sanitizer"
10
+ require "elmas/resources/aging_receivables_list"
11
+ require "elmas/resources/receivables_list"
9
12
  require "elmas/resources/shared_sales_attributes"
10
13
  require "elmas/resources/base_entry_line"
11
14
  require "elmas/resources/bank_entry"
12
15
  require "elmas/resources/bank_entry_line"
13
16
  require "elmas/resources/contact"
14
17
  require "elmas/resources/sales_invoice"
18
+ require "elmas/resources/sales_invoice_line"
19
+ require "elmas/resources/printed_sales_invoice"
20
+ require "elmas/resources/layout"
15
21
  require "elmas/resources/journal"
16
22
  require "elmas/resources/item"
17
23
  require "elmas/resources/item_group"
18
- require "elmas/resources/sales_invoice_line"
19
24
  require "elmas/resources/account"
20
25
  require "elmas/resources/gl_account"
21
26
  require "elmas/resources/sales_entry"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elmas
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marthyn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-01 00:00:00.000000000 Z
11
+ date: 2015-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -223,6 +223,7 @@ files:
223
223
  - ".rspec"
224
224
  - ".rubocop.yml"
225
225
  - ".travis.yml"
226
+ - Changelog.md
226
227
  - Gemfile
227
228
  - Guardfile
228
229
  - LICENSE
@@ -243,6 +244,7 @@ files:
243
244
  - lib/elmas/request.rb
244
245
  - lib/elmas/resource.rb
245
246
  - lib/elmas/resources/account.rb
247
+ - lib/elmas/resources/aging_receivables_list.rb
246
248
  - lib/elmas/resources/bank_entry.rb
247
249
  - lib/elmas/resources/bank_entry_line.rb
248
250
  - lib/elmas/resources/base_entry_line.rb
@@ -255,10 +257,13 @@ files:
255
257
  - lib/elmas/resources/item.rb
256
258
  - lib/elmas/resources/item_group.rb
257
259
  - lib/elmas/resources/journal.rb
260
+ - lib/elmas/resources/layout.rb
258
261
  - lib/elmas/resources/mailbox.rb
262
+ - lib/elmas/resources/printed_sales_invoice.rb
259
263
  - lib/elmas/resources/project.rb
260
264
  - lib/elmas/resources/purchase_entry.rb
261
265
  - lib/elmas/resources/purchase_entry_line.rb
266
+ - lib/elmas/resources/receivables_list.rb
262
267
  - lib/elmas/resources/sales_entry.rb
263
268
  - lib/elmas/resources/sales_entry_line.rb
264
269
  - lib/elmas/resources/sales_invoice.rb
@@ -271,6 +276,7 @@ files:
271
276
  - lib/elmas/resources/transaction_line.rb
272
277
  - lib/elmas/resources/vat_code.rb
273
278
  - lib/elmas/response.rb
279
+ - lib/elmas/result_set.rb
274
280
  - lib/elmas/sanitizer.rb
275
281
  - lib/elmas/uri.rb
276
282
  - lib/elmas/utils.rb