pretzel 0.1.3 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +30 -4
- data/docs/01-00-application.md +106 -0
- data/docs/01-01-routing.md +67 -0
- data/docs/01-02-filters.md +25 -0
- data/docs/01-04-mapping.md +36 -0
- data/docs/02-00-conveniece-methods.md +45 -0
- data/docs/02-01-json.md +35 -0
- data/docs/03-00-templating.md +123 -0
- data/docs/04-01-middleware.md +17 -0
- data/docs/04-02-extensions.md +117 -0
- data/lib/pretzel/core.rb +5 -1
- data/lib/pretzel/ext/convenience.rb +10 -8
- data/lib/pretzel/ext/json.rb +6 -4
- data/lib/pretzel/ext/templating.rb +20 -16
- data/lib/pretzel/extends.rb +15 -0
- data/lib/pretzel/version.rb +1 -1
- data/test/scrap.rb +5 -3
- data/test/scrap_extension.rb +39 -0
- data/test/test_extension.rb +61 -0
- data/test/test_json.rb +1 -1
- data/test/test_templating.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eda1d2b5c8b88a860d1881278ca7782a5a739cb6
|
4
|
+
data.tar.gz: 1f23c29e34fbae8f463a528e4a311dcfadd8f0cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db7f4114905d34429e9a41efb623963a71b915293baef73a3e866f6e4d5eb441e2bb8f5f4b7d1f2409ccde6fed9695d1fdc88e09d476fd10a0e05a7620ab15a2
|
7
|
+
data.tar.gz: 45fd5052b6c18e97ba811823afe2dbb9f086996fb01a6975786993a14ebf437dd87bd43b72167f1c409c87f93fe8b6a24c9d9165d05863eb1b3a1f24aaefb7db
|
data/README.md
CHANGED
@@ -1,7 +1,33 @@
|
|
1
|
+
## The Pretzel suite
|
1
2
|
|
2
|
-
|
3
|
-

|
3
|
+
[](https://travis-ci.org/pretzelhands/pretzel)
|
4
|
+
[](https://badge.fury.io/rb/pretzel)
|
5
5
|
[](https://codeclimate.com/github/pretzelhands/pretzel)
|
6
6
|
|
7
|
-
|
7
|
+
---
|
8
|
+
|
9
|
+
Welcome to the canonical repo of the Pretzel suite, a tiny web framework inspired by the ideas of Sinatra and Nancy. This framework has some strong opinions on how to structure an application and thus may not be suited for everyone.
|
10
|
+
|
11
|
+
If you like convention-over-configuration and building web applications from small building blocks along the lines of the UNIX philosophy, you have come to the right place.
|
12
|
+
|
13
|
+
### Philosophy
|
14
|
+
|
15
|
+
The Pretzel suite is built upon the UNIX philosopy. A piece of software should do one thing and do it well. The Pretzel core itself is a tiny 144 lines in 3 modules. It basically only provides routing, filters and it exposes some Rack middleware.
|
16
|
+
|
17
|
+
After that everything is an extension. Even the templating and the status/redirect/session methods. The goal is to create a framework which you can take and stick together the way you need it.
|
18
|
+
|
19
|
+
### Documentation
|
20
|
+
|
21
|
+
The documentation is available in the `docs` folder. It is sorted by a xx-yy-name pattern, where xx is a major and yy a minor chapter. The documentation takes the approach to teach by example, thus you will get lots of code snippets to visualize how things work.
|
22
|
+
|
23
|
+
### Bug reports
|
24
|
+
|
25
|
+
If you found a bug, feel free to send it in via the issue tracker on the right side. Or, if you know some code, maybe write a patch for it yourself! I usually lint my code with `ruby -wc`, and I'd ask you to do the same. Also don't forget to *write and run* tests. Otherwise I may go on neverending rants and we both don't want that.
|
26
|
+
|
27
|
+
### Who's responsible for this and why?
|
28
|
+
|
29
|
+
The dude behind all of this is me, [Richard Blechinger.](http://twitter.com/F0lis) On the internet I'm more commonly known by nicknames "Folis" and "Pretzelhands" (See where the framework name comes from?). I'm a web developer who tries to learn as many things as humanly possible. This is why the Pretzel suite exists, too. It started as endeavour to look at Rack and how it works.
|
30
|
+
|
31
|
+
### Licensing
|
32
|
+
|
33
|
+
MIT everytime all the time. Go ahead and do what you want.
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Basic Pretzel Application
|
2
|
+
|
3
|
+
The Pretzel suite comes with a very small core consisting of about 144 lines split
|
4
|
+
into 3 modules: Core, Routing::Evaluation and Routing::Methods. Every Pretzel application
|
5
|
+
also automatically includes the Extension::Convenience module which provides some
|
6
|
+
neat wrappers around common functionality.
|
7
|
+
|
8
|
+
## Directory structure
|
9
|
+
|
10
|
+
Before we get down and dirty with the code, let's talk management. The Pretzel suite
|
11
|
+
has strong opinions on where things should go. The basic folder structure for a Pretzel
|
12
|
+
application looks like this:
|
13
|
+
|
14
|
+
```
|
15
|
+
my_app
|
16
|
+
|
|
17
|
+
|- config.ru
|
18
|
+
|- application.rb
|
19
|
+
|- assets
|
20
|
+
|- views
|
21
|
+
|- includes
|
22
|
+
|
23
|
+
```
|
24
|
+
|
25
|
+
`config.ru` is your rackup file. You can read more about that down below. The `application.rb`
|
26
|
+
file can be named whatever you want, and it should contain the class declaration
|
27
|
+
your application.
|
28
|
+
|
29
|
+
The `assets` directory is a folder for your static content. CSS/JS, images, etc.
|
30
|
+
Everything is mapped 1-to-1 in the URL scheme, so if you put a file under `/assets/css/style.css`
|
31
|
+
you will find it under `/assets/css/style.css` when the application is running.
|
32
|
+
|
33
|
+
The `views` folder is where your `liquid` templates go. (See 03-00-templating for more
|
34
|
+
information). The `includes` folder inside contains the `liquid` partials.
|
35
|
+
|
36
|
+
**NOTE:** These settings are fixed and may not be changed at runtime. Convention over
|
37
|
+
configuration takes effect here.
|
38
|
+
|
39
|
+
## Creating an application
|
40
|
+
|
41
|
+
A basic Pretzel application consists of a class inheriting from the Pretzel::Core
|
42
|
+
class. It looks like this:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
require "pretzel/core"
|
46
|
+
|
47
|
+
class Application < Pretzel::Core
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
This gives you the following functionality:
|
52
|
+
|
53
|
+
* Routing with 6 methods (`GET, POST, PUT, DELETE, OPTIONS, HEAD`)
|
54
|
+
* Before and after filters
|
55
|
+
* Access to a `Rack::Response` and `Rack::Request` module
|
56
|
+
* Ability to include Rack middleware with the `use` function
|
57
|
+
* A fully setup session hash, which can be accessed with `session`
|
58
|
+
* Wrapper functions for setting the response status and redirecting
|
59
|
+
|
60
|
+
## Running your application
|
61
|
+
|
62
|
+
For running your application you have two options: Run it from the file directly
|
63
|
+
or run it with `rackup`. The Pretzel suite comes with Thin as web server, so you're
|
64
|
+
good to go.
|
65
|
+
|
66
|
+
### Running the application directly
|
67
|
+
|
68
|
+
To run your application directly, you need to call one of the Rack handlers like this:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
require "rack"
|
72
|
+
require "pretzel/core"
|
73
|
+
|
74
|
+
class Application < Pretzel::Core
|
75
|
+
end
|
76
|
+
|
77
|
+
Rack::Handler::Thin.run Application.new
|
78
|
+
```
|
79
|
+
|
80
|
+
Then you can just run the following command from the terminal: `$ ruby application.rb`
|
81
|
+
and your application should be up and running!
|
82
|
+
|
83
|
+
### Running the application via rackup
|
84
|
+
|
85
|
+
Running your application via `rackup` is a lot more flexible and is the recommended
|
86
|
+
way to get your things online. First setup your application in a file `application.rb`.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
require "pretzel/core"
|
90
|
+
|
91
|
+
class Application < Pretzel::Core
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
Then create a new file called `config.ru`, require your application and call the
|
96
|
+
`run` function. It looks like this:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
require_relative "application"
|
100
|
+
run Application.new
|
101
|
+
```
|
102
|
+
|
103
|
+
Finally execute this command in the directory of your `config.ru`: `$ rackup -o "0.0.0.0" -p PORT`
|
104
|
+
|
105
|
+
The port is freely configurable, but don't forget the `-o "0.0.0.0"` part as to listen
|
106
|
+
on all available interfaces.
|
@@ -0,0 +1,67 @@
|
|
1
|
+
## Routing
|
2
|
+
|
3
|
+
Routing with the Pretzel suite is very close to Sinatra, with one exception:
|
4
|
+
The recommendation is not to do your routing in the class declaration, but outside of it.
|
5
|
+
|
6
|
+
It looks like this:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
require "pretzel/core"
|
10
|
+
|
11
|
+
class Application < Pretzel::Core
|
12
|
+
end
|
13
|
+
|
14
|
+
Application.get "/" do
|
15
|
+
"Hello, World"
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
This has the advantage, that you can split your routes into seperate files to keep
|
20
|
+
your application maintainable.
|
21
|
+
|
22
|
+
All routes are matched in order from first-declared to last-declared
|
23
|
+
|
24
|
+
### Routing methods and blocks
|
25
|
+
|
26
|
+
There are 6 routing methods available to every Pretzel applications. They are as
|
27
|
+
follows: `get, post, put, delete, options, head`. Usually, if you're not deep down
|
28
|
+
into the HTTP spec, you will probably only need the first four.
|
29
|
+
|
30
|
+
Each routing method takes a route and a block as arguments. Routes can be declared
|
31
|
+
and visited with or without a trailing slash. The suite makes no differentiation
|
32
|
+
here for simplicity purposes.[1]
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
Application.get "/test" do
|
36
|
+
# Do stuff
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
### Block variables
|
41
|
+
|
42
|
+
Within blocks you have access to various variables that allow you
|
43
|
+
to easily modify and manipulate the data flowing around. The following objects
|
44
|
+
are available:
|
45
|
+
|
46
|
+
* request - A Rack::Request
|
47
|
+
* response - A Rack::Response
|
48
|
+
* parameters - A Hash containing the GET, POST and URL parameters of this route.
|
49
|
+
|
50
|
+
This follows the approach of not hiding anything from you and giving you full access
|
51
|
+
|
52
|
+
### Named parameters
|
53
|
+
|
54
|
+
Each route can have named parameters in the URL, by prefixing the part with a
|
55
|
+
colon like this: `/hello/:name`. These parameters will be added to your parameters
|
56
|
+
hash in the route block
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
# Assume we access /hello/world in our browser
|
60
|
+
Application.get "/hello/:name" do
|
61
|
+
"Hello, #{parameters['name']}!" # => "Hello, world!"
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
----
|
66
|
+
|
67
|
+
[1] This actually made the route parsing regex more complicated, but whatever.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
## Filters
|
2
|
+
|
3
|
+
Filters are functions that can handle things for you whenever a route is called
|
4
|
+
The Pretzel suite has two seperate filters: `before` and `after`.
|
5
|
+
|
6
|
+
They are declared very similar to routes:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
Application.before do
|
10
|
+
# Do this before every route
|
11
|
+
end
|
12
|
+
|
13
|
+
Application.after do
|
14
|
+
# Do this after every route
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
Those filters are executed before and after each call to a route. In the
|
19
|
+
function blocks you have access to two objects:
|
20
|
+
|
21
|
+
* response - A Rack::Response
|
22
|
+
* request - A Rack::Request
|
23
|
+
|
24
|
+
Thus these filters can be used to modify headers, request data and such as you
|
25
|
+
see fit. Quite handy!
|
@@ -0,0 +1,36 @@
|
|
1
|
+
## Mapping
|
2
|
+
|
3
|
+
Mapping is one of the most awesome features of Rack applications.
|
4
|
+
It allows you to put together a bunch of seperate applications into a single one,
|
5
|
+
thus allowing you to create service architectures.
|
6
|
+
|
7
|
+
Let's see how it works:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require "pretzel/core"
|
11
|
+
|
12
|
+
class Application < Pretzel::Core
|
13
|
+
end
|
14
|
+
|
15
|
+
class SubApplication < Pretzel::Core
|
16
|
+
end
|
17
|
+
|
18
|
+
SubApplication.get "/test" do
|
19
|
+
"Hello, SubApplication"
|
20
|
+
end
|
21
|
+
|
22
|
+
Application.get "/" do
|
23
|
+
"Hello, MainApplication"
|
24
|
+
end
|
25
|
+
|
26
|
+
Application.map "/sub" do
|
27
|
+
run SubApplication.new
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
Now in this setup whenever you access a route that's on the `/sub` route, you will
|
32
|
+
get forwarded to the routes of the `SupApplication`.
|
33
|
+
|
34
|
+
Thus when you access `/sub/test` you will get the response `Hello, SubApplication`
|
35
|
+
If you use this efficiently you can build an application on the so-called service
|
36
|
+
architecture, which is pretty neat!
|
@@ -0,0 +1,45 @@
|
|
1
|
+
## Convenience methods
|
2
|
+
|
3
|
+
The Pretzel suite comes with a set of three convenience methods. These are not part of
|
4
|
+
the Core framework, but are still included with every Pretzel project via the ways
|
5
|
+
of Pretzel::Core.
|
6
|
+
|
7
|
+
The convenience methods are defined in `Pretzel::Extension::Convenience` and the following
|
8
|
+
are available:
|
9
|
+
|
10
|
+
* redirect
|
11
|
+
* session
|
12
|
+
* status
|
13
|
+
|
14
|
+
**NOTE:** All convenience functions are only available in the *route blocks.*
|
15
|
+
|
16
|
+
### redirect(url)
|
17
|
+
|
18
|
+
The `redirect` function allows you to easily execute a HTTP redirect to another URL
|
19
|
+
of your application. It only takes a single argument and is called like this:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
redirect "/redirect-page"
|
23
|
+
```
|
24
|
+
|
25
|
+
### session
|
26
|
+
|
27
|
+
The `session` method is probably the most useful of the convenience functions. It
|
28
|
+
does what it says on the tin: Handle a session. It's already all setup with signed
|
29
|
+
cookies by the Pretzel::Core, so you can use it right away. It can be interacted
|
30
|
+
with as a regular Hash. It's just a thin wrapper around `request.session`.
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
session[:my_var] = "Hello, World" # Set a session variable
|
34
|
+
session[:my_var] # => "Hello, World"
|
35
|
+
```
|
36
|
+
|
37
|
+
### status(status)
|
38
|
+
|
39
|
+
`status` is a simple wrapper around response.status that allows you to set the HTTP
|
40
|
+
status of your response. This is usually done with APIs to indicate various success
|
41
|
+
and error states. `status` takes a single argument
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
status 201 # Set the status to "Resource created"
|
45
|
+
```
|
data/docs/02-01-json.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
## JSON
|
2
|
+
|
3
|
+
The Pretzel suite comes with a predelivered JSON extension (`Pretzel::Extension::JSON`)
|
4
|
+
that helps you easily send JSON responses for an API or something similar. It is a non-core
|
5
|
+
extension and you have to manually add it to your application.
|
6
|
+
|
7
|
+
You can include it like this:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require "pretzel/core"
|
11
|
+
require "pretzel/ext/json"
|
12
|
+
|
13
|
+
class Application < Pretzel::Core
|
14
|
+
extends Pretzel::Extension::JSON
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
And you're ready to go! The JSON module only provides a single method: `json`.
|
19
|
+
You can call this method with two parameters: A Hash and a `status`.
|
20
|
+
|
21
|
+
Only the hash is required. If you do not set a status, the response will just return
|
22
|
+
the status `200 OK`.
|
23
|
+
|
24
|
+
Calling it looks like this:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
Application.get "/json" do
|
28
|
+
json({
|
29
|
+
:hello => "world"
|
30
|
+
}, 201)
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
This sets the response status to 201, the content type to "application/json" and
|
35
|
+
converts the Hash to JSON according to the specification.
|
@@ -0,0 +1,123 @@
|
|
1
|
+
## Templating
|
2
|
+
|
3
|
+
Another pre-delivered feature of the Pretzel suite is templating. This is done
|
4
|
+
with the excellent [Liquid templating language.](https://github.com/shopify/liquid)
|
5
|
+
|
6
|
+
The purpose of this document is to show how to use the Pretzel suite with Liquid,
|
7
|
+
not to explain the templating language itself. For that, visit the link above and
|
8
|
+
look through the wiki to get a basic grasp.
|
9
|
+
|
10
|
+
Pretzel projects once again take a convention-over-configuration approach to templating.
|
11
|
+
All your templates must be stored in a `views` that resides next to your application file.
|
12
|
+
|
13
|
+
Inside the view folder you have free reign as to how to structure your templates with
|
14
|
+
one notable exceptions: *Liquid partials go in the `views/includes` folder*. As a fair
|
15
|
+
hint: Remember that all partials need to be named with an underscore in front. This
|
16
|
+
has caused me some headaches before.
|
17
|
+
|
18
|
+
Now for the folder structure:
|
19
|
+
|
20
|
+
```
|
21
|
+
app
|
22
|
+
|
|
23
|
+
|- application.rb
|
24
|
+
|- config.ru
|
25
|
+
|- views
|
26
|
+
|- includes
|
27
|
+
|- _partial.liquid
|
28
|
+
|- your_template.liquid
|
29
|
+
```
|
30
|
+
|
31
|
+
### Preparing your application
|
32
|
+
|
33
|
+
The code side of things is even simpler. The templating functionality is provided
|
34
|
+
in an extension: `Pretzel::Extension::Templating`. As with other extensions you have
|
35
|
+
to include it before using it, like so:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
require "pretzel/core"
|
39
|
+
require "pretzel/ext/templating"
|
40
|
+
|
41
|
+
class Application < Pretzel::Core
|
42
|
+
extends Pretzel::Extension::Templating
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
### Using basic templates
|
47
|
+
|
48
|
+
The extension exposes a single function: `render`, which is used like this:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
Application.get "/" do
|
52
|
+
render :index
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
The template name is passed as first argument. It can be either a symbol or a string.
|
57
|
+
It *must* not contain the file extension and is always rooted in the views folder.
|
58
|
+
|
59
|
+
Here are some more examples
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
render :index # => views/index.liquid
|
63
|
+
render "subfolder/my_template" # => views/subfolder/my_template.liquid
|
64
|
+
render :"subfolder/other_template" # => views/subfolder/other_template.liquid
|
65
|
+
```
|
66
|
+
|
67
|
+
### Templates and locals
|
68
|
+
|
69
|
+
Most templates aren't satisfied with simple rendering, and usually you want to pass
|
70
|
+
a bunch of variables (so called `locals`) in. The render function has that covered:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
Application.get "/" do
|
74
|
+
render :index, {
|
75
|
+
"name" => "Richard"
|
76
|
+
}
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
The locals are always passed in as a simple Ruby Hash. For more information on locals,
|
81
|
+
see the Liquid documentation!
|
82
|
+
|
83
|
+
### Layouts
|
84
|
+
|
85
|
+
Many applications have some sort of HTML structure that they want to persist across
|
86
|
+
various views. This is usually called a layout or a theme. The Pretzel suite has
|
87
|
+
this integrated too.
|
88
|
+
|
89
|
+
Whenever you call `render` on a template, the framework will look for a file in your
|
90
|
+
`views` folder called `layout.liquid`. This template exposes a special variable
|
91
|
+
called `template`. An example layout may look like this:
|
92
|
+
|
93
|
+
```html
|
94
|
+
<!DOCTYPE html>
|
95
|
+
<html lang="en">
|
96
|
+
<head>
|
97
|
+
<meta charset="UTF-8">
|
98
|
+
<title>Document</title>
|
99
|
+
</head>
|
100
|
+
<body>
|
101
|
+
{{ template }}
|
102
|
+
</body>
|
103
|
+
</html>
|
104
|
+
```
|
105
|
+
|
106
|
+
The part where you output template is, shockingly, the place where your template will
|
107
|
+
appear. Cool, huh?
|
108
|
+
|
109
|
+
#### Deactivating the layout
|
110
|
+
|
111
|
+
If there is no `layout.liquid` file, the Pretzel suite will just not apply a layout.
|
112
|
+
In case you have created a layout but you want to deactivate it for a certain route,
|
113
|
+
you just need to pass `"layout" => false` into the locals like so:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
Application.get "/no-layout" do
|
117
|
+
render :some_template, {
|
118
|
+
"layout" => false
|
119
|
+
}
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
This will supress the layout rendering for this template.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
## Rack middleware
|
2
|
+
|
3
|
+
Since Pretzel applications are just Rack applications under the hood you
|
4
|
+
can extend them with any type of Rack middleware. It's really easy, too.
|
5
|
+
|
6
|
+
Here's how it is done:
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
require "pretzel/core"
|
10
|
+
require "rack/lobster" # The middleware
|
11
|
+
|
12
|
+
class Application < Pretzel::Core
|
13
|
+
use Rack::Lobster
|
14
|
+
end
|
15
|
+
```
|
16
|
+
|
17
|
+
And done.
|
@@ -0,0 +1,117 @@
|
|
1
|
+
## Pretzel extensions
|
2
|
+
|
3
|
+
While Rack middleware is great for low-level extensions, sometimes you
|
4
|
+
may want to extend the Pretzel suite on a somewhat higher level.
|
5
|
+
|
6
|
+
Maybe your extension requires some custom routes or you want to include
|
7
|
+
some Liquid extensions? This is the reason for the `extends` method.
|
8
|
+
|
9
|
+
It is used like this:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require "pretzel/core"
|
13
|
+
|
14
|
+
class Application < Pretzel::Core
|
15
|
+
extends Pretzel::Extension::Test
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
And you're good to go.
|
20
|
+
|
21
|
+
## Structure
|
22
|
+
|
23
|
+
Each extension consists of two parts: `App` and `Route`.
|
24
|
+
Both are submodules of your extension. The structure looks like this.
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
module Pretzel::Extension
|
28
|
+
|
29
|
+
module Test
|
30
|
+
module App
|
31
|
+
# Code-y awesomeness
|
32
|
+
end
|
33
|
+
|
34
|
+
module Route
|
35
|
+
# Code-y awesomeness
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
## Where to declare things
|
43
|
+
|
44
|
+
The `App` and `Route` modules are rather aptly named. Any methods declared in
|
45
|
+
the `App` module will be available in the class declaration like this:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
class Application < Pretzel::Core
|
49
|
+
extends Your::Extension
|
50
|
+
your_method
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
On the other side, any methods declared in the `Route` module are available in
|
55
|
+
the route controllers like this:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class Application < Pretzel::Core
|
59
|
+
extends Your::Extension
|
60
|
+
end
|
61
|
+
|
62
|
+
Application.get "/" do
|
63
|
+
your_extension
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
### Custom extension routes
|
68
|
+
|
69
|
+
The `Route` module is also where you want to declare your custom routes.
|
70
|
+
This can be done by using Ruby's `self.included` method to your advantage:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
module Pretzel::Extension
|
74
|
+
|
75
|
+
module Test
|
76
|
+
module Route
|
77
|
+
|
78
|
+
def self.included(application)
|
79
|
+
application.get "/ext/index" do
|
80
|
+
"Extensions!"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
### Extending Liquid
|
91
|
+
|
92
|
+
If you want to keep your application clean and modular, you can also use the
|
93
|
+
`Route` module to extend Liquid. You can read all about [the subject matter here.](https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#extending-liquid)
|
94
|
+
|
95
|
+
An example Liquid extension may look like this:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
|
99
|
+
module Pretzel::Extension
|
100
|
+
|
101
|
+
module Test
|
102
|
+
module Route
|
103
|
+
module TestFilter
|
104
|
+
def reverse(string)
|
105
|
+
string.reverse!
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
Liquid::Template.register_filter(TestFilter)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
It can then be included in your application with `extends` and you're ready
|
117
|
+
to use the filter in your templates. (Of course you can also create blocks and tags.)
|
data/lib/pretzel/core.rb
CHANGED
@@ -3,17 +3,21 @@ require "forwardable"
|
|
3
3
|
require "securerandom"
|
4
4
|
|
5
5
|
require_relative "version"
|
6
|
+
require_relative "extends"
|
7
|
+
|
6
8
|
require_relative "ext/convenience"
|
7
9
|
|
8
10
|
require_relative "routing/eval"
|
9
11
|
require_relative "routing/methods"
|
10
12
|
|
13
|
+
|
11
14
|
module Pretzel
|
12
15
|
class Core
|
16
|
+
extend Pretzel::Extension
|
13
17
|
include Pretzel::Routing::Evaluation
|
14
18
|
extend Pretzel::Routing::Methods
|
15
19
|
|
16
|
-
|
20
|
+
extends Pretzel::Extension::Convenience
|
17
21
|
|
18
22
|
attr_reader :request, :response, :parameters
|
19
23
|
|
@@ -2,16 +2,18 @@ module Pretzel
|
|
2
2
|
module Extension
|
3
3
|
module Convenience
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
module Route
|
6
|
+
def status(status)
|
7
|
+
response.status = status
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
def session
|
11
|
+
request.session || raise("Rack::Session not set.")
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
def redirect(url)
|
15
|
+
response.redirect url
|
16
|
+
end
|
15
17
|
end
|
16
18
|
|
17
19
|
end
|
data/lib/pretzel/ext/json.rb
CHANGED
@@ -4,11 +4,13 @@ module Pretzel
|
|
4
4
|
module Extension
|
5
5
|
module JSON
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
module Route
|
8
|
+
def json(hash, status=200)
|
9
|
+
response["Content-Type"] = "application/json"
|
10
|
+
response.status = status
|
10
11
|
|
11
|
-
|
12
|
+
hash.to_json
|
13
|
+
end
|
12
14
|
end
|
13
15
|
|
14
16
|
end
|
@@ -3,29 +3,33 @@ require "liquid"
|
|
3
3
|
module Pretzel
|
4
4
|
module Extension
|
5
5
|
module Templating
|
6
|
-
def self.included(_class)
|
7
|
-
Liquid::Template.file_system = Liquid::LocalFileSystem.new("#{Dir.pwd}/views/includes")
|
8
|
-
end
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
module Route
|
8
|
+
def self.included(_class)
|
9
|
+
Liquid::Template.file_system = Liquid::LocalFileSystem.new("#{Dir.pwd}/views/includes")
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(path, locals = {})
|
13
|
+
response["Content-Type"] = "text/html"
|
14
|
+
layout_file = File.expand_path("#{Dir.pwd}/views/layout.liquid")
|
15
|
+
raw_file = File.expand_path("#{Dir.pwd}/views/#{path.to_s}.liquid")
|
16
|
+
|
17
|
+
template = Liquid::Template.parse(File.read(raw_file))
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
if File.exist?(layout_file)
|
20
|
+
# Always render the layout, unless explicitly told otherwise.
|
21
|
+
unless locals["layout"] == false
|
22
|
+
layout = Liquid::Template.parse(File.read(layout_file))
|
23
|
+
layout.render({ "template" => template.render(locals) })
|
24
|
+
else
|
25
|
+
template.render(locals)
|
26
|
+
end
|
22
27
|
else
|
23
28
|
template.render(locals)
|
24
29
|
end
|
25
|
-
else
|
26
|
-
template.render(locals)
|
27
30
|
end
|
28
31
|
end
|
32
|
+
|
29
33
|
end # end module
|
30
34
|
end # end module
|
31
35
|
end # end module
|
data/lib/pretzel/version.rb
CHANGED
data/test/scrap.rb
CHANGED
@@ -2,14 +2,16 @@
|
|
2
2
|
# implemented. The code here is not intended to be sensible.
|
3
3
|
|
4
4
|
require_relative "setup_environment"
|
5
|
+
require_relative "scrap_extension"
|
5
6
|
|
6
7
|
class ScrapApplication < Pretzel::Core
|
7
|
-
|
8
|
-
|
8
|
+
extends Pretzel::Extension::Templating
|
9
|
+
extends Pretzel::Extension::Scrap
|
9
10
|
end
|
10
11
|
|
11
12
|
ScrapApplication.get "/" do
|
12
|
-
|
13
|
+
some_class_stuff "classy test"
|
14
|
+
some_instance_stuff "Test!"
|
13
15
|
end
|
14
16
|
|
15
17
|
ScrapApplication.get "/yolo/" do
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Pretzel
|
4
|
+
module Extension
|
5
|
+
|
6
|
+
class Scrap
|
7
|
+
module Instance
|
8
|
+
def some_instance_stuff(text)
|
9
|
+
p text
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module App
|
14
|
+
def some_class_stuff(text)
|
15
|
+
p "CLASS: #{text}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Routes
|
20
|
+
def self.included(app)
|
21
|
+
app.get "/ext/test" do
|
22
|
+
render :index
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Templates
|
28
|
+
module TestFilter
|
29
|
+
def tester(input)
|
30
|
+
input.reverse!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Liquid::Template.register_filter(TestFilter)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative "setup_environment"
|
2
|
+
|
3
|
+
module Pretzel
|
4
|
+
module Extension
|
5
|
+
class Test
|
6
|
+
module Instance
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
module App
|
11
|
+
def class_method
|
12
|
+
"Class method"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Route
|
17
|
+
def instance_method
|
18
|
+
"Instance method"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.included(app)
|
22
|
+
app.get "/ext/index" do
|
23
|
+
render :index
|
24
|
+
end
|
25
|
+
|
26
|
+
app.get "/ext/instance" do
|
27
|
+
instance_method
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ExtensionApplication < Pretzel::Core
|
37
|
+
extends Pretzel::Extension::Templating
|
38
|
+
extends Pretzel::Extension::Test
|
39
|
+
end
|
40
|
+
|
41
|
+
class TestExtension < Minitest::Test
|
42
|
+
include Rack::Test::Methods
|
43
|
+
|
44
|
+
def app
|
45
|
+
ExtensionApplication.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_extension_route
|
49
|
+
get "/ext/index"
|
50
|
+
assert last_response.body.include? "Hello, World!"
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_extension_classmethod
|
54
|
+
assert_equal ExtensionApplication.class_method, "Class method"
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_extension_instancemethod
|
58
|
+
get "/ext/instance"
|
59
|
+
assert_equal last_response.body, "Instance method"
|
60
|
+
end
|
61
|
+
end
|
data/test/test_json.rb
CHANGED
data/test/test_templating.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pretzel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Blechinger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -94,18 +94,30 @@ files:
|
|
94
94
|
- LICENSE
|
95
95
|
- README.md
|
96
96
|
- Rakefile
|
97
|
+
- docs/01-00-application.md
|
98
|
+
- docs/01-01-routing.md
|
99
|
+
- docs/01-02-filters.md
|
100
|
+
- docs/01-04-mapping.md
|
101
|
+
- docs/02-00-conveniece-methods.md
|
102
|
+
- docs/02-01-json.md
|
103
|
+
- docs/03-00-templating.md
|
104
|
+
- docs/04-01-middleware.md
|
105
|
+
- docs/04-02-extensions.md
|
97
106
|
- lib/pretzel/core.rb
|
98
107
|
- lib/pretzel/ext/convenience.rb
|
99
108
|
- lib/pretzel/ext/json.rb
|
100
109
|
- lib/pretzel/ext/templating.rb
|
110
|
+
- lib/pretzel/extends.rb
|
101
111
|
- lib/pretzel/routing/eval.rb
|
102
112
|
- lib/pretzel/routing/methods.rb
|
103
113
|
- lib/pretzel/version.rb
|
104
114
|
- pretzel.gemspec
|
105
115
|
- test/assets/test.css
|
106
116
|
- test/scrap.rb
|
117
|
+
- test/scrap_extension.rb
|
107
118
|
- test/setup_environment.rb
|
108
119
|
- test/test_convenience.rb
|
120
|
+
- test/test_extension.rb
|
109
121
|
- test/test_filters.rb
|
110
122
|
- test/test_json.rb
|
111
123
|
- test/test_map.rb
|
@@ -145,8 +157,10 @@ summary: The Pretzel web application core
|
|
145
157
|
test_files:
|
146
158
|
- test/assets/test.css
|
147
159
|
- test/scrap.rb
|
160
|
+
- test/scrap_extension.rb
|
148
161
|
- test/setup_environment.rb
|
149
162
|
- test/test_convenience.rb
|
163
|
+
- test/test_extension.rb
|
150
164
|
- test/test_filters.rb
|
151
165
|
- test/test_json.rb
|
152
166
|
- test/test_map.rb
|