rbvore 0.1.1

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.
data/lib/rbvore.rb ADDED
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbvore/version"
4
+
5
+ module Rbvore
6
+ class Error < StandardError; end
7
+ # Your code goes here...
8
+ end
9
+
10
+ require 'rbvore/api'
11
+ require 'rbvore/link'
12
+ require 'rbvore/resource'
13
+ require 'rbvore/discount'
14
+ require 'rbvore/menu_category'
15
+ require 'rbvore/menu_item'
16
+ require 'rbvore/item_modifier'
17
+ require 'rbvore/item'
18
+ require 'rbvore/tender_type'
19
+ require 'rbvore/payment'
20
+ require 'rbvore/table'
21
+ require 'rbvore/employee'
22
+ require 'rbvore/ticket_service_charge'
23
+ require 'rbvore/ticket_discount'
24
+ require 'rbvore/order_type'
25
+ require 'rbvore/revenue_center'
26
+ require 'rbvore/ticket'
27
+ require 'rbvore/location'
28
+
29
+ module Rbvore
30
+ def self.resource_subclass?(klass)
31
+ klass.respond_to?(:superclass) && klass.superclass == Resource
32
+ end
33
+
34
+ def self.resource_classes
35
+ @resource_classes ||= Rbvore.constants.map { |const|
36
+ klass = Rbvore.const_get(const)
37
+ klass if resource_subclass?(klass)
38
+ }.compact
39
+ end
40
+
41
+ # constantize attempts to turn a class name string into a proper constant that
42
+ # is a subclass of Rbvore::Resource
43
+ def self.constantize(name)
44
+ return name if resource_subclass?(name)
45
+
46
+ resource_classes.each do |klass|
47
+ return klass if [klass.singularize, klass.list_name, klass.pluralize].include?(name.to_s)
48
+ end
49
+
50
+ raise Rbvore::Error, "No Rbvore resource classes found for #{name.inspect}"
51
+ end
52
+ end
data/lib/rbvore/api.rb ADDED
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'json'
6
+ require 'forwardable'
7
+ require 'cgi'
8
+
9
+ module Rbvore
10
+ class API
11
+ DEFAULT_HOST = ENV.fetch("OMNIVORE_HOST", "api.omnivore.io")
12
+ DEFAULT_API_KEY = ENV.fetch("OMNIVORE_API_KEY", nil)
13
+ BASE_ENDPOINT = "/1.0"
14
+
15
+ class Error < Error
16
+ extend Forwardable
17
+ attr_reader :response
18
+ def_delegators :response, :code, :body, :json_body, :uri
19
+
20
+ def initialize(message = nil, response:)
21
+ @response = response
22
+ @message = message || [response.code, response.message].join(" ")
23
+ super(@message)
24
+ end
25
+ end
26
+
27
+ class Response
28
+ extend Forwardable
29
+
30
+ attr_reader :original
31
+ def_delegators :original, :code, :body, :uri, :msg, :message, :inspect, :content_type
32
+
33
+ def initialize(obj)
34
+ @original = obj
35
+ end
36
+
37
+ def json_body
38
+ @json_body ||= JSON.parse(body)
39
+ end
40
+
41
+ def success?
42
+ original.is_a?(Net::HTTPSuccess)
43
+ end
44
+
45
+ def error
46
+ API::Error.new(response: self) unless success?
47
+ end
48
+ end
49
+
50
+ def initialize(host = DEFAULT_HOST)
51
+ @host = host
52
+ end
53
+
54
+ def request(method, endpoint, body: nil, params: {}, headers: {}, api_key: nil) # rubocop:disable Metrics/ParameterLists
55
+ api_key ||= DEFAULT_API_KEY
56
+ uri = build_uri(endpoint, params)
57
+ http_start(uri) { |http|
58
+ http.request(
59
+ build_request(method, uri, body: body, headers: headers, api_key: api_key),
60
+ )
61
+ }
62
+ end
63
+
64
+ def fetch_all(endpoint, resource_class, params: {}, api_key: nil)
65
+ response = request(
66
+ :get,
67
+ endpoint,
68
+ params: params,
69
+ api_key: api_key,
70
+ )
71
+ raise response.error unless response.success?
72
+
73
+ Resource.parse_collection(response.json_body, resource_class)
74
+ end
75
+
76
+ def fetch_one(endpoint, resource_class, params: {}, api_key: nil)
77
+ response = request(
78
+ :get,
79
+ endpoint,
80
+ params: params,
81
+ api_key: api_key,
82
+ )
83
+ raise response.error unless response.success?
84
+
85
+ Resource.parse_object(response.json_body, resource_class)
86
+ end
87
+
88
+ def self.endpoint(*items)
89
+ ([BASE_ENDPOINT] + items.map { |item|
90
+ case
91
+ when Rbvore.resource_subclass?(item)
92
+ item.pluralize
93
+ when item.nil?
94
+ nil
95
+ else
96
+ item.to_s
97
+ end
98
+ }.compact).join("/")
99
+ end
100
+
101
+ private
102
+
103
+ def build_uri(endpoint, params = nil)
104
+ url = if endpoint.start_with? "http"
105
+ endpoint
106
+ else
107
+ "https://#{@host}#{endpoint}"
108
+ end
109
+
110
+ URI.parse(url).tap { |uri|
111
+ next if params.nil? || params.empty?
112
+
113
+ uri.query = encode_form_data(params)
114
+ }
115
+ end
116
+
117
+ def encode_form_data(body)
118
+ body.map { |key, value| "#{key}=#{CGI.escape(value)}" }.join("&")
119
+ end
120
+
121
+ def build_request(method, uri, body: nil, headers: {}, api_key: nil)
122
+ method_class(method).new(uri.request_uri).tap { |r|
123
+ headers.each do |key, value|
124
+ r.add_field(key, value)
125
+ end
126
+ r.add_field "Accept", "application/json"
127
+ r.add_field "Api-Key", api_key unless api_key.nil?
128
+ set_body(r, body)
129
+ }
130
+ end
131
+
132
+ def http_start(uri, &block)
133
+ Response.new(Net::HTTP.start(uri.host, uri.port, http_opts, &block))
134
+ end
135
+
136
+ def http_opts
137
+ { use_ssl: true }
138
+ end
139
+
140
+ def set_body(request, body)
141
+ if body.is_a? String
142
+ request.body = body
143
+ elsif !body.nil?
144
+ request.add_field "Content-Type", "application/json"
145
+ request.body = body.to_json
146
+ end
147
+ end
148
+
149
+ def method_class(name)
150
+ case name.to_sym
151
+ when :get
152
+ Net::HTTP::Get
153
+ when :post
154
+ Net::HTTP::Post
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class Discount < Resource
5
+ attr_accessor :id, :available, :max_amount, :max_percent, :min_amount, :min_percent,
6
+ :min_ticket_total, :name, :open, :pos_id, :type, :value, :applies_to
7
+
8
+ def initialize(hash)
9
+ set_attributes(hash)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class Employee < Resource
5
+ attr_accessor :id, :check_name, :first_name, :last_name, :login, :middle_name,
6
+ :pos_id, :start_date
7
+
8
+ def initialize(hash)
9
+ set_attributes(hash)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class Item < Resource
5
+ attr_accessor :id, :name, :price, :quantity, :seat, :sent, :split, :comment
6
+ attr_timestamp_accessor :sent_at
7
+ attr_objects menu_item: MenuItem
8
+ attr_collections modifiers: ItemModifier, discounts: Discount
9
+
10
+ def initialize(hash)
11
+ set_attributes(hash)
12
+ end
13
+ end
14
+
15
+ class VoidedItem < Item; end
16
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class ItemModifier < Resource
5
+ attr_accessor :id, :name, :price, :quantity, :comment
6
+
7
+ def initialize(hash)
8
+ set_attributes(hash)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class UnknownLinkError < Error; end
5
+
6
+ class Link
7
+ attr_accessor :href, :type
8
+
9
+ def initialize(hash)
10
+ self.href = hash["href"]
11
+ self.type = hash["type"]
12
+ end
13
+
14
+ def type_name
15
+ @type_name ||= type.match(/name=(?<name>[a-z\_]+)/).named_captures["name"]
16
+ end
17
+
18
+ def resource_class
19
+ Rbvore.constantize(type_name)
20
+ end
21
+
22
+ def list?
23
+ type_name.end_with? "list"
24
+ end
25
+
26
+ def api
27
+ @api ||= API.new
28
+ end
29
+
30
+ def fetch(api_key: nil, params: {})
31
+ response = api.request(
32
+ :get,
33
+ href,
34
+ params: params,
35
+ api_key: api_key,
36
+ )
37
+ raise response.error unless response.success?
38
+
39
+ parse_resources(response.json_body)
40
+ end
41
+
42
+ def parse_resources(json_body)
43
+ if list?
44
+ Resource.parse_collection(json_body, resource_class)
45
+ else
46
+ Resource.parse_object(json_body, resource_class)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class Location < Resource
5
+ attr_accessor :address, :concept_name, :display_name, :name, :id, :custom_id,
6
+ :google_place_id, :health, :latitude, :longitude, :owner, :phone,
7
+ :pos_type, :status, :timezone, :website, :development
8
+ attr_timestamp_accessor :created, :modified
9
+ attr_collections(
10
+ tender_types: TenderType,
11
+ employees: Employee,
12
+ tables: Table,
13
+ order_types: OrderType,
14
+ revenue_centers: RevenueCenter,
15
+ tickets: Ticket,
16
+ )
17
+
18
+ def initialize(hash)
19
+ set_attributes(hash)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class MenuCategory < Resource
5
+ attr_accessor :id, :level, :name, :pos_id
6
+
7
+ def initialize(hash)
8
+ set_attributes(hash)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class MenuItem < Resource
5
+ attr_accessor :id, :barcodes, :in_stock, :name, :open, :pos_id, :price_per_unit
6
+
7
+ attr_collections menu_categories: MenuCategory
8
+
9
+ def initialize(hash)
10
+ set_attributes(hash)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class OrderType < Resource
5
+ attr_accessor :id, :name, :pos_id, :available
6
+
7
+ def initialize(hash)
8
+ set_attributes(hash)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rbvore
4
+ class Payment < Resource
5
+ attr_accessor :id, :amount, :change, :comment, :full_name, :status, :last4, :tip, :type
6
+ attr_objects tender_type: TenderType
7
+
8
+ def initialize(hash)
9
+ set_attributes(hash)
10
+ end
11
+
12
+ def api
13
+ @api ||= API.new
14
+ end
15
+
16
+ def create_3rd_party(location_id:, ticket_id:, auto_close: false, api_key: nil)
17
+ response = api.request(
18
+ :post,
19
+ self.class.endpoint(location_id: location_id, ticket_id: ticket_id),
20
+ body: third_party_body(auto_close),
21
+ api_key: api_key,
22
+ )
23
+ raise response.error unless response.success?
24
+
25
+ Payment.new(response.json_body)
26
+ end
27
+
28
+ def third_party_body(auto_close)
29
+ {
30
+ amount: amount,
31
+ tip: tip,
32
+ tender_type: tender_type,
33
+ auto_close: auto_close,
34
+ comment: comment,
35
+ type: "3rd_party",
36
+ }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbvore/resource/parsers'
4
+ require 'rbvore/resource/names'
5
+ require 'rbvore/resource/fetchers'
6
+
7
+ module Rbvore
8
+ class Resource
9
+ extend Names
10
+ extend Fetchers
11
+ extend Parsers
12
+ include Parsers
13
+
14
+ attr_reader :links
15
+
16
+ # define_link_getter sets up an attribute getter that fetches from
17
+ # the associated link, if available
18
+ def self.define_link_getter(name)
19
+ define_method(name) do |opts = {}|
20
+ stored_value = instance_variable_get("@#{name}")
21
+ return stored_value unless stored_value.nil?
22
+
23
+ instance_variable_set("@#{name}", fetch_link(name, api_key: opts[:api_key]))
24
+ end
25
+ end
26
+
27
+ # attr_objects sets up an associated object that can be parsed from json
28
+ def self.attr_objects(hash)
29
+ hash.each do |name, klass|
30
+ define_link_getter(name)
31
+ define_method("#{name}=") do |obj|
32
+ instance_variable_set(
33
+ "@#{name}",
34
+ parse_object(obj, klass),
35
+ )
36
+ end
37
+ end
38
+ end
39
+
40
+ # attr_collections sets up an associated list of objects that can be
41
+ # parsed from the json
42
+ def self.attr_collections(hash)
43
+ hash.each do |name, klass|
44
+ define_link_getter(name)
45
+ define_method("#{name}=") do |ary|
46
+ instance_variable_set(
47
+ "@#{name}",
48
+ parse_collection(ary, klass),
49
+ )
50
+ end
51
+ end
52
+ end
53
+
54
+ def self.attr_timestamp_accessor(*attrs)
55
+ attrs.each do |name|
56
+ attr_reader(name)
57
+ define_method("#{name}=") { |value|
58
+ instance_variable_set("@#{name}", parse_timestamp(value))
59
+ }
60
+ end
61
+ end
62
+
63
+ def set_attribute(key, value)
64
+ setter = "#{self.class.underscore(key)}="
65
+ send(setter, value) if respond_to?(setter)
66
+ end
67
+
68
+ def set_attributes(hash) # rubocop:disable Naming/AccessorMethodName
69
+ hash.each do |key, value|
70
+ set_attribute(key, value)
71
+ end
72
+ end
73
+
74
+ def fetch_link(name, api_key: nil, params: {})
75
+ link = links[name.to_sym]
76
+ raise UnknownLinkError, "Unknown link `#{name}` for #{self.class.singularize}" if link.nil?
77
+
78
+ link.fetch(api_key: api_key, params: params)
79
+ end
80
+
81
+ def _links=(hash)
82
+ @links = hash.each_with_object({}) { |(name, data), memo|
83
+ memo[name.to_sym] = Link.new(data)
84
+ }
85
+ end
86
+
87
+ def _embedded=(hash)
88
+ set_attributes(hash)
89
+ end
90
+
91
+ def inspect
92
+ "#<#{self.class} #{[@id, @name || @display_name].join(" ")}>"
93
+ end
94
+ end
95
+ end