rbvore 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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