clover_sandbox_simulator 1.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.
Files changed (25) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +10 -0
  3. data/README.md +316 -0
  4. data/bin/simulate +209 -0
  5. data/lib/clover_sandbox_simulator/configuration.rb +51 -0
  6. data/lib/clover_sandbox_simulator/data/restaurant/categories.json +39 -0
  7. data/lib/clover_sandbox_simulator/data/restaurant/discounts.json +39 -0
  8. data/lib/clover_sandbox_simulator/data/restaurant/items.json +238 -0
  9. data/lib/clover_sandbox_simulator/data/restaurant/modifiers.json +62 -0
  10. data/lib/clover_sandbox_simulator/data/restaurant/tenders.json +41 -0
  11. data/lib/clover_sandbox_simulator/generators/data_loader.rb +54 -0
  12. data/lib/clover_sandbox_simulator/generators/entity_generator.rb +164 -0
  13. data/lib/clover_sandbox_simulator/generators/order_generator.rb +540 -0
  14. data/lib/clover_sandbox_simulator/services/base_service.rb +111 -0
  15. data/lib/clover_sandbox_simulator/services/clover/customer_service.rb +82 -0
  16. data/lib/clover_sandbox_simulator/services/clover/discount_service.rb +58 -0
  17. data/lib/clover_sandbox_simulator/services/clover/employee_service.rb +82 -0
  18. data/lib/clover_sandbox_simulator/services/clover/inventory_service.rb +120 -0
  19. data/lib/clover_sandbox_simulator/services/clover/order_service.rb +170 -0
  20. data/lib/clover_sandbox_simulator/services/clover/payment_service.rb +123 -0
  21. data/lib/clover_sandbox_simulator/services/clover/services_manager.rb +49 -0
  22. data/lib/clover_sandbox_simulator/services/clover/tax_service.rb +53 -0
  23. data/lib/clover_sandbox_simulator/services/clover/tender_service.rb +117 -0
  24. data/lib/clover_sandbox_simulator.rb +43 -0
  25. metadata +195 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dc73877a0d3dbbce2dfe5b038167864972933519265d341665ee23646f37f5c1
4
+ data.tar.gz: 808ede5d13382014d7c2ab5f58379160c0983b7db8ead184032b70a5f39b453a
5
+ SHA512:
6
+ metadata.gz: afdb2f5ddd9026721f63328fb2efd4cc97799d952fea3288d8b090ca4daab696719df353eb8d515aafdfe5b62b165798cbde650cb9ffabaf7e827fae1487a321
7
+ data.tar.gz: 1218800960726b410b98d5b8d0ca7cfed1aed9a1f916214a257d456ca08373123724764ec04680d1eabde7ebd9f25d98d77a43301d54ee5d0fa2ca74000b7873
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ group :development, :test do
8
+ gem "pry", "~> 0.14"
9
+ gem "pry-byebug", "~> 3.10"
10
+ end
data/README.md ADDED
@@ -0,0 +1,316 @@
1
+ # Clover Sandbox Simulator
2
+
3
+ A Ruby gem for simulating Point of Sale operations in Clover sandbox environments. Generates realistic restaurant orders, payments, and transaction data for testing integrations with Clover's API.
4
+
5
+ ## Features
6
+
7
+ - **Realistic Restaurant Data**: Complete menu with 39 items across 7 categories (appetizers, entrees, sides, desserts, drinks, alcoholic beverages, specials)
8
+ - **Safe Sandbox Payments**: Uses Cash, Check, Gift Card, and other safe tenders (avoids broken Credit/Debit cards in Clover sandbox)
9
+ - **Split Payments**: Supports 1-4 tender splits per order, more common for larger parties
10
+ - **Meal Period Simulation**: Orders distributed across breakfast, lunch, happy hour, dinner, and late night with realistic weights
11
+ - **Dining Options**: Dine-in, To-Go, and Delivery with period-appropriate distributions
12
+ - **Dynamic Order Volume**: Different order counts for weekdays, Friday, Saturday, Sunday (40-120 orders/day)
13
+ - **Tips & Taxes**: Variable tip rates by dining option (15-25% dine-in, 0-15% takeout, 10-20% delivery)
14
+ - **Discounts**: 7 discount types including Happy Hour, Senior, Military, Employee, Birthday, and fixed amounts
15
+ - **Employees & Customers**: Auto-generated with realistic names and contact info
16
+ - **Party Size Variation**: 1-6 guests affecting item counts and split payment probability
17
+ - **Order Notes**: Random special instructions (allergies, modifications, VIP customers)
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'clover_sandbox_simulator'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ ```bash
30
+ bundle install
31
+ ```
32
+
33
+ Or install it yourself as:
34
+
35
+ ```bash
36
+ gem install clover_sandbox_simulator
37
+ ```
38
+
39
+ ## Configuration
40
+
41
+ Copy the sample environment file and configure your Clover credentials:
42
+
43
+ ```bash
44
+ cp .env.sample .env
45
+ ```
46
+
47
+ Edit `.env` with your Clover sandbox credentials:
48
+
49
+ ```env
50
+ CLOVER_MERCHANT_ID=your_merchant_id
51
+ CLOVER_API_TOKEN=your_api_token
52
+ CLOVER_ENVIRONMENT=https://sandbox.dev.clover.com/
53
+ LOG_LEVEL=INFO
54
+ TAX_RATE=8.25
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ### Quick Start
60
+
61
+ Run a full simulation (setup + generate orders):
62
+
63
+ ```bash
64
+ ./bin/simulate full
65
+ ```
66
+
67
+ ### Commands
68
+
69
+ ```bash
70
+ # Set up restaurant entities (categories, items, discounts, etc.)
71
+ ./bin/simulate setup
72
+
73
+ # Generate orders for today (random count based on day of week)
74
+ ./bin/simulate generate
75
+
76
+ # Generate a specific number of orders
77
+ ./bin/simulate generate -n 25
78
+
79
+ # Generate a realistic full day of restaurant operations
80
+ ./bin/simulate day
81
+
82
+ # Generate a busy day (2x normal volume)
83
+ ./bin/simulate day -m 2.0
84
+
85
+ # Generate a slow day (0.5x normal volume)
86
+ ./bin/simulate day -m 0.5
87
+
88
+ # Generate a lunch or dinner rush
89
+ ./bin/simulate rush -p lunch -n 20
90
+ ./bin/simulate rush -p dinner -n 30
91
+
92
+ # Run full simulation (setup + orders)
93
+ ./bin/simulate full
94
+
95
+ # Check current status
96
+ ./bin/simulate status
97
+
98
+ # Delete all entities (requires confirmation)
99
+ ./bin/simulate delete --confirm
100
+
101
+ # Enable verbose logging
102
+ ./bin/simulate generate -v
103
+ ```
104
+
105
+ ## Menu Structure
106
+
107
+ ### Categories
108
+ - Appetizers
109
+ - Entrees
110
+ - Sides
111
+ - Desserts
112
+ - Drinks
113
+ - Alcoholic Beverages
114
+ - Specials
115
+
116
+ ### Sample Items
117
+
118
+ | Category | Item | Price |
119
+ |----------|------|-------|
120
+ | Appetizers | Buffalo Wings | $12.99 |
121
+ | Appetizers | Loaded Nachos | $10.99 |
122
+ | Entrees | Classic Burger | $14.99 |
123
+ | Entrees | NY Strip Steak | $28.99 |
124
+ | Entrees | Grilled Salmon | $21.99 |
125
+ | Sides | French Fries | $4.99 |
126
+ | Desserts | Cheesecake | $7.99 |
127
+ | Drinks | Soft Drink | $2.99 |
128
+ | Alcoholic | Draft Beer | $5.99 |
129
+
130
+ ## Payment Tenders
131
+
132
+ **IMPORTANT**: Credit Card and Debit Card are **broken** in Clover sandbox. This gem intentionally avoids them.
133
+
134
+ Safe tenders used:
135
+ - Cash (preferred for orders under $20)
136
+ - Check
137
+ - Gift Card
138
+ - External Payment
139
+ - Mobile Payment
140
+ - Store Credit
141
+
142
+ The simulator uses whatever safe tenders are available in the Clover merchant account.
143
+
144
+ ## Tips
145
+
146
+ Tips vary by dining option to simulate realistic customer behavior:
147
+
148
+ | Dining Option | Min Tip | Max Tip |
149
+ |---------------|---------|---------|
150
+ | Dine-In (HERE) | 15% | 25% |
151
+ | To-Go | 0% | 15% |
152
+ | Delivery | 10% | 20% |
153
+
154
+ - Large parties (6+) automatically receive 18% auto-gratuity
155
+ - Split payments divide tips proportionally across tenders
156
+
157
+ ## Order Patterns
158
+
159
+ ### Daily Volume
160
+
161
+ | Day | Min Orders | Max Orders |
162
+ |-----|------------|------------|
163
+ | Weekday | 40 | 60 |
164
+ | Friday | 70 | 100 |
165
+ | Saturday | 80 | 120 |
166
+ | Sunday | 50 | 80 |
167
+
168
+ ### Meal Periods
169
+
170
+ Orders are distributed across realistic meal periods with weighted distribution:
171
+
172
+ | Period | Hours | Weight | Avg Items | Avg Party Size |
173
+ |--------|-------|--------|-----------|----------------|
174
+ | Breakfast | 7-10 AM | 15% | 2-4 | 1-2 |
175
+ | Lunch | 11 AM-2 PM | 30% | 2-5 | 1-4 |
176
+ | Happy Hour | 3-5 PM | 10% | 2-4 | 2-4 |
177
+ | Dinner | 5-9 PM | 35% | 3-6 | 2-6 |
178
+ | Late Night | 9-11 PM | 10% | 2-4 | 1-3 |
179
+
180
+ ### Dining Options
181
+
182
+ Each meal period has different dining option distributions:
183
+
184
+ | Period | Dine-In | To-Go | Delivery |
185
+ |--------|---------|-------|----------|
186
+ | Breakfast | 40% | 50% | 10% |
187
+ | Lunch | 35% | 45% | 20% |
188
+ | Happy Hour | 80% | 15% | 5% |
189
+ | Dinner | 70% | 15% | 15% |
190
+ | Late Night | 50% | 30% | 20% |
191
+
192
+ ## Architecture
193
+
194
+ ```
195
+ clover_sandbox_simulator/
196
+ ├── bin/simulate # CLI entry point
197
+ ├── lib/
198
+ │ ├── clover_sandbox_simulator.rb # Gem entry point
199
+ │ └── clover_sandbox_simulator/
200
+ │ ├── configuration.rb # Environment config
201
+ │ ├── services/
202
+ │ │ ├── base_service.rb
203
+ │ │ └── clover/ # Clover API services
204
+ │ │ ├── inventory_service.rb
205
+ │ │ ├── order_service.rb
206
+ │ │ ├── payment_service.rb
207
+ │ │ ├── tender_service.rb
208
+ │ │ ├── tax_service.rb
209
+ │ │ ├── discount_service.rb
210
+ │ │ ├── employee_service.rb
211
+ │ │ ├── customer_service.rb
212
+ │ │ └── services_manager.rb
213
+ │ ├── generators/
214
+ │ │ ├── data_loader.rb
215
+ │ │ ├── entity_generator.rb
216
+ │ │ └── order_generator.rb
217
+ │ └── data/
218
+ │ └── restaurant/ # JSON data files
219
+ │ ├── categories.json
220
+ │ ├── items.json
221
+ │ ├── discounts.json
222
+ │ ├── tenders.json
223
+ │ └── modifiers.json
224
+ └── spec/ # RSpec tests
225
+ ```
226
+
227
+ ## Development
228
+
229
+ ```bash
230
+ # Run all tests
231
+ bundle exec rspec
232
+
233
+ # Run tests with documentation format
234
+ bundle exec rspec --format documentation
235
+
236
+ # Run specific test file
237
+ bundle exec rspec spec/services/clover/tender_service_spec.rb
238
+
239
+ # Run linter
240
+ bundle exec rubocop
241
+
242
+ # Open console
243
+ bundle exec irb -r ./lib/clover_sandbox_simulator
244
+ ```
245
+
246
+ ## Testing
247
+
248
+ The gem includes comprehensive RSpec tests with WebMock for HTTP stubbing.
249
+
250
+ ### Test Coverage
251
+
252
+ - **268 examples, 0 failures**
253
+ - Configuration validation
254
+ - Data loading from JSON files
255
+ - All Clover API services:
256
+ - InventoryService (categories, items)
257
+ - OrderService (create, line items, dining options)
258
+ - PaymentService (single and split payments)
259
+ - TenderService (safe tenders, split selection)
260
+ - TaxService (rates, calculation)
261
+ - DiscountService (percentage and fixed)
262
+ - EmployeeService (CRUD, random selection)
263
+ - CustomerService (CRUD, anonymous orders)
264
+ - ServicesManager (memoization, lazy loading)
265
+ - Entity generator idempotency
266
+ - Order generator (meal periods, dining options, tips)
267
+ - Edge cases (nil handling, empty arrays, API errors)
268
+
269
+ ### Test Files
270
+
271
+ ```
272
+ spec/
273
+ ├── configuration_spec.rb
274
+ ├── generators/
275
+ │ ├── data_loader_spec.rb
276
+ │ ├── entity_generator_spec.rb
277
+ │ └── order_generator_spec.rb
278
+ └── services/clover/
279
+ ├── customer_service_spec.rb
280
+ ├── discount_service_spec.rb
281
+ ├── employee_service_spec.rb
282
+ ├── inventory_service_spec.rb
283
+ ├── order_service_spec.rb
284
+ ├── payment_service_spec.rb
285
+ ├── services_manager_spec.rb
286
+ ├── tax_service_spec.rb
287
+ └── tender_service_spec.rb
288
+ ```
289
+
290
+ ### Idempotency Verification
291
+
292
+ All setup operations are idempotent - running them multiple times will not create duplicates:
293
+
294
+ ```ruby
295
+ # This is safe to run multiple times
296
+ generator = CloverSandboxSimulator::Generators::EntityGenerator.new
297
+ generator.setup_all # First run: creates entities
298
+ generator.setup_all # Second run: skips existing, returns same results
299
+ ```
300
+
301
+ The tests verify:
302
+ - Categories are not duplicated
303
+ - Items are not duplicated
304
+ - Discounts are not duplicated
305
+ - Employees/customers only created if count threshold not met
306
+
307
+ ## Clover API Notes
308
+
309
+ - **Sandbox URL**: `https://sandbox.dev.clover.com/`
310
+ - **API Version**: v3
311
+ - **Authentication**: Bearer token (OAuth)
312
+ - **Date Limitation**: Clover sandbox only allows creating orders for TODAY
313
+
314
+ ## License
315
+
316
+ MIT License
data/bin/simulate ADDED
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "clover_sandbox_simulator"
6
+ require "thor"
7
+
8
+ module CloverSandboxSimulator
9
+ # Command-line interface for Clover Sandbox Simulator
10
+ class CLI < Thor
11
+ class_option :verbose, type: :boolean, aliases: "-v", desc: "Enable verbose logging"
12
+
13
+ desc "setup", "Set up restaurant entities (categories, items, discounts, employees, customers)"
14
+ option :business_type, type: :string, default: "restaurant", desc: "Business type (restaurant, retail, salon)"
15
+ def setup
16
+ configure_logging
17
+
18
+ puts "🍽️ Clover Sandbox Simulator - Entity Setup"
19
+ puts "=" * 50
20
+
21
+ generator = Generators::EntityGenerator.new(business_type: options[:business_type].to_sym)
22
+ generator.setup_all
23
+
24
+ puts "\n✅ Setup complete!"
25
+ end
26
+
27
+ desc "generate", "Generate orders for today"
28
+ option :count, type: :numeric, aliases: "-n", desc: "Number of orders to generate"
29
+ def generate
30
+ configure_logging
31
+
32
+ puts "🍽️ Clover Sandbox Simulator - Order Generation"
33
+ puts "=" * 50
34
+
35
+ generator = Generators::OrderGenerator.new
36
+ count = options[:count]
37
+
38
+ orders = generator.generate_today(count: count)
39
+
40
+ puts "\n✅ Generated #{orders.size} orders!"
41
+ end
42
+
43
+ desc "day", "Generate a realistic full day of restaurant operations"
44
+ option :multiplier, type: :numeric, aliases: "-m", default: 1.0, desc: "Order multiplier (0.5 = slow day, 2.0 = busy day)"
45
+ def day
46
+ configure_logging
47
+
48
+ puts "🍽️ Clover Sandbox Simulator - Realistic Restaurant Day"
49
+ puts "=" * 50
50
+
51
+ generator = Generators::OrderGenerator.new
52
+ orders = generator.generate_realistic_day(multiplier: options[:multiplier])
53
+
54
+ puts "\n✅ Generated #{orders.size} orders!"
55
+ end
56
+
57
+ desc "rush", "Generate a lunch or dinner rush"
58
+ option :period, type: :string, aliases: "-p", default: "dinner", desc: "Meal period (breakfast, lunch, happy_hour, dinner, late_night)"
59
+ option :count, type: :numeric, aliases: "-n", default: 15, desc: "Number of orders"
60
+ def rush
61
+ configure_logging
62
+
63
+ period = options[:period].to_sym
64
+ count = options[:count]
65
+
66
+ puts "🍽️ Clover Sandbox Simulator - #{period.to_s.upcase} Rush"
67
+ puts "=" * 50
68
+
69
+ generator = Generators::OrderGenerator.new
70
+ data = generator.send(:fetch_required_data)
71
+
72
+ unless data
73
+ puts "❌ Failed to fetch data. Run setup first."
74
+ return
75
+ end
76
+
77
+ orders = []
78
+ count.times do |i|
79
+ order = generator.send(:create_realistic_order,
80
+ period: period,
81
+ data: data,
82
+ order_num: i + 1,
83
+ total_in_period: count
84
+ )
85
+
86
+ if order
87
+ orders << order
88
+ generator.send(:update_stats, order, period)
89
+ end
90
+ end
91
+
92
+ generator.send(:print_summary)
93
+ puts "\n✅ Generated #{orders.size} #{period} orders!"
94
+ end
95
+
96
+ desc "full", "Run full simulation (setup + generate orders)"
97
+ option :count, type: :numeric, aliases: "-n", desc: "Number of orders to generate"
98
+ option :business_type, type: :string, default: "restaurant", desc: "Business type"
99
+ def full
100
+ configure_logging
101
+
102
+ puts "🍽️ Clover Sandbox Simulator - Full Simulation"
103
+ puts "=" * 50
104
+
105
+ # Setup entities
106
+ entity_gen = Generators::EntityGenerator.new(business_type: options[:business_type].to_sym)
107
+ entity_gen.setup_all
108
+
109
+ puts "\n"
110
+
111
+ # Generate orders
112
+ order_gen = Generators::OrderGenerator.new
113
+ orders = order_gen.generate_today(count: options[:count])
114
+
115
+ puts "\n✅ Full simulation complete!"
116
+ puts " Orders generated: #{orders.size}"
117
+ end
118
+
119
+ desc "delete", "Delete all entities (use with caution!)"
120
+ option :confirm, type: :boolean, desc: "Confirm deletion"
121
+ def delete
122
+ unless options[:confirm]
123
+ puts "⚠️ This will delete ALL entities from Clover!"
124
+ puts " Run with --confirm to proceed."
125
+ return
126
+ end
127
+
128
+ configure_logging
129
+
130
+ puts "🗑️ Deleting all entities..."
131
+
132
+ generator = Generators::EntityGenerator.new
133
+ generator.delete_all
134
+
135
+ puts "\n✅ All entities deleted!"
136
+ end
137
+
138
+ desc "status", "Show current Clover merchant status"
139
+ def status
140
+ configure_logging
141
+
142
+ puts "🍽️ Clover Sandbox Simulator - Status"
143
+ puts "=" * 50
144
+
145
+ services = Services::Clover::ServicesManager.new
146
+
147
+ categories = services.inventory.get_categories
148
+ items = services.inventory.get_items
149
+ tenders = services.tender.get_safe_tenders
150
+ employees = services.employee.get_employees
151
+ customers = services.customer.get_customers
152
+ discounts = services.discount.get_discounts
153
+
154
+ puts "Categories: #{categories.size}"
155
+ puts "Menu Items: #{items.size}"
156
+ puts "Tenders: #{tenders.size} (sandbox-safe)"
157
+ puts "Employees: #{employees.size}"
158
+ puts "Customers: #{customers.size}"
159
+ puts "Discounts: #{discounts.size}"
160
+
161
+ puts "\n📋 Categories:"
162
+ categories.each { |c| puts " - #{c['name']}" }
163
+
164
+ puts "\n💳 Safe Tenders (no credit/debit):"
165
+ tenders.each { |t| puts " - #{t['label']}" }
166
+ end
167
+
168
+ desc "version", "Show version"
169
+ def version
170
+ puts "Clover Sandbox Simulator v1.0.0"
171
+ end
172
+
173
+ private
174
+
175
+ def configure_logging
176
+ if options[:verbose]
177
+ CloverSandboxSimulator.configuration.logger.level = Logger::DEBUG
178
+ else
179
+ CloverSandboxSimulator.configuration.logger.level = Logger::INFO
180
+ end
181
+ end
182
+
183
+ def print_order_summary(orders)
184
+ return if orders.empty?
185
+
186
+ total_revenue = 0
187
+ total_tips = 0
188
+ total_tax = 0
189
+
190
+ orders.each do |order|
191
+ next unless order && order["payments"]&.dig("elements")
192
+
193
+ order["payments"]["elements"].each do |payment|
194
+ total_revenue += payment["amount"] || 0
195
+ total_tips += payment["tipAmount"] || 0
196
+ total_tax += payment["taxAmount"] || 0
197
+ end
198
+ end
199
+
200
+ puts "\n📊 Summary:"
201
+ puts " Revenue: $#{total_revenue / 100.0}"
202
+ puts " Tips: $#{total_tips / 100.0}"
203
+ puts " Tax: $#{total_tax / 100.0}"
204
+ puts " Total: $#{(total_revenue + total_tips + total_tax) / 100.0}"
205
+ end
206
+ end
207
+ end
208
+
209
+ CloverSandboxSimulator::CLI.start(ARGV)
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CloverSandboxSimulator
4
+ class Configuration
5
+ attr_accessor :merchant_id, :api_token, :environment, :log_level, :tax_rate, :business_type
6
+
7
+ def initialize
8
+ @merchant_id = ENV.fetch("CLOVER_MERCHANT_ID", nil)
9
+ @api_token = ENV.fetch("CLOVER_API_TOKEN", nil)
10
+ @environment = normalize_url(ENV.fetch("CLOVER_ENVIRONMENT", "https://sandbox.dev.clover.com/"))
11
+ @log_level = parse_log_level(ENV.fetch("LOG_LEVEL", "INFO"))
12
+ @tax_rate = ENV.fetch("TAX_RATE", "8.25").to_f
13
+ @business_type = ENV.fetch("BUSINESS_TYPE", "restaurant").to_sym
14
+ end
15
+
16
+ def validate!
17
+ raise ConfigurationError, "CLOVER_MERCHANT_ID is required" if merchant_id.nil? || merchant_id.empty?
18
+ raise ConfigurationError, "CLOVER_API_TOKEN is required" if api_token.nil? || api_token.empty?
19
+
20
+ true
21
+ end
22
+
23
+ def logger
24
+ @logger ||= Logger.new($stdout).tap do |log|
25
+ log.level = @log_level
26
+ log.formatter = proc do |severity, datetime, _progname, msg|
27
+ timestamp = datetime.strftime("%Y-%m-%d %H:%M:%S")
28
+ "[#{timestamp}] #{severity.ljust(5)} | #{msg}\n"
29
+ end
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def normalize_url(url)
36
+ url = url.strip
37
+ url.end_with?("/") ? url : "#{url}/"
38
+ end
39
+
40
+ def parse_log_level(level)
41
+ case level.to_s.upcase
42
+ when "DEBUG" then Logger::DEBUG
43
+ when "INFO" then Logger::INFO
44
+ when "WARN" then Logger::WARN
45
+ when "ERROR" then Logger::ERROR
46
+ when "FATAL" then Logger::FATAL
47
+ else Logger::INFO
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,39 @@
1
+ {
2
+ "categories": [
3
+ {
4
+ "name": "Appetizers",
5
+ "sort_order": 1,
6
+ "description": "Start your meal right"
7
+ },
8
+ {
9
+ "name": "Entrees",
10
+ "sort_order": 2,
11
+ "description": "Main courses"
12
+ },
13
+ {
14
+ "name": "Sides",
15
+ "sort_order": 3,
16
+ "description": "Perfect accompaniments"
17
+ },
18
+ {
19
+ "name": "Desserts",
20
+ "sort_order": 4,
21
+ "description": "Sweet endings"
22
+ },
23
+ {
24
+ "name": "Drinks",
25
+ "sort_order": 5,
26
+ "description": "Non-alcoholic beverages"
27
+ },
28
+ {
29
+ "name": "Alcoholic Beverages",
30
+ "sort_order": 6,
31
+ "description": "Beer, wine, and cocktails"
32
+ },
33
+ {
34
+ "name": "Specials",
35
+ "sort_order": 7,
36
+ "description": "Chef's daily specials"
37
+ }
38
+ ]
39
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "discounts": [
3
+ {
4
+ "name": "Happy Hour",
5
+ "percentage": 15,
6
+ "description": "15% off during happy hour (3-6 PM)"
7
+ },
8
+ {
9
+ "name": "Senior Discount",
10
+ "percentage": 10,
11
+ "description": "10% off for seniors 65+"
12
+ },
13
+ {
14
+ "name": "Military Discount",
15
+ "percentage": 15,
16
+ "description": "15% off for active duty and veterans"
17
+ },
18
+ {
19
+ "name": "Employee Discount",
20
+ "percentage": 25,
21
+ "description": "25% off for employees"
22
+ },
23
+ {
24
+ "name": "Birthday Special",
25
+ "percentage": 20,
26
+ "description": "20% off for birthday celebrations"
27
+ },
28
+ {
29
+ "name": "$5 Off",
30
+ "amount": 500,
31
+ "description": "$5 off orders over $30"
32
+ },
33
+ {
34
+ "name": "$10 Off",
35
+ "amount": 1000,
36
+ "description": "$10 off orders over $50"
37
+ }
38
+ ]
39
+ }