pennylane 0.1.0 → 0.2.0.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da1842f4d3dee5f1bd7c9bccd793eb1c185cb5fece2178e967da0cbec3b752be
4
- data.tar.gz: 1f050f189dae434bdf4febe2c7715858b1f422273a129aa6654677c95ba94921
3
+ metadata.gz: 9a60c1035dbad731707f86c3e97610c740d286cb8e6f70b7ac30709bbe29a0f0
4
+ data.tar.gz: b3fd35e53857c0d8df547bb361d4ae69bd1b73a482b7552232ab4cfcce5e8926
5
5
  SHA512:
6
- metadata.gz: c245562b9cdcfc4754348d1a43d2029d5b6d025cd61191c0ceeb3e7b814ed81bae299cc7e86b76799a11275fc296a4e5fd8080ab133f262981ccf9091fad665f
7
- data.tar.gz: 0404a595eedc597bc04c2dafa9db4796e1ab9d6a12a0bfc9f28132195b50f0ebe8516552c5de04326ed1d79cfef97a2d07de0e7b30ce6fd188abc991e07a221c
6
+ metadata.gz: 903f576a32e59f954c68c255ce0aeacf6a820315f233f9825b2d35036cff3ab0928c6ac9fb0e84daa059da40dfde4f1829c5dff61ad58a57a406a3483e69b9e8
7
+ data.tar.gz: 72ed99e03c1d31ff1a871ebf0d335110c9a0a38a9acc9d8e14ecaf27a50731afef55ce6d1c77d89cbb0f95558eba121938ba8da7b87827e6c2416be10def3c5d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0-alpha] - 2024-04-20
4
+
5
+ - Support resources
6
+ - `CategoryGroup`
7
+ - `Category`
8
+ - `Product`
9
+ - Added missing `#create` to `Supplier`
10
+ - Adding `#update` to `Customer` and `Supplier`
11
+
12
+
3
13
  ## [0.1.0] - 2024-04-04
4
14
 
5
- - Initial release
15
+ - Initial release
data/Gemfile.lock CHANGED
@@ -1,15 +1,33 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pennylane (0.1.0)
4
+ pennylane (0.2.0.pre.alpha)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ activesupport (7.1.3.2)
10
+ base64
11
+ bigdecimal
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ connection_pool (>= 2.2.5)
14
+ drb
15
+ i18n (>= 1.6, < 2)
16
+ minitest (>= 5.1)
17
+ mutex_m
18
+ tzinfo (~> 2.0)
9
19
  ast (2.4.2)
20
+ base64 (0.2.0)
21
+ bigdecimal (3.1.7)
22
+ concurrent-ruby (1.2.3)
23
+ connection_pool (2.4.1)
24
+ drb (2.2.1)
25
+ i18n (1.14.4)
26
+ concurrent-ruby (~> 1.0)
10
27
  json (2.7.2)
11
28
  language_server-protocol (3.17.0.3)
12
29
  minitest (5.22.3)
30
+ mutex_m (0.2.0)
13
31
  parallel (1.24.0)
14
32
  parser (3.3.0.5)
15
33
  ast (~> 2.4.1)
@@ -36,6 +54,8 @@ GEM
36
54
  ruby-progressbar (1.13.0)
37
55
  test-unit (3.6.2)
38
56
  power_assert
57
+ tzinfo (2.0.6)
58
+ concurrent-ruby (~> 1.0)
39
59
  unicode-display_width (2.5.0)
40
60
  vcr (6.2.0)
41
61
 
@@ -43,6 +63,7 @@ PLATFORMS
43
63
  arm64-darwin-22
44
64
 
45
65
  DEPENDENCIES
66
+ activesupport (~> 7.1)
46
67
  minitest (~> 5.22)
47
68
  pennylane!
48
69
  rake (~> 13.0)
