myfinance 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +23 -0
  4. data/.hound.yml +1063 -0
  5. data/.rspec +2 -0
  6. data/.travis.yml +7 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile.lock +94 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +209 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +7 -0
  14. data/lib/myfinance.rb +35 -0
  15. data/lib/myfinance/client.rb +31 -0
  16. data/lib/myfinance/configuration.rb +12 -0
  17. data/lib/myfinance/entities/base.rb +9 -0
  18. data/lib/myfinance/entities/collection.rb +57 -0
  19. data/lib/myfinance/entities/entity.rb +16 -0
  20. data/lib/myfinance/entities/entity_collection.rb +14 -0
  21. data/lib/myfinance/entities/financial_account.rb +30 -0
  22. data/lib/myfinance/entities/payable_account.rb +6 -0
  23. data/lib/myfinance/entities/receivable_account.rb +6 -0
  24. data/lib/myfinance/exception.rb +11 -0
  25. data/lib/myfinance/http.rb +33 -0
  26. data/lib/myfinance/request.rb +53 -0
  27. data/lib/myfinance/resources/base.rb +28 -0
  28. data/lib/myfinance/resources/entity.rb +39 -0
  29. data/lib/myfinance/resources/financial_account.rb +70 -0
  30. data/lib/myfinance/resources/payable_account.rb +33 -0
  31. data/lib/myfinance/resources/receivable_account.rb +33 -0
  32. data/lib/myfinance/response.rb +41 -0
  33. data/lib/myfinance/version.rb +3 -0
  34. data/myfinance.gemspec +45 -0
  35. data/spec/lib/myfinance/client_spec.rb +49 -0
  36. data/spec/lib/myfinance/configuration_spec.rb +26 -0
  37. data/spec/lib/myfinance/entities/base_spec.rb +28 -0
  38. data/spec/lib/myfinance/entities/collection_spec.rb +70 -0
  39. data/spec/lib/myfinance/entities/entity_collection_spec.rb +20 -0
  40. data/spec/lib/myfinance/entities/entity_spec.rb +11 -0
  41. data/spec/lib/myfinance/entities/payable_account_spec.rb +13 -0
  42. data/spec/lib/myfinance/entities/receivable_account_spec.rb +13 -0
  43. data/spec/lib/myfinance/http_spec.rb +37 -0
  44. data/spec/lib/myfinance/request_spec.rb +29 -0
  45. data/spec/lib/myfinance/resources/entity_spec.rb +63 -0
  46. data/spec/lib/myfinance/resources/payable_account_spec.rb +146 -0
  47. data/spec/lib/myfinance/resources/receivable_account_spec.rb +146 -0
  48. data/spec/lib/myfinance/response_spec.rb +58 -0
  49. data/spec/myfinance_spec.rb +25 -0
  50. data/spec/spec_helper.rb +39 -0
  51. data/spec/support/client.rb +3 -0
  52. data/spec/support/matchers/have_attr_accessor.rb +18 -0
  53. data/spec/support/shared_examples/entity_attributes.rb +9 -0
  54. data/spec/support/shared_examples/http_request_methods.rb +29 -0
  55. data/spec/support/vcr.rb +7 -0
  56. metadata +306 -0
@@ -0,0 +1,12 @@
1
+ require 'base64'
2
+
3
+ module Myfinance
4
+ class Configuration
5
+ attr_accessor :url, :user_agent
6
+
7
+ def initialize
8
+ @url = 'https://app.myfinance.com.br'
9
+ @user_agent = "Myfinance Ruby Client v#{Myfinance::VERSION}"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ require "virtus"
2
+
3
+ module Myfinance
4
+ module Entities
5
+ class Base
6
+ include Virtus.model
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,57 @@
1
+ module Myfinance
2
+ module Entities
3
+ #
4
+ # A wrapper to Myfinance collection returns from API. This wrapper represents a collection and it's responsible for processing pagination information as well.
5
+ #
6
+ class Collection < Base
7
+ PAGE_REGEX = /page=(\d+)/
8
+
9
+ attr_reader :response, :collection, :headers
10
+
11
+ def initialize(response)
12
+ @response = response
13
+ @collection = []
14
+ @headers = response.headers['Link'].split(',') rescue []
15
+ end
16
+
17
+ def self.build(response)
18
+ self.new(response).build
19
+ end
20
+
21
+ def build
22
+ build_collection
23
+ self
24
+ end
25
+
26
+ def next_page
27
+ page_for(:next)
28
+ end
29
+
30
+ def last_page
31
+ page_for(:last)
32
+ end
33
+
34
+ def previous_page
35
+ page_for(:prev)
36
+ end
37
+
38
+ def first_page
39
+ page_for(:first)
40
+ end
41
+
42
+ private
43
+
44
+ def page_for(page_rel)
45
+ header_link_for(page_rel).match(PAGE_REGEX)[1].to_i rescue nil
46
+ end
47
+
48
+ def header_link_for(rel)
49
+ headers.select{|n| n =~ /rel=#{rel}/}.first
50
+ end
51
+
52
+ def build_collection
53
+ raise NotImplementedError
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,16 @@
1
+ module Myfinance
2
+ module Entities
3
+ class Entity < Base
4
+ attribute :id, Integer
5
+ attribute :name, String
6
+ attribute :account_id, Integer
7
+ attribute :charging_uuid, String
8
+ attribute :created_at, DateTime
9
+ attribute :default_in_menu, Boolean
10
+ attribute :deleted_at, DateTime
11
+ attribute :federation_subscription_number, String
12
+ attribute :imported_from_sync_at, DateTime
13
+ attribute :updated_at, DateTime
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ module Myfinance
2
+ module Entities
3
+ #
4
+ # A wrapper to Myfinance entities collection
5
+ #
6
+ class EntityCollection < Collection
7
+ def build_collection
8
+ response.parsed_body.each do |attributes|
9
+ collection.push(Myfinance::Entities::Entity.new(attributes['entity']))
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ module Myfinance
2
+ module Entities
3
+ class FinancialAccount < Base
4
+ [:id, :entity_id, :status, :category_id, :person_id, :recurrence_id, :number_of_parcels,
5
+ :current_parcel, :classification_center_id, :expected_deposit_account_id].each do |k|
6
+ attribute k, Integer
7
+ end
8
+
9
+ [:status_name, :description, :document, :observation, :recurrence_period,
10
+ :competency_month].each { |k| attribute k, String }
11
+
12
+ [:amount, :interest_amount, :discount_amount, :total_amount, :ticket_amount].each do |k|
13
+ attribute k, Float
14
+ end
15
+
16
+ [:remind, :income_tax_relevant, :recurrent, :parcelled, :ticket_amount].each do |k|
17
+ attribute k, Boolean
18
+ end
19
+
20
+ [:due_date, :occurred_at, :document_emission_date, :reminded_at].each do |k|
21
+ attribute k, Date
22
+ end
23
+ [:created_at, :updated_at].each { |k| attribute k, DateTime }
24
+
25
+ attribute :financial_account_taxes_attributes, Array[Hash]
26
+ attribute :reconciliations, Hash[String => Array]
27
+ attribute :links, Array[Hash[String => String]]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ module Myfinance
2
+ module Entities
3
+ class PayableAccount < FinancialAccount
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Myfinance
2
+ module Entities
3
+ class ReceivableAccount < FinancialAccount
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ module Myfinance
2
+ class Exception < StandardError
3
+ attr_accessor :code, :body
4
+
5
+ def initialize(args = {})
6
+ super(args[:message])
7
+ @code = args[:code]
8
+ @body = args[:body]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ require "myfinance/request"
2
+ require "myfinance/response"
3
+
4
+ module Myfinance
5
+ class Http
6
+ attr_reader :token
7
+
8
+ def initialize(token)
9
+ @token = token
10
+ end
11
+
12
+ %w[get post delete put].each do |m|
13
+ define_method(m) do |path, options = {}, &block|
14
+ send_request(m.to_sym, path, options, &block)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def send_request(method, path, options, &block)
21
+ request = Request.new(options.merge!({
22
+ method: method,
23
+ token: token,
24
+ url: "#{Myfinance.configuration.url}#{path}",
25
+ user_agent: Myfinance.configuration.user_agent
26
+ }))
27
+
28
+ response = Response.new(request.run)
29
+
30
+ response.resolve!(&block)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ module Myfinance
2
+ class Request
3
+
4
+ def initialize(args)
5
+ @args = args
6
+ end
7
+
8
+ def run
9
+ request.run
10
+ request.response
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :args
16
+
17
+ def request
18
+ @request ||= Typhoeus::Request.new(args[:url], options)
19
+ end
20
+
21
+ def options
22
+ {
23
+ method: args[:method],
24
+ params: args[:params],
25
+ body: body,
26
+ headers: headers,
27
+ accept_encoding: "gzip"
28
+ }.reject {|k,v| v.nil?}
29
+ end
30
+
31
+ def headers
32
+ headers = args.fetch(:headers) { {} }
33
+
34
+ {
35
+ "Accept" => "application/json",
36
+ "Content-Type" => "application/json",
37
+ "User-Agent" => args[:user_agent],
38
+ "Authorization" => "Basic #{authorization_hash}"
39
+ }.merge(headers)
40
+ end
41
+
42
+ def body
43
+ body = args[:body]
44
+ body = MultiJson.dump(body) if body.is_a?(Hash)
45
+ body
46
+ end
47
+
48
+ def authorization_hash
49
+ ::Base64.strict_encode64("#{args[:token]}:X")
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,28 @@
1
+ module Myfinance
2
+ module Resources
3
+ class Base
4
+
5
+ attr_accessor :http
6
+
7
+ def initialize(http)
8
+ @http = http
9
+ end
10
+
11
+ private
12
+
13
+ def respond_with_collection(response)
14
+ collection_klass = Myfinance::Entities.const_get("#{entity_klass_name}Collection")
15
+ collection_klass.build(response)
16
+ end
17
+
18
+ def respond_with_object(response, key)
19
+ entity_klass = Myfinance::Entities.const_get(entity_klass_name)
20
+ entity_klass.new(response.parsed_body(key))
21
+ end
22
+
23
+ def entity_klass_name
24
+ self.class.to_s.gsub('Resources', 'Entities')
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module Myfinance
2
+ module Resources
3
+ #
4
+ # A wrapper to Myfinance entities API
5
+ #
6
+ # [API]
7
+ # Documentation: https://app.myfinance.com.br/docs/api/entities
8
+ #
9
+ class Entity < Base
10
+ #
11
+ # List all entities of the user
12
+ #
13
+ # [API]
14
+ # Method: <tt>GET /entities</tt>
15
+ #
16
+ # Documentation: https://app.myfinance.com.br/docs/api/entities#get_index
17
+ #
18
+ def find_all
19
+ http.get('/entities', body: {}) do |response|
20
+ respond_with_collection(response)
21
+ end
22
+ end
23
+
24
+ #
25
+ # Find a specific entity
26
+ #
27
+ # [API]
28
+ # Method: <tt>GET /entities/:id</tt>
29
+ #
30
+ # Documentation: https://app.myfinance.com.br/docs/api/entities#get_show
31
+ #
32
+ def find(entity_id)
33
+ http.get("/entities/#{entity_id}", body: {}) do |response|
34
+ respond_with_object(response, 'entity')
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,70 @@
1
+ module Myfinance
2
+ module Resources
3
+ class FinancialAccount < Base
4
+
5
+ def initialize(http)
6
+ @http = http
7
+ set_method_for(pay_or_receive_method)
8
+ set_method_for(undo_payment_or_receivement)
9
+ end
10
+ #
11
+ # Creates a payable/receivable account
12
+ #
13
+ # [API]
14
+ # Method: <tt>POST /entities/:entity_id/payable_accounts</tt>
15
+ # Method: <tt>POST /entities/:entity_id/receivable_accounts</tt>
16
+ #
17
+ # Documentation: https://app.myfinance.com.br/docs/api/payable_accounts#post_create
18
+ # Documentation: https://app.myfinance.com.br/docs/api/receivable_accounts#post_create
19
+ #
20
+ def create(entity_id, params = {})
21
+ request_and_build_response(:post, endpoint_for(nil, entity_id, :create), params)
22
+ end
23
+
24
+ #
25
+ # Destroys a payable/receivable account
26
+ #
27
+ # [API]
28
+ # Method: <tt>DELETE /entities/:entity_id/payable_accounts/:id</tt>
29
+ # Method: <tt>DELETE /entities/:entity_id/receivable_accounts/:id</tt>
30
+ #
31
+ # Documentation: https://app.myfinance.com.br/docs/api/payable_accounts#delete_destroy
32
+ # Documentation: https://app.myfinance.com.br/docs/api/receivable_accounts#delete_destroy
33
+ #
34
+ def destroy(id, entity_id)
35
+ http.delete(endpoint_for(id, entity_id, :destroy)) do |response|
36
+ true
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def request_and_build_response(method, endpoint, params={})
43
+ http.send(method, endpoint, body: { resource_key => params }) do |response|
44
+ respond_with_object(response, resource_key)
45
+ end
46
+ end
47
+
48
+ def endpoint_for(id, entity_id, key)
49
+ parameterize_endoint(id, entity_id, key)
50
+ end
51
+
52
+ def default_endpoints
53
+ {
54
+ create: "/entities/:entity_id/#{resource_key}s",
55
+ destroy: "/entities/:entity_id/#{resource_key}s/:id"
56
+ }
57
+ end
58
+
59
+ def parameterize_endoint(id, entity_id, key)
60
+ endpoints[key.to_sym].gsub(':entity_id', entity_id.to_s).gsub(':id', id.to_s)
61
+ end
62
+
63
+ def set_method_for(action)
64
+ self.class.send(:define_method, action) do |id, entity_id, params={}|
65
+ request_and_build_response(:put, endpoint_for(id, entity_id, action), params)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,33 @@
1
+ module Myfinance
2
+ module Resources
3
+ #
4
+ # A wrapper to Myfinance payable accounts API
5
+ #
6
+ # [API]
7
+ # Documentation: https://app.myfinance.com.br/docs/api/payable_accounts
8
+ #
9
+ class PayableAccount < FinancialAccount
10
+
11
+ private
12
+
13
+ def endpoints
14
+ default_endpoints.merge({
15
+ pay: '/entities/:entity_id/payable_accounts/:id/pay',
16
+ undo_payment: '/entities/:entity_id/payable_accounts/:id/undo_payment'
17
+ })
18
+ end
19
+
20
+ def pay_or_receive_method
21
+ 'pay'
22
+ end
23
+
24
+ def undo_payment_or_receivement
25
+ 'undo_payment'
26
+ end
27
+
28
+ def resource_key
29
+ 'payable_account'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module Myfinance
2
+ module Resources
3
+ #
4
+ # A wrapper to Myfinance receivable accounts API
5
+ #
6
+ # [API]
7
+ # Documentation: https://app.myfinance.com.br/docs/api/receivable_accounts
8
+ #
9
+ class ReceivableAccount < FinancialAccount
10
+
11
+ private
12
+
13
+ def endpoints
14
+ default_endpoints.merge({
15
+ receive: '/entities/:entity_id/receivable_accounts/:id/receive',
16
+ undo_receivement: '/entities/:entity_id/receivable_accounts/:id/undo_receivement',
17
+ })
18
+ end
19
+
20
+ def pay_or_receive_method
21
+ 'receive'
22
+ end
23
+
24
+ def undo_payment_or_receivement
25
+ 'undo_receivement'
26
+ end
27
+
28
+ def resource_key
29
+ 'receivable_account'
30
+ end
31
+ end
32
+ end
33
+ end