waxx 0.1.2 → 0.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 93fa484e47a0d9a2f46a2833d4a2a64b34275be8
4
- data.tar.gz: 89279ba78c8d5c8927a2cd49c1c4fffe2fe556a6
2
+ SHA256:
3
+ metadata.gz: 0f46260fb9e41a49af9eb045a81a88ef270e01100626d8b4458f8e6b9c633ebc
4
+ data.tar.gz: 5e0071cbe8095d8c084ca2089a4a69c8d46f397620f493ebf80f43f9e5b96192
5
5
  SHA512:
6
- metadata.gz: cc81172fccb3147846eaf7912abce34616cb467c5fcadfd7c5fb24a473885effa55e643a93ae9c16dec3cf9d71f4f95690985f405b59599d43af4cc084e1afe6
7
- data.tar.gz: 3d9df1ef5ac36f734ce4720a31657c0bd4791ef3f04e2b953f0b5904bcaf29b9c69142d96bbbad30e29a4785d24ee5b6160f2aa1b790462ecb8f39f729a8ef29
6
+ metadata.gz: bd026b73c6a231fb17d4edf1aa03810bec7760356b21697c352b1cce723efa2120f779ad1ac049c3ab364d5a273c37384b77f9bd03676b6d845636c1228ef533
7
+ data.tar.gz: 64b91a88da24468dc8735bf2b4c79686962a726ae71e2e06abc6c4e64dff3db4492f02250394e9670742857368ed7c22fcda0bf3e253de8a3e6600f3d1fc7e01
checksums.yaml.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -1,10 +1,6 @@
1
1
  # Waxx - Web Application X(x)
2
2
 
3
- **NOTICE: This is the first public release of Waxx and the APIs may change. Do not build large production apps with it yet!**
4
-
5
- **NOTICE: Waxx does not run on Windows yet. Working on it. Stay tuned.**
6
-
7
- The Waxx Framerwork is a high perfomance, functional-inspired (but not truly functional), web application development environment written in Ruby and inspired by Go and Haskel.
3
+ The Waxx Framework is a high perfomance, functional-inspired (but not truly functional), web application development environment written in Ruby and inspired by Go and Haskel.
8
4
 
9
5
  ## Goals
10
6
 
@@ -16,11 +12,11 @@ The Waxx Framerwork is a high perfomance, functional-inspired (but not truly fun
16
12
 
17
13
  ## Target Users
18
14
 
19
- The Waxx Framework was developed to build CRUD applications and REST and RPC services. It scales very well on multi-core machines and is [will be] suitable for very large deployments.
15
+ The Waxx Framework was developed to build CRUD applications and REST and RPC services. It scales very well on multi-core machines and is suitable for very large deployments.
20
16
 
21
17
  ## Who's Behind This
22
18
 
23
- Waxx was developed by Dan Fitzpatrick at [ePark Labs](https://www.eparklabs.com/).
19
+ Waxx was developed by Dan Fitzpatrick at [ePark Labs](https://www.eparklabs.com/).
24
20
 
25
21
  ## Hello World
26
22
 
@@ -63,7 +59,7 @@ waxx on
63
59
 
64
60
  The Waxx gem is cryptographically signed to be sure the gem you install hasn't been tampered with.
65
61
  Because of this you need to add the public key to your list of trusted gem certs.
66
- Follow theese direction. (You only need step one the first time you install the gem.)
62
+ Follow these direction. (You only need step one the first time you install the gem.)
67
63
 
68
64
  ```bash
69
65
  sudo gem cert --add <(curl -s https://www.waxx.io/waxx-gem-public-key.pem)
@@ -79,23 +75,23 @@ Then run `waxx buff` (waxx off && waxx on) or if you prefer `waxx restart`
79
75
  See [Install Waxx](https://www.waxx.io/doc/install) for complete details.
80
76
 
81
77
  ### High Performance
82
- Waxx is multi-threaded queue-based system.
83
- You specify the number of threads in the config file.
84
- Each thread is prespawned and each thread makes it's own database connection.
85
- Requests are received and put into a FIFO request queue.
86
- The threads work through the queue.
87
- Each request, including session management, a database query, access control, and rendering in HTML or JSON is approximately 1-2ms (on a modern Xeon server).
88
- With additional libraries, Waxx also easily generates XML, XLSX, CSV, PDF, etc.
89
-
90
- ### Easy to Grok
91
- Waxx has no Classes.
92
- It is Module-based and the Modules have methods (functions).
93
- Each method within a Module is given parameters and the method runs in isolation.
94
- There are no instance variables and no global variables.
95
- Consequently, it is very easy to understand what any method does and it is very easy to test methods.
96
- You can call any method in the whole system from the console using `waxx console`.
97
- Passing in the same variables to a function will always return the same result.
98
- Waxx does have `x.res.out` variable, which is appended to with `x << "text"`, that is passed into each method and any method can append to the response body or set response headers.
78
+ Waxx is multi-threaded queue-based system.
79
+ You specify the number of threads in the config file.
80
+ Each thread is prespawned and each thread makes it's own database connection.
81
+ Requests are received and put into a FIFO request queue.
82
+ The threads work through the queue.
83
+ Each request, including session management, a database query, access control, and rendering in HTML or JSON is approximately 1-2ms (on a modern Xeon server).
84
+ With additional libraries, Waxx also easily generates XML, XLSX, CSV, PDF, etc.
85
+
86
+ ### Easy to Grok
87
+ Waxx has no Classes.
88
+ It is Module-based and the Modules have methods (functions).
89
+ Each method within a Module is given parameters and the method runs in isolation.
90
+ There are no instance variables and no global variables.
91
+ Consequently, it is very easy to understand what any method does and it is very easy to test methods.
92
+ You can call any method in the whole system from the console using `waxx console`.
93
+ Passing in the same variables to a function will always return the same result.
94
+ Waxx does have `x.res.out` variable, which is appended to with `x << "text"`, that is passed into each method and any method can append to the response body or set response headers.
99
95
  So it is not truly functional because this is considered a side effect.
100
96
  My opinion is that when you are building a response, then copying the response on every method is a waste of resources.
101
97
  So it does have this side effect by design.
@@ -103,7 +99,7 @@ So it does have this side effect by design.
103
99
  #### Waxx Terminology
104
100
 
105
101
  - Object: A database table or database object or a container for some specific functionality
106
- - Object `has` fields (an array of Hashes). A field is both a database field/column/attribute and a UI control (for HTML apps)
102
+ - Object `has` fields (an array of Hashes). A field is both a database field/column/attribute and a UI control (for HTML apps)
107
103
  - Field `is` (represents) a single object (INNER JOIN) or many related objects (LEFT JOIN)
108
104
  - Object `runs` a URL path - business logic (normally get from or post to a view)
109
105
  - View: Like a DB view -- fields from one or more tables/objects
@@ -146,26 +142,26 @@ Waxx was built with code maintainablity in mind. The following principles help i
146
142
  1. Simple to know where the code is located for any URI. A request to /person/list will start in the `app/person/person.rb` file and normally use the view defined in the file `app/person/list.rb`
147
143
  2. Fields are defined upfront. The fields you want to use in your app are defined in the Object file `app/person/person.rb`
148
144
  3. Field have attributes that make a lot of UI development simple (optional). has(email: {label: "Email Address" ...})`
149
- 4. Views allow you to see exactly what is on an interface and all the business logic. Only the fields on a view can be updated so it is impossible to taint the database by passing in extra parameters.
145
+ 4. Views allow you to see exactly what is on an interface and all the business logic. Only the fields on a view can be updated so it is impossible to taint the database by passing in extra parameters.
150
146
  5. Most rendering is automatic unless you want to do special stuff. You can use pure Ruby functions or your favorite template engine. The View file `app/person/list.rb` contains all of the fields, joined tables, and layout for a view.
151
147
  6. Full visibility into the external API and each endpoint's access control allows to immediate auditing of who can see and do what.
152
148
 
153
149
  There are no routes.
154
- All paths are `/:app/:act/:arg1/:arg2/...`.
155
- The URL maps to an App and runs the act (method).
156
- For example: `example.com/person/list` will execute the `list` method in the `App::Person` module.
157
- This method is defined in `app/person/person.rb`.
158
- Another example: A request to `/website_page/content/3` will execute the `content` method in the `App::WebsitePage` app and pass in `3` as the first parameter after 'x'.
159
- There is a default app and a default method in each app.
150
+ All paths are `/:app/:act/:arg1/:arg2/...`.
151
+ The URL maps to an App and runs the act (method).
152
+ For example: `example.com/person/list` will execute the `list` method in the `App::Person` module.
153
+ This method is defined in `app/person/person.rb`.
154
+ Another example: A request to `/website_page/content/3` will execute the `content` method in the `App::WebsitePage` app and pass in `3` as the first parameter after 'x'.
155
+ There is a default app and a default method in each app.
160
156
  So a request to `example.com/` will show the home page if the default app is `website` and the default method in website is `home`.
161
157
 
162
158
 
163
159
  ### File Structure
164
- Waxx places each module in it's own directory. This includes the Object, Runner, Views, Layouts, and Tests.
165
- I normally place my app-specific javascript and css in this same folder as well.
166
- In this way, all of the functionality and features of a specific App or Module are fully self-contained.
167
- However, you can optionally put your files anywhere and require them in your code.
168
- So if you like all the objects to be in one folder you can do that.
160
+ Waxx places each module in it's own directory. This includes the Object, Runner, Views, Layouts, and Tests.
161
+ I normally place my app-specific javascript and css in this same folder as well.
162
+ In this way, all of the functionality and features of a specific App or Module are fully self-contained.
163
+ However, you can optionally put your files anywhere and require them in your code.
164
+ So if you like all the objects to be in one folder you can do that.
169
165
  If you work with a large team where backend and frontend people do not overlap, then maybe that will work for you.
170
166
 
171
167
  This is a normal structure:
@@ -231,7 +227,7 @@ This is a normal structure:
231
227
  | `-- deploy # The script to deploy to stage (run on the production server(s))
232
228
  |-- private # A folder for private files (served by the file app if included)
233
229
  `-- public # The public folder (Web server should have this as the root)
234
-
230
+
235
231
  ```
236
232
 
237
233
  The Waxx::Object has two purposes:
@@ -297,7 +293,7 @@ require_relative 'list' # The List View is defined here
297
293
  require_relative 'record' # The Record View is defined here
298
294
  ```
299
295
 
300
- A view is like a database view (not like a Rails view). The view specifies what tables/objects and fields/properties are going to be displayed and potentially edited. The Html layout module is like a Rails view. Other layouts include: Json, Csv, Pdf, Xlsx.
296
+ A view is like a database view (not like a Rails view). The view specifies what tables/objects and fields/properties are going to be displayed and potentially edited. The Html layout module is like a Rails view. Other layouts include: Json, Csv, Pdf, Xlsx.
301
297
 
302
298
  **app/person/list.rb** *(This is the view that lists the users)*
303
299
 
@@ -305,7 +301,7 @@ A view is like a database view (not like a Rails view). The view specifies what
305
301
  module App::Person::List
306
302
  extend Waxx::View
307
303
  extend self
308
-
304
+
309
305
  has(
310
306
  :id,
311
307
  :first_name,
@@ -313,28 +309,28 @@ module App::Person::List
313
309
  :email
314
310
  # This view does not include the bio field
315
311
  )
316
-
312
+
317
313
  module Html
318
314
  extend Waxx::Html
319
315
  extend self
320
-
316
+
321
317
  def get(x, data, message={})
322
- # This method appends to x and includes your site layout and nav.
318
+ # This method appends to x and includes your site layout and nav.
323
319
  # The content attribute is what goes in the content area of the page.
324
320
  App::Html.page(x,
325
321
  title: "People",
326
322
  content: content(x, data)
327
323
  )
328
324
  end
329
-
325
+
330
326
  def content(x, data)
331
327
  # You put your HTML output here using:
332
328
  %(<p>HTHL or a template engine</p>)
333
329
  end
334
-
330
+
335
331
  end
336
332
  end
337
- ```
333
+ ```
338
334
 
339
335
  **app/person/record.rb** *(This is the view to view, edit, update, and delete a record)*
340
336
 
@@ -342,7 +338,7 @@ end
342
338
  module App::Person::Record
343
339
  extend Waxx::View
344
340
  extend self
345
-
341
+
346
342
  has(
347
343
  :id,
348
344
  :first_name,
@@ -350,23 +346,23 @@ module App::Person::Record
350
346
  :email,
351
347
  :bio
352
348
  )
353
-
349
+
354
350
  module Html
355
351
  extend Waxx::Html
356
352
  extend self
357
-
353
+
358
354
  def get(x, data, message={})
359
355
  App::Html.page(
360
356
  title: "#{data['first_name']} #{data['last_name']}",
361
357
  content: content(x, data)
362
358
  )
363
359
  end
364
-
360
+
365
361
  def content(x, data)
366
362
  # You put your HTML output here using:
367
363
  %(<p>HTHL or a template engine</p>)
368
364
  end
369
-
365
+
370
366
  def post(x)
371
367
  # Following a post, redirect to the list view
372
368
  x.res.redirect "/person/list"
@@ -425,7 +421,7 @@ Then in the list view, we can add the company that the person is associated with
425
421
  module App::Person::List
426
422
  extend Waxx::View
427
423
  extend self
428
-
424
+
429
425
  has(
430
426
  :id,
431
427
  :first_name,
@@ -435,7 +431,7 @@ module App::Person::List
435
431
  )
436
432
  end
437
433
  ```
438
-
434
+
439
435
  In this case the attribute "company_name" will be added to the view and is the value of the "name" field in the company table. The syntax for this is `<name>: <relationship_name (as defined in the object)>.<field>`.
440
436
 
441
437
  ### LEFT JOIN (is: name:table.field+)
@@ -462,10 +458,10 @@ end
462
458
 
463
459
  *Note: The + sign after the related attribute make this join a left join (Oracle style)*
464
460
 
465
- INNER JOIN (If you don't want to show invoices with no items):
461
+ INNER JOIN (If you don't want to show invoices with no items):
466
462
 
467
463
  `id: {pkey: true, is:"items: invoice_item.invoice_id"}`
468
-
464
+
469
465
  LEFT JOIN (If you want to show invoices with no items):
470
466
 
471
467
  `id: {pkey: true, is:"items: invoice_item.invoice_id+"}`
@@ -496,7 +492,7 @@ This will show a list of all invoices and the items on the invoices:
496
492
  module App::Invoice::Items
497
493
  extend Waxx::View
498
494
  extend self
499
-
495
+
500
496
  has(
501
497
  :id,
502
498
  :invoice_date,
@@ -509,14 +505,14 @@ module App::Invoice::Items
509
505
  )
510
506
  end
511
507
  ```
512
-
508
+
513
509
  This will generate the following SQL:
514
510
 
515
511
  ```sql
516
- SELECT invoice.id, invoice.invoice_date, company.name as company, product.name as product,
512
+ SELECT invoice.id, invoice.invoice_date, company.name as company, product.name as product,
517
513
  items.description as desc, items.quantity as qty, items.unit_price as price,
518
514
  (items.quantity * items.unit_price) as total
519
- FROM invoice
515
+ FROM invoice
520
516
  LEFT JOIN invoice_item AS items ON invoice.id = invoice_item.invoice_id
521
517
  INNER JOIN company ON invoice.customer_id = company.id
522
518
  INNER JOIN product ON items.product_id = product.id
@@ -535,7 +531,7 @@ The join table is just another object in Waxx
535
531
  module App::Usr
536
532
  extend Waxx::Pg
537
533
  extend self
538
-
534
+
539
535
  has({
540
536
  id: {pkey: true, is:"group_member: usr_grp.usr_id+"},
541
537
  email: {validate: "email"},
@@ -547,7 +543,7 @@ The join table is just another object in Waxx
547
543
  module App::Grp
548
544
  extend Waxx::Pg
549
545
  extend self
550
-
546
+
551
547
  has({
552
548
  id: {pkey: true, is:"group_members: usr_grp.grp_id+"},
553
549
  name: {required: true}
@@ -558,19 +554,19 @@ The join table is just another object in Waxx
558
554
  module App::UsrGrp
559
555
  extend Waxx::Pg
560
556
  extend self
561
-
557
+
562
558
  has({
563
559
  id: {pkey: true},
564
560
  usr_id: {required: true, is:"usr:usr.id"},
565
561
  grp_id: {required: true, is:"grp:grp.id"}
566
562
  })
567
563
  end
568
-
564
+
569
565
  # View that joins all three tables (show all users and groups they are in)
570
566
  module App::Usr::Groups
571
567
  extend Waxx::View
572
568
  extend self
573
-
569
+
574
570
  has(
575
571
  :id,
576
572
  :email,
@@ -616,7 +612,7 @@ Each slash-delimited argument after the first two are treated as arguments to th
616
612
  module App::Artist
617
613
  extend Waxx::Pg
618
614
  extend self
619
-
615
+
620
616
  runs(
621
617
  in: {
622
618
  desc: "Show a list of artists in an area",
@@ -628,7 +624,7 @@ module App::Artist
628
624
  end
629
625
  ```
630
626
 
631
- In this case all three parameters are required. An error will be raised if the city is missing.
627
+ In this case all three parameters are required. An error will be raised if the city is missing.
632
628
  There are two options: Add default values or use a proc instead of a lambda:
633
629
 
634
630
  ```
@@ -643,10 +639,10 @@ get: -> (x, country="us", state_prov="", city="") { }
643
639
  ```
644
640
 
645
641
  NOTE: You can use `return` in `lambda` and `->` constructs, but you need to use `break` in `proc` constructs to stop processing.
646
-
642
+
647
643
  ### Variable Act / not_found
648
644
 
649
- What if you want the act be a variable like `/artist/david-bowie` or `/artist/motorhead`?
645
+ What if you want the act be a variable like `/artist/david-bowie` or `/artist/motorhead`?
650
646
 
651
647
  You define **`not_found`** in your Object runs method:
652
648
 
@@ -654,7 +650,7 @@ You define **`not_found`** in your Object runs method:
654
650
  module App::Artist
655
651
  extend Waxx::Pg
656
652
  extend self
657
-
653
+
658
654
  runs(
659
655
  default: "list",
660
656
  list: {
@@ -699,7 +695,7 @@ Waxx includes a full user and session management system. The following apps are
699
695
  Using these apps allow you to add users and groups and put users in groups. You define your access control lists for each method. There are several levels of permissions. The following seven code blocks are parts of the same file:
700
696
 
701
697
  ### Example ACLs
702
- ACLs are defined as a attribute (`acl: [nil|string|array|hash|lambda]`) of each method options hash.
698
+ ACLs are defined as a attribute (`acl: [nil|string|array|hash|lambda]`) of each method options hash.
703
699
 
704
700
  The following code blocks are different examples of the acl attribute in practice.
705
701
 
@@ -709,7 +705,7 @@ The following code blocks are different examples of the acl attribute in practic
709
705
  module App::Product
710
706
  extend Waxx::Pg
711
707
  extend self
712
-
708
+
713
709
  runs(
714
710
  default: "list",
715
711
  ```
@@ -777,7 +773,7 @@ If the proc or lambda returns true, then the user is allowed to proceed, otherwi
777
773
 
778
774
  ```ruby
779
775
  special: {
780
- desc: "View and edit a product from a specific IP
776
+ desc: "View and edit a product from a specific IP
781
777
  or if the user has a secret key in their session",
782
778
  acl: -> (x) {
783
779
  x.req.env['X-REAL-IP'] == "10.10.10.10" or x.usr['secret'] == "let-me-in"
@@ -812,10 +808,10 @@ End the object file
812
808
 
813
809
  A fast JSON response for an autocomplete form field
814
810
 
815
- If you want to have a quick JSON response for an autocomplete -- Just use a Waxx::Object and bypass the Waxx::View and layout (Json, HTML, etc.).
811
+ If you want to have a quick JSON response for an autocomplete -- Just use a Waxx::Object and bypass the Waxx::View and layout (Json, HTML, etc.).
816
812
  Direct access is available to the database driver with `x.db.app` where `app` is the name of the database connection defined in your config.yaml file.
817
- In this case, as a user types in an autocomplete input box, the browser sends a request to: `/artist/autocomplete.json?q=da`
818
- When the .json extension is used, the response content type will be application/json.
813
+ In this case, as a user types in an autocomplete input box, the browser sends a request to: `/artist/autocomplete.json?q=da`
814
+ When the .json extension is used, the response content type will be application/json.
819
815
  What the user types would be in the `q` attribute.
820
816
 
821
817
  PostgreSQL DB:
@@ -834,8 +830,8 @@ module App::Artist
834
830
  desc: "Show a list of artists that match the 'q' param",
835
831
  get: -> (x) {
836
832
  x << x.db.app.exec("
837
- SELECT id, name
838
- FROM artist
833
+ SELECT id, name
834
+ FROM artist
839
835
  WHERE name ILIKE $1
840
836
  ORDER BY name
841
837
  LIMIT 20",
@@ -870,7 +866,7 @@ module App::Artist
870
866
  end
871
867
  ```
872
868
 
873
- Both of these should return a response in less than one millisecond (assuming your data is indexed and running on descent hardware).
869
+ Both of these should return a response in less than one millisecond (assuming your data is indexed and running on decent hardware).
874
870
 
875
871
  That is the intro. Give it a whirl.
876
872
 
data/lib/waxx/app.rb CHANGED
@@ -27,7 +27,7 @@ module Waxx::App
27
27
  # The layout of the error page (html) is defined in app/app/error/html.rb
28
28
  def not_found(x, title:"Not Found", message:nil)
29
29
  begin
30
- if message.nil?
30
+ if message.nil? and @runs[:website] and @runs[:website][:page]
31
31
  return @runs[:website][:page][:get].call(x, *(x.args))
32
32
  end
33
33
  rescue => e
@@ -66,7 +66,7 @@ module Waxx::App
66
66
  # 5. args: The args to pass to the method (after x) (Array)
67
67
  #
68
68
  # Example: `App.run(x, :person, :record, :get, [1])` will call the get method with the parameter "1" of the record handler defined in App::Person
69
- def run(x, app, act, meth, args)
69
+ def run(x, app, act, meth, args=[])
70
70
  if @runs[app.to_sym][act][meth.to_sym]
71
71
  begin
72
72
  @runs[app.to_sym][act][meth.to_sym][x, *args]
@@ -100,7 +100,11 @@ module Waxx::App
100
100
  # Layouts in app/app/error/*
101
101
  def error(x, status:200, type:"request", title:"An error occurred", message:"", args: [])
102
102
  x.res.status = status
103
- App[:app_error][type.to_sym][:get][x, title, message, *args]
103
+ if App[:app_error][type.to_sym]
104
+ App[:app_error][type.to_sym][:get][x, title, message, *args]
105
+ else
106
+ x << "ERROR: #{title} - #{message}"
107
+ end
104
108
  end
105
109
 
106
110
  ##
@@ -158,6 +162,8 @@ module Waxx::App
158
162
 
159
163
  def login_needed(x)
160
164
  if x.ext == "json"
165
+ x.res.status = 400
166
+ x << {ok: false, msg: 'Login needed: Session did not pass ACL'}
161
167
  else
162
168
  App::Html.render(x,
163
169
  title: "Please Login",
data/lib/waxx/console.rb CHANGED
@@ -112,16 +112,19 @@ module Waxx::Console
112
112
  require 'irb'
113
113
  puts "waxx console"
114
114
  #help = "Use the source, Luke"
115
- x = Waxx::Console.x
116
- binding.irb
117
- #IRB.setup(nil)
118
- #workspace = IRB::WorkSpace.new(self)
119
- #irb = IRB::Irb.new(workspace)
120
- #IRB.conf[:MAIN_CONTEXT] = irb.context
121
- #irb.eval_input
122
- #require 'lib/waxx/irb.rb'
123
- #@x = Waxx::Console.x
124
- #IRB.start_session(self) #"#{opts[:base]}/lib/waxx/irb_env.rb")
115
+ begin
116
+ x = Waxx::Console.x
117
+ binding.irb
118
+ rescue
119
+ IRB.setup(nil)
120
+ workspace = IRB::WorkSpace.new(self)
121
+ irb = IRB::Irb.new(workspace)
122
+ IRB.conf[:MAIN_CONTEXT] = irb.context
123
+ irb.eval_input
124
+ require 'lib/waxx/irb.rb'
125
+ @x = Waxx::Console.x
126
+ IRB.start_session(self) #"#{opts[:base]}/lib/waxx/irb_env.rb")
127
+ end
125
128
  else
126
129
  puts "Error: You need to call 'waxx console' from the root of a waxx installation."
127
130
  exit 1
@@ -159,7 +162,8 @@ module Waxx::Console
159
162
  File.open(m_file, "w"){|f|
160
163
  f.puts "BEGIN;\n\n\n\nCOMMIT;"
161
164
  }
162
- system "/usr/bin/env #{ENV['EDITOR']} #{m_file}"
165
+ puts "Created #{m_file}"
166
+ system "/usr/bin/env #{ENV['EDITOR'] || 'vim'} #{m_file}"
163
167
  end
164
168
 
165
169
  def test(target, opts)
data/lib/waxx/database.rb CHANGED
@@ -62,7 +62,7 @@ module Waxx::Database
62
62
  def migrate(db_only=nil, opts={})
63
63
  dbs = connections
64
64
  dbs.each{|name, db|
65
- next if db_only and db_only != db
65
+ next if db_only and db_only.to_sym != name
66
66
  puts "Migrating: db.#{name}"
67
67
  # get the latest version
68
68
  latest = db.exec("SELECT value FROM waxx WHERE name = 'db.#{name}.migration.last'").first['value']
@@ -77,4 +77,12 @@ module Waxx::Database
77
77
  puts "Migration complete"
78
78
  end
79
79
 
80
+ def [](name)
81
+ app[name]
82
+ end
83
+
84
+ def collection(name)
85
+ app[name]
86
+ end
87
+
80
88
  end
data/lib/waxx/encrypt.rb CHANGED
@@ -12,7 +12,7 @@ module Waxx::Encrypt
12
12
  when :b64
13
13
  Base64.encode64(aes.update(str.to_s) + aes.final).chomp
14
14
  when :url
15
- http_escape(Base64.encode64(aes.update(str.to_s) + aes.final).chomp)
15
+ Waxx::Http.escape(Base64.encode64(aes.update(str.to_s) + aes.final).chomp)
16
16
  when :bin
17
17
  aes.update(str.to_s) + aes.final
18
18
  else
@@ -28,7 +28,7 @@ module Waxx::Encrypt
28
28
  when :b64
29
29
  aes.update(Base64.decode64(str.to_s + "\n")) + aes.final
30
30
  when :url
31
- aes.update(Base64.decode64(http_unescape(str.to_s) + "\n")) + aes.final
31
+ aes.update(Base64.decode64(Waxx::Http.unescape(str.to_s) + "\n")) + aes.final
32
32
  when :bin
33
33
  aes.update(str.to_s) + aes.final
34
34
  else
data/lib/waxx/http.rb CHANGED
@@ -8,9 +8,10 @@ module Waxx::Http
8
8
  ContentTypes
9
9
  end
10
10
 
11
- def ctype(t, default="application/octet-stream")
11
+ def content_type(t, default="application/octet-stream")
12
12
  ContentTypes[t.to_sym] || default
13
13
  end
14
+ alias ctype content_type
14
15
 
15
16
  def time(t=Time.new.utc)
16
17
  t.strftime('%a, %d %b %Y %H:%M:%S UTC')
@@ -116,15 +117,20 @@ module Waxx::Http
116
117
  if %w(PUT POST PATCH).include? meth
117
118
  data = io.read(env['Content-Length'].to_i)
118
119
  Waxx.debug "data.size: #{data.size} #{env['Content-Type']}"
119
- case env['Content-Type']
120
- when /x-www-form-urlencoded/
121
- post = query_string_to_hash(data).freeze
122
- when /multipart/
123
- post = parse_multipart(env, data).freeze
124
- when /json/
125
- post = (JSON.parse(data)).freeze
126
- else
127
- post = data.freeze
120
+ if env['Content-Length'].to_i == 0
121
+ post = {}.freeze
122
+ data = nil
123
+ else
124
+ case (env['Content-Type'] || env['content-type'] || env['Content-type'])
125
+ when /x-www-form-urlencoded/
126
+ post = query_string_to_hash(data).freeze
127
+ when /multipart/
128
+ post = parse_multipart(env, data).freeze
129
+ when /json/
130
+ post = (JSON.parse(data)).freeze
131
+ else
132
+ post = data.freeze
133
+ end
128
134
  end
129
135
  else
130
136
  post = {}.freeze
data/lib/waxx/mongodb.rb CHANGED
@@ -2,6 +2,7 @@
2
2
  # Released under the Apache Version 2 License. See LICENSE.txt.
3
3
 
4
4
  module Waxx::Mongodb
5
+ extend self
5
6
 
6
7
  attr :db
7
8
  attr :table
@@ -20,6 +21,10 @@ module Waxx::Mongodb
20
21
  has(cols) if cols
21
22
  end
22
23
 
24
+ def connect(str)
25
+ Mongo::Client.new(str).database
26
+ end
27
+
23
28
  def has(opts=nil)
24
29
  init if @table.nil?
25
30
  return @columns if opts.nil?
@@ -45,6 +50,10 @@ module Waxx::Mongodb
45
50
  }
46
51
  end
47
52
 
53
+ def connect(conf)
54
+ ::Mongo::Client.new(conf).database
55
+ end
56
+
48
57
  def [](n)
49
58
  @columns[n.to_sym]
50
59
  end
data/lib/waxx/patch.rb CHANGED
@@ -2,6 +2,11 @@
2
2
  # Released under the Apache Version 2 License. See LICENSE.txt.
3
3
 
4
4
  # Patches to Ruby Classes
5
+ class BigDecimal
6
+ def to_json(*a)
7
+ to_f.to_json(*a)
8
+ end
9
+ end
5
10
  class Date
6
11
  # HTML format for a date
7
12
  def h
@@ -45,7 +50,8 @@ end
45
50
  class Hash
46
51
  # Add an symbol/string indifferent access to a hash
47
52
  def /(k)
48
- self[k.to_sym] || self[k.to_s]
53
+ return self[k.to_sym] if self.has_key?(k.to_sym)
54
+ self[k.to_s]
49
55
  end
50
56
  end
51
57
  class NilClass
@@ -64,6 +70,10 @@ class NilClass
64
70
  def to_sym
65
71
  "".to_sym
66
72
  end
73
+ # Nil is empty
74
+ def empty?
75
+ true
76
+ end
67
77
  end
68
78
  class Numeric
69
79
  # HTML format (self -- no escaping needed)
@@ -82,6 +92,18 @@ class Numeric
82
92
  return x if size == 0
83
93
  x << (d + (num_parts[1].to_s + "0000000")[0,size])
84
94
  end
95
+ def ordinal
96
+ case self % 100
97
+ when 11, 12, 13 then "#{self}th"
98
+ else
99
+ case self % 10
100
+ when 1 then "#{self}st"
101
+ when 2 then "#{self}nd"
102
+ when 3 then "#{self}rd"
103
+ else "#{self}th"
104
+ end
105
+ end
106
+ end
85
107
  end
86
108
  class String
87
109
  # Escape HTML entities
@@ -97,6 +119,10 @@ class String
97
119
  return x if size == 0
98
120
  x << (d + (num_parts[1].to_s + "0000000")[0,size])
99
121
  end
122
+ # Capitalize all words
123
+ def capitalize_all
124
+ split(/[ _]/).map{|l| l.capitalize}.join(' ')
125
+ end
100
126
  end
101
127
  class Time
102
128
  # HTML format
@@ -130,7 +156,6 @@ class TrueClass
130
156
  raise "Unknown format in TrueClass.f: #{format}. Needs to be :yn, :tf, :icon, or :num."
131
157
  end
132
158
  end
133
-
134
159
  # Show true as 1
135
160
  def to_i
136
161
  1
data/lib/waxx/pdf.rb CHANGED
@@ -41,12 +41,12 @@ module Waxx::Pdf
41
41
  return_file(x)
42
42
  end
43
43
 
44
- def render_file(x, pdf)
45
- pdf.render_file file_path(x)
44
+ def render_file(x, pdf, path)
45
+ pdf.render_file path
46
46
  end
47
47
 
48
- def return_file(x)
49
- File.open(file_path(x), "rb"){|f| x << f.read}
48
+ def return_file(x, path)
49
+ File.open(path, "rb"){|f| x << f.read}
50
50
  end
51
51
 
52
52
  def show_grid(pdf)
data/lib/waxx/pg.rb CHANGED
@@ -143,7 +143,16 @@ module Waxx::Pg
143
143
  #[sql.join(" "), vals.flatten]
144
144
  Waxx.debug sql
145
145
  Waxx.debug vals.join(", ")
146
- x.db[@db].exec(sql.join(" "), vals.flatten)
146
+ begin
147
+ x.db[@db].exec(sql.join(" "), vals.flatten)
148
+ rescue => e
149
+ if e =~ /connection/
150
+ x.db[@db].reset
151
+ x.db[@db].exec(sql.join(" "), vals.flatten)
152
+ else
153
+ raise e
154
+ end
155
+ end
147
156
  end
148
157
 
149
158
  def get_by_id(x, id, select=nil, view:nil)
@@ -151,6 +160,11 @@ module Waxx::Pg
151
160
  end
152
161
  alias by_id get_by_id
153
162
 
163
+ def get_by_ulid(x, ulid, select=nil, view:nil)
164
+ get(x, select: select, view: view, where: ["ulid = $1", [ulid]]).first
165
+ end
166
+ alias by_ulid get_by_ulid
167
+
154
168
  def post(x, data, cols:nil, returning:nil, view:nil, &blk)
155
169
  if view
156
170
  cols = view.columns.select{|n,c| c[:table] == @table}
@@ -193,9 +207,7 @@ module Waxx::Pg
193
207
  vals = []
194
208
  ret = []
195
209
  i = 1
196
- Waxx.debug "data: #{data}"
197
210
  cols.each{|n,v|
198
- Waxx.debug "col: #{n}: #{v.inspect}"
199
211
  if data.has_key? n.to_s or data.has_key? n.to_sym
200
212
  set << "#{n} = $#{i}"
201
213
  vals << cast(v, data/n)
@@ -208,10 +220,10 @@ module Waxx::Pg
208
220
  vals << id
209
221
  Waxx.debug(sql)
210
222
  Waxx.debug(vals)
211
- x.db[@db].exec(sql, vals).first
223
+ x.db[@db].exec(sql, vals).first
212
224
  end
213
225
  alias patch put
214
-
226
+
215
227
  def put_post(x, id, data, cols:nil, returning:nil, view: nil)
216
228
  q = nil
217
229
  q = get_by_id(x, id, @pkey) if id.to_i > 0
@@ -230,8 +242,8 @@ module Waxx::Pg
230
242
  end
231
243
  end
232
244
 
233
- def delete(x, id)
234
- x.db[@db].exec("DELETE FROM #{@table} WHERE #{@pkey} = $1", [id])
245
+ def delete(x, id, where: nil)
246
+ x.db[@db].exec("DELETE FROM #{@table} WHERE #{@pkey} = $1 #{where}", [id])
235
247
  end
236
248
 
237
249
  def order(req_order, default_order='')
data/lib/waxx/res.rb CHANGED
@@ -84,8 +84,20 @@ module Waxx
84
84
  def complete
85
85
  re = out.join
86
86
  headers["Content-Length"] = re.bytesize
87
- server.print head
88
- server.print re
87
+ begin
88
+ server.print head
89
+ server.print re
90
+ # Connection reset by peer
91
+ rescue Errno::ECONNRESET => e
92
+ Waxx.debug(e.class)
93
+ Waxx.debug(e)
94
+ Waxx.debug(e.backtrace.join("\n"))
95
+ # Broken pipe
96
+ rescue Errno::EPIPE => e
97
+ Waxx.debug(e.class)
98
+ Waxx.debug(e)
99
+ Waxx.debug(e.backtrace.join("\n"))
100
+ end
89
101
  end
90
102
 
91
103
  def cookie(name:"", value:nil, domain:nil, expires:nil, path:"/", secure:true, http_only: false, same_site: "Lax")
data/lib/waxx/server.rb CHANGED
@@ -17,18 +17,17 @@ module Waxx::Server
17
17
  attr :queue
18
18
 
19
19
  def parse_uri(r)
20
- Waxx.debug "parse_uri"
20
+ Waxx.debug "parse_uri: #{r}", 7
21
21
  meth, uri, ver = r.split(" ")
22
22
  path, params = uri.split("?", 2)
23
- _, app, act, arg = path.split(".").first.split("/", 4)
24
- app = Waxx['default']['app'] if app.to_s == ''
25
- act = App[app.to_sym][:default] if act.to_s == '' and App[app.to_sym]
26
- act = Waxx['default']['act'] if act.to_s == ''
27
- oid = arg.split("/").first.gsub(/[^0-9]/,"").to_i rescue 0
28
- ext = path =~ /\./ ? path.split(".").last.downcase : Waxx['default']['ext']
29
- args = arg.split("/") rescue []
23
+ parts = path.split('/')
24
+ ext = parts.last.include?('.') ? parts.last.split('.').last : Waxx['default']['ext'] rescue Waxx['default']['ext']
25
+ parts[parts.size - 1] = parts.last.to_s.sub(/.#{ext}$/,'') if parts.size > 0
26
+ app = parts[1].to_s.empty? ? Waxx['default']['app'] : parts[1]
27
+ act = parts[2].to_s.empty? ? (App[app.to_sym][:default] || Waxx['default']['act']) : parts[2] rescue Waxx['default']['act']
28
+ args = parts.slice(3,99) || []
29
+ oid = args.first.to_s.gsub(/[^0-9]/,"").to_i rescue 0
30
30
  get = Waxx::Http.query_string_to_hash(params).freeze
31
- Waxx.debug "parse_uri.oid #{oid}"
32
31
  [meth, uri, app, act, oid, args, ext, get]
33
32
  end
34
33
 
@@ -90,6 +89,7 @@ module Waxx::Server
90
89
  end
91
90
  #Waxx.debug "no-file"
92
91
  env, head = Waxx::Http.parse_head(io)
92
+ Waxx.debug [Time.now.to_s, meth, uri].join(" "), 2
93
93
  #Waxx.debug head, 9
94
94
  cookie = Waxx::Http.parse_cookie(env['Cookie'])
95
95
  begin
@@ -104,7 +104,17 @@ module Waxx::Server
104
104
  Waxx.debug e.to_s, 1
105
105
  ua = {}
106
106
  end
107
- post, data = Waxx::Http.parse_data(env, meth, io, head)
107
+ begin
108
+ post, data = Waxx::Http.parse_data(env, meth, io, head)
109
+ rescue => e
110
+ post = nil
111
+ req = Waxx::Req.new(env, data, meth, uri, get, post, cookie, start_time).freeze
112
+ res = Waxx::Res.new(io, 400, default_response_headers(req, ext), [], [], [])
113
+ jobs = []
114
+ x = Waxx::X.new(req, res, usr, ua, db, meth.downcase.to_sym, app, act, oid, args, ext, jobs).freeze
115
+ fatal_error(x, e)
116
+ return finish(x, io)
117
+ end
108
118
  req = Waxx::Req.new(env, data, meth, uri, get, post, cookie, start_time).freeze
109
119
  res = Waxx::Res.new(io, 200, default_response_headers(req, ext), [], [], [])
110
120
  jobs = []
@@ -171,7 +181,7 @@ module Waxx::Server
171
181
  def fatal_error(x, e)
172
182
  x.res.status = 503
173
183
  puts "FATAL ERROR: #{e}\n#{e.backtrace}"
174
- report = "APPLICATION ERROR\n=================\n\nUSR:\n\n#{x.usr.map{|n,v| "#{n}: #{v}"}.join("\n")}\n\nERROR:\n\n#{e}\n#{e.backtrace.join("\n")}\n\nENV:\n\n#{x.req.env.map{|n,v| "#{n}: #{v}"}.join("\n")}\n\nGET:\n\n#{x.req.get.map{|n,v| "#{n}: #{v}"}.join("\n")}\n\nPOST:\n\n#{x.req.post.map{|n,v| "#{n}: #{v}"}.join("\n")}\n\n"
184
+ report = "APPLICATION ERROR\n=================\n\nUSR:\n\n#{x.usr.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\nERROR:\n\n#{e}\n#{e.backtrace.join("\n")}\n\nENV:\n\n#{x.req.env.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\nGET:\n\n#{x.req.get.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\nPOST:\n\n#{x.req.post.map{|n,v| "#{n}: #{v}"}.join("\n") rescue nil}\n\n"
175
185
  if Waxx['debug']['on_screen']
176
186
  x << "<pre>#{report.h}</pre>"
177
187
  else
@@ -189,16 +199,16 @@ module Waxx::Server
189
199
  from_email = Waxx['site']['support_email']
190
200
  subject = "[Bug] #{Waxx['site']['name']} #{x.meth}:#{x.req.uri}"
191
201
  # Send email via DB
192
- App::Email.post(x, d:{
202
+ App::Email.post(x,
193
203
  to_email: to_email,
194
204
  from_email: from_email,
195
205
  subject: subject,
196
206
  body_text: report
197
- })
207
+ )
198
208
  rescue => e2
199
209
  begin
200
210
  # Send email directly
201
- Mail.deliver do
211
+ ::Mail.deliver do
202
212
  from from_email
203
213
  to to_email
204
214
  subject subject
@@ -249,7 +259,13 @@ module Waxx::Server
249
259
  Thread.current[:last_used] = Time.new.to_i
250
260
  Waxx.debug "Create thread #{Thread.current[:name]}"
251
261
  loop do
252
- Waxx::Server.process_request(@@queue.pop, Thread.current[:db])
262
+ Waxx.debug "Thread loop start", 9
263
+ begin
264
+ Waxx::Server.process_request(@@queue.pop, Thread.current[:db])
265
+ rescue => e
266
+ Waxx.debug "Error: process_request loop: #{e} #{e.backtrace}", 1
267
+ end
268
+ Waxx.debug "Thread loop end", 9
253
269
  end
254
270
  end
255
271
  end
@@ -260,8 +276,13 @@ module Waxx::Server
260
276
  @@queue = Queue.new
261
277
  thread_count = Waxx['server']['min_threads'] || Waxx['server']['threads'] || 4
262
278
  1.upto(thread_count).each do |i|
263
- create_thread(i)
279
+ begin
280
+ create_thread(i)
281
+ rescue => e
282
+ Waxx.debug "Error creating thread #{i}: #{e}", 1
283
+ end
264
284
  end
285
+ Waxx.debug "Created #{thread_count} threads", 7
265
286
  thread_count
266
287
  end
267
288
 
data/lib/waxx/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Waxx
2
- Version = '0.1.2'
2
+ Version = '0.1.3'
3
3
  end
data/lib/waxx/view.rb CHANGED
@@ -352,8 +352,8 @@ module Waxx::View
352
352
 
353
353
  ##
354
354
  # Save data
355
- def put_post(x, id, data, args:nil)
356
- @object.put_post(x, id, data, view: self)
355
+ def put_post(x, id, data, args:nil, returning: nil)
356
+ @object.put_post(x, id, data, view: self, returning: returning)
357
357
  end
358
358
  alias post put_post
359
359
  alias put put_post
data/lib/waxx/waxx.rb CHANGED
@@ -42,7 +42,7 @@ module Waxx
42
42
  # )
43
43
  # # Set the level in config.yaml (debug.level) of what level or lower to ouutput
44
44
  def debug(str, level=3)
45
- puts str.to_s if level <= Waxx['debug']['level'].to_i
45
+ puts "#{Time.new} #{Thread.current[:name]} #{str}" if level <= Waxx['debug']['level'].to_i
46
46
  end
47
47
 
48
48
  ##
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waxx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Fitzpatrick
@@ -11,26 +11,30 @@ bindir:
11
11
  cert_chain:
12
12
  - |
13
13
  -----BEGIN CERTIFICATE-----
14
- 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
- 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
- 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
- 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
- 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
- E9mgVoC6TnsAQ4w9Z4JPMEKXxiso3WfqTQJo4d9rfdlFZ/84Wu6VPScH24A2Zxkt
20
- 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
- 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
- 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
- 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
- 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
- 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
- eC5pbzANBgkqhkiG9w0BAQUFAAOCAQEAGcX2EJNHw2VBwnWG5YAp5XdrWrhzKtZw
27
- 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
- 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
- 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
- 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
- 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
32
36
  -----END CERTIFICATE-----
33
- date: 2017-07-27 00:00:00.000000000 Z
37
+ date: 2019-07-27 00:00:00.000000000 Z
34
38
  dependencies: []
35
39
  description: Waxx is a high performace REST/RPC hybrid web application development
36
40
  framework.
@@ -132,8 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
136
  - !ruby/object:Gem::Version
133
137
  version: '0'
134
138
  requirements: []
135
- rubyforge_project:
136
- rubygems_version: 2.6.8
139
+ rubygems_version: 3.0.4
137
140
  signing_key:
138
141
  specification_version: 4
139
142
  summary: A fast and flexible application development framework
metadata.gz.sig CHANGED
@@ -1,3 +1,4 @@
1
- m8ŧj���D����K����(�,82��"pܲ��E��!��=����^����5�UcGYtG#&���z��(��C\jr�Ux�� r�%��2�"��a
2
- �Y�����M��ŷ ��Po�$a���cm~���H�v��-��E��c�* �21p���W�^�.o�����=3���k��?zE��_p��}��i��H�5nP)(�U���15���� �Y�����P
3
- h7�/����I��0����a[nW�
1
+ N�����r��£���x{}��ܾ-�b�2+�֢� �QL��UQ �$o@�ɽw�X@zy�L�z22�F�K&z|���]��{��6W5��A��l����v0y��W�ѝa<�~� �P�|�q2�bg�� �Й/Y�C �ۭ]#�/,�f�̚�bUP�V8���
2
+ +�q��
3
+ (Jjސ��Hm^@dG�epu?s�v��lG-��
4
+ �a�0�dH��<l OK�t`&!?��[x� �~�k�8���M!�.h�G�)SNM��a�i-��"3dڰ՝�M�F@