data/README.md CHANGED
@@ -38,6 +38,32 @@ Pennylane::Customer.list(filter: [{field: 'name', operator: 'eq', value: 'Apple'
38
38
 
39
39
  # Retrieve single customer
40
40
  Pennylane::Customer.retrieve('38a1f19a-256d-4692-a8fe-0a16403f59ff')
41
+
42
+ # Update a customer
43
+ cus = Pennylane::Customer.retrieve('38a1f19a-256d-4692-a8fe-0a16403f59ff')
44
+ cus.update(name: 'Apple Inc')
45
+
46
+ ```
47
+
48
+ ### Per-request api key [TODO]
49
+ For apps that need to use multiple keys during the lifetime of a process. it's also possible to set a per-request key:
50
+ ```ruby
51
+ require "pennylane"
52
+
53
+ Pennylane::Customer.list(
54
+ {},
55
+ {
56
+ api_key: 'x1fa....'
57
+ }
58
+ )
59
+
60
+ Pennylane::Customer.retrieve(
61
+ '38a1f19a-256d-4692-a8fe-0a16403f59ff',
62
+ {
63
+ api_key: 'x1fa....'
64
+ }
65
+ )
66
+
41
67
  ```
42
68
 
43
69
  ## Test mode
@@ -50,6 +76,26 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
50
76
 
51
77
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
52
78
 
79
+ Resources implemented so far :
80
+ ### CUSTOMER INVOICING
81
+
82
+ - Customer Invoices 🚧
83
+ - Estimates 🚧
84
+ - Billing Subscriptions 🚧
85
+
86
+ ### REFERENTIALS
87
+
88
+ - Customers ✅
89
+ - Suppliers ✅
90
+ - Categories ✅
91
+ - CategoryGroups ✅
92
+ - Products ✅
93
+ - Plan Items 🚧
94
+ - Enums 🚧
95
+
96
+ ### SUPPLIER INVOICING
97
+ - Supplier Invoices 🚧
98
+
53
99
  ## Contributing
54
100
 
55
101
  Bug reports and pull requests are welcome on GitHub at https://github.com/sbounmy/pennylane. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/pennylane/blob/main/CODE_OF_CONDUCT.md).
@@ -36,8 +36,7 @@ module Pennylane
36
36
 
37
37
  def request method, path, params:, opts: {}
38
38
  req = initialize_request(method, path, params[:query]).tap do |req|
39
- req.body = params[:body].to_json if params[:body] && method != :get
40
- # req.query = params if !params.empty? && method == :get
39
+ req.body = params[:body].to_json if params[:body]
41
40
  end
42
41
 
43
42
  http.request(req).tap do |resp|
@@ -1,10 +1,7 @@
1
1
  module Pennylane
2
2
  class Object
3
3
  include Enumerable
4
-
5
- def initialize(id = nil)
6
- @id = id
7
- end
4
+ attr_reader :id
8
5
 
9
6
  def initialize_from_response(response, params = {}, opts = {})
10
7
  values = Util.symbolize_names(response)
@@ -19,7 +16,7 @@ module Pennylane
19
16
  end
20
17
 
21
18
  def self.build_from(response, params = {}, opts = {})
22
- new(response['id']).initialize_from_response(response, params, opts)
19
+ new.initialize_from_response(response, params, opts)
23
20
  end
24
21
 
25
22
  def self.objects
@@ -11,9 +11,9 @@ module Pennylane
11
11
  "#{object_name}s"
12
12
  end
13
13
 
14
- def request_pennylane_object(method:, path:, params: {}, opts: {}, usage: [])
14
+ def request_pennylane_object(method:, path:, params: {}, opts: {}, usage: [], with: {})
15
15
  resp, opts = execute_resource_request(method, path, params, opts, usage)
16
- Util.convert_to_pennylane_object(Util.normalize_response(resp), params, opts)
16
+ Util.convert_to_pennylane_object(Util.normalize_response(resp, with), params, opts)
17
17
  end
18
18
 
19
19
  def execute_resource_request(method, path, params = {}, opts = {}, usage = [])
@@ -33,18 +33,46 @@ module Pennylane
33
33
  def client
34
34
  @client ||= Pennylane::Client.new(Pennylane.api_key)
35
35
  end
36
+
37
+ def normalize_filters(filters)
38
+ filters[:filter] = filters[:filter].to_json if filters[:filter]
39
+ filters
40
+ end
41
+ end
42
+
43
+ # object happens to be nil when the object is in a list
44
+ def id
45
+ object.source_id
46
+ rescue
47
+ super
48
+ end
49
+
50
+ #
51
+ def object
52
+ @values[self.class.object_name.to_sym]
53
+ end
54
+
55
+ # def inspect
56
+ # id_string = respond_to?(:id) && !id.nil? ? " id=#{id}" : ""
57
+ # "#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " +
58
+ # JSON.pretty_generate(object.instance_variable_get(:@values) || @values)
59
+ # end
60
+
61
+ def update(attributes)
62
+ resp, opts = self.class.request_pennylane_object(method: :put, path: "/#{self.class.object_name_plural}/#{id}", params: { body: { self.class.object_name => attributes } })
63
+ @values = resp.instance_variable_get :@values
64
+ self
36
65
  end
37
66
 
38
67
  # So we can call directly method on the object rather than going through his key
39
68
  # Pennylane::Customer.retrieve('any-id').name == Pennylane::Customer.retrieve('any-id').customer.name
40
69
  def method_missing(method_name, *args, &block)
41
- obj = @values[self.class.object_name.to_sym]
42
- raise NoMethodError, "undefined method `#{method_name}` for #{self.class}" unless obj
43
- obj.send(method_name, *args, &block)
70
+ raise NoMethodError, "undefined method `#{method_name}` for #{self.class}" unless object
71
+ object.send(method_name, *args, &block)
44
72
  end
45
73
 
46
74
  def respond_to_missing?(method_name, include_private = false)
47
- @values[self.class.object_name.to_sym].respond_to?(method_name) || super
75
+ object.respond_to?(method_name) || super
48
76
  end
49
77
 
50
78
  end
@@ -0,0 +1,27 @@
1
+ module Pennylane
2
+ class Category < Resources::Base
3
+
4
+ class << self
5
+
6
+ # override the object_name_plural method otherwise it will return 'categorys'
7
+ def object_name_plural
8
+ 'categories'
9
+ end
10
+
11
+ def list filters = {}, opts = {}
12
+ normalize_filters(filters)
13
+ request_pennylane_object(method: :get, path: "/categories", params: { query: filters }, opts: opts)
14
+ end
15
+
16
+ def retrieve id, opts = {}
17
+ request_pennylane_object(method: :get, path: "/categories/#{id}", params: {}, opts: opts)
18
+ end
19
+
20
+ def create params, opts = {}
21
+ request_pennylane_object(method: :post, path: "/categories", params: { body: { object_name => params } }, opts: opts)
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,16 @@
1
+ module Pennylane
2
+ class CategoryGroup < Resources::Base
3
+
4
+ class << self
5
+
6
+ def object_name
7
+ 'category_group'
8
+ end
9
+
10
+ def list filters = {}, opts = {}
11
+ request_pennylane_object(method: :get, path: "/category_groups", params: { query: filters }, opts: opts)
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -4,11 +4,12 @@ module Pennylane
4
4
  class << self
5
5
 
6
6
  def list filters = {}, opts = {}
7
+ normalize_filters(filters)
7
8
  request_pennylane_object(method: :get, path: "/customers", params: { query: filters }, opts: opts)
8
9
  end
9
10
 
10
- def retrieve customer_id, opts = {}
11
- request_pennylane_object(method: :get, path: "/customers/#{customer_id}", params: {}, opts: opts)
11
+ def retrieve id, opts = {}
12
+ request_pennylane_object(method: :get, path: "/customers/#{id}", params: {}, opts: opts)
12
13
  end
13
14
 
14
15
  def create params, opts = {}
@@ -0,0 +1,45 @@
1
+ module Pennylane
2
+ class CustomerInvoice < Resources::Base
3
+
4
+ class << self
5
+
6
+ def object_name
7
+ 'customer_invoice'
8
+ end
9
+
10
+ def list filters = {}, opts = {}
11
+ normalize_filters(filters)
12
+ request_pennylane_object(method: :get, path: "/customer_invoices", params: { query: filters }, opts: opts, with: { invoice: 'customer_invoice' })
13
+ end
14
+
15
+ def retrieve id, opts = {}
16
+ request_pennylane_object(method: :get, path: "/customer_invoices/#{id}", params: {}, opts: opts, with: { invoice: 'customer_invoice' })
17
+ end
18
+
19
+ def create params, opts = {}
20
+ request_pennylane_object(method: :post, path: "/customer_invoices", params: { body: params }, opts: opts, with: { invoice: 'customer_invoice' })
21
+ end
22
+
23
+ end
24
+
25
+ # since object name is different from the class name, we need to override the method
26
+ def update(attributes)
27
+ resp, opts = self.class.request_pennylane_object(method: :put, path: "/#{self.class.object_name_plural}/#{id}",
28
+ params: { body: { 'invoice' => attributes } },
29
+ opts: {}, with: { invoice: 'customer_invoice' })
30
+ @values = resp.instance_variable_get :@values
31
+ self
32
+ end
33
+
34
+ # since object name is different from the class name, we need to override the object_name method
35
+ def object
36
+ @values[:invoice]
37
+ end
38
+
39
+ # doesnt have a `source_id` so we override it
40
+ def id
41
+ object.id
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ module Pennylane
2
+ class Product < Resources::Base
3
+
4
+ class << self
5
+
6
+ def list filters = {}, opts = {}
7
+ normalize_filters(filters)
8
+ request_pennylane_object(method: :get, path: "/products", params: { query: filters }, opts: opts)
9
+ end
10
+
11
+ def retrieve id, opts = {}
12
+ request_pennylane_object(method: :get, path: "/products/#{id}", params: {}, opts: opts)
13
+ end
14
+
15
+ def create params, opts = {}
16
+ request_pennylane_object(method: :post, path: "/products", params: { body: { product: params } }, opts: opts)
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Pennylane
2
+ class Supplier < Resources::Base
3
+ class << self
4
+
5
+ def list filters = {}, opts = {}
6
+ normalize_filters(filters)
7
+ request_pennylane_object(method: :get, path: "/suppliers", params: { query: filters }, opts: opts)
8
+ end
9
+
10
+ def retrieve supplier_id, opts = {}
11
+ request_pennylane_object(method: :get, path: "/suppliers/#{supplier_id}", params: {}, opts: opts)
12
+ end
13
+
14
+ def create params, opts = {}
15
+ request_pennylane_object(method: :post, path: "/suppliers", params: { body: { object_name => params } }, opts: opts)
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -20,28 +20,38 @@ module Pennylane
20
20
  # It will add an _object key to each hash in the response
21
21
  # This key will contain the name of the object
22
22
  # It will also add an _object key to the root of the response
23
- def normalize_response(object)
23
+ # with: is used to map the object name to a different class
24
+ # Example : GET /customer_invoices will return a list of invoices
25
+ # {
26
+ # "total_pages": 5,
27
+ # "current_page": 1,
28
+ # "total_invoices": 12,
29
+ # "invoices": []
30
+ # }
31
+ # `invoices` should be `customer_invoice` so we can cast it to the right class CustomerInvoice
32
+ # Since we don't have the ability to change the API response.
33
+ # We can achieve this by calling normalize_response(response, with: {invoice: 'customer_invoice'})
34
+ def normalize_response(object, with={})
24
35
  # puts object.inspect
25
36
  case object
26
37
  when Hash
27
38
  new_hash = {}
28
- new_hash['_object'] = object.has_key?('total_pages') ? 'list' : singularize(object.keys.first)
29
-
39
+ new_hash['_object'] = object.has_key?('total_pages') ? 'list' : (with[singularize(object.keys.first).to_sym] || singularize(object.keys.first))
30
40
  object.each do |key, value|
31
41
  if value.is_a? Array
32
42
  new_hash[key] = value.map do |h|
33
- h['_object'] = singularize(key) if h.is_a? Hash
34
- normalize_response(h)
43
+ h['_object'] = with[singularize(key).to_sym] || singularize(key) if h.is_a? Hash
44
+ normalize_response(h, with)
35
45
  end
36
46
  elsif value.is_a? Hash
37
- value['_object'] = singularize(key)
47
+ value['_object'] = with[singularize(key).to_sym] || singularize(key)
38
48
  end
39
- new_hash[key] = normalize_response(value)
49
+ new_hash[key] = normalize_response(value, with)
40
50
  end
41
51
  new_hash
42
52
  when Array
43
53
  object.map do |value|
44
- normalize_response(value)
54
+ normalize_response(value, with)
45
55
  end
46
56
  else
47
57
  object
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pennylane
4
- VERSION = "0.1.0"
5
- end
4
+ VERSION = "0.2.0-alpha"
5
+ end
data/lib/pennylane.rb CHANGED
@@ -22,7 +22,12 @@ module Pennylane
22
22
 
23
23
  API_RESOURCES = {
24
24
  ListObject.object_name => ListObject,
25
- Customer.object_name => Customer
25
+ Category.object_name => Category,
26
+ CategoryGroup.object_name => CategoryGroup,
27
+ Customer.object_name => Customer,
28
+ CustomerInvoice.object_name => CustomerInvoice,
29
+ Product.object_name => Product,
30
+ Supplier.object_name => Supplier
26
31
  }.freeze
27
32
 
28
33
  @config = Pennylane::Configuration.new
data/pennylane.gemspec CHANGED
@@ -34,5 +34,6 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency "vcr", "~> 6.2"
35
35
  spec.add_development_dependency "test-unit", "~> 3.6"
36
36
  spec.add_development_dependency "minitest", "~> 5.22"
37
+ spec.add_development_dependency "activesupport", "~> 7.1"
37
38
  # guide at: https://bundler.io/guides/creating_gem.html
38
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pennylane
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0.pre.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephane Bounmy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-14 00:00:00.000000000 Z
11
+ date: 2024-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: vcr
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.22'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '7.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '7.1'
55
69
  description: Pennylane offers integrated financial management and accounting tools
56
70
  for businesses. See https://pennylane.com for details.
57
71
  email:
@@ -74,7 +88,12 @@ files:
74
88
  - lib/pennylane/list_object.rb
75
89
  - lib/pennylane/object.rb
76
90
  - lib/pennylane/resources/base.rb
91
+ - lib/pennylane/resources/category.rb
92
+ - lib/pennylane/resources/category_group.rb
77
93
  - lib/pennylane/resources/customer.rb
94
+ - lib/pennylane/resources/customer_invoice.rb
95
+ - lib/pennylane/resources/product.rb
96
+ - lib/pennylane/resources/supplier.rb
78
97
  - lib/pennylane/util.rb
79
98
  - lib/pennylane/version.rb
80
99
  - pennylane.gemspec
@@ -97,9 +116,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
97
116
  version: 2.6.0
98
117
  required_rubygems_version: !ruby/object:Gem::Requirement
99
118
  requirements:
100
- - - ">="
119
+ - - ">"
101
120
  - !ruby/object:Gem::Version
102
- version: '0'
121
+ version: 1.3.1
103
122
  requirements: []
104
123
  rubygems_version: 3.4.10
105
124
  signing_key: