atd 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -3
- data/.gitlab-ci.yml +53 -2
- data/.rubocop.yml +1 -4
- data/CHANGELOG +6 -0
- data/Guardfile +28 -0
- data/README.md +111 -203
- data/atd.gemspec +5 -0
- data/lib/atd.rb +77 -82
- data/lib/atd/builtin_class_modifications.rb +35 -28
- data/lib/atd/internal_helpers.rb +9 -0
- data/lib/atd/routes.rb +57 -45
- data/lib/atd/version.rb +1 -1
- data/lib/extensions/precompilers.rb +1 -1
- metadata +49 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba460feaa80c9fb85ebab903d9e62e0300e579cd
|
4
|
+
data.tar.gz: 2c917612c41a5642f8115a1fd76e01e4a4471c11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 762ce1b5272cac9f2f643fc98a69f97b084f19aac2c971a247a09e8dd704d7359ea383a740aa89f7b4d0ee7ad17183cb8c77e4ebffa2cb605cf2063d2e464be1
|
7
|
+
data.tar.gz: d223ee6e845e202f03f04f04a36518ddc184a79fe8fa7b8d92ab476b62ef95c6b81123664e8d80401cee5b87c4675e38b9f6c9429044e70a988359b1b9fbe087
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
@@ -1,12 +1,63 @@
|
|
1
|
-
image: ruby:2.
|
1
|
+
image: ruby:2.4.0
|
2
2
|
|
3
3
|
before_script:
|
4
4
|
- ruby -v
|
5
5
|
- which ruby
|
6
|
+
- gem environment
|
7
|
+
- gem install rubygems-update
|
8
|
+
- gem install rubygems-update --version 2.6.3
|
9
|
+
- gem update --system 2.6.10
|
6
10
|
- gem install bundler --no-ri --no-rdoc
|
11
|
+
- gem environment
|
7
12
|
- bundle install --jobs $(nproc) "${FLAGS[@]}"
|
8
13
|
|
9
|
-
test:
|
14
|
+
test-2.4.0:
|
15
|
+
image: ruby:2.4.0
|
16
|
+
script:
|
17
|
+
- bundle exec rake test
|
18
|
+
|
19
|
+
test-2.3.3:
|
20
|
+
image: ruby:2.3.3
|
21
|
+
script:
|
22
|
+
- bundle exec rake test
|
23
|
+
|
24
|
+
test-2.3.2:
|
25
|
+
image: ruby:2.3.2
|
26
|
+
script:
|
27
|
+
- bundle exec rake test
|
28
|
+
|
29
|
+
test-2.3.1:
|
30
|
+
image: ruby:2.3.1
|
31
|
+
script:
|
32
|
+
- bundle exec rake test
|
33
|
+
|
34
|
+
test-2.3.0:
|
35
|
+
image: ruby:2.3.0
|
36
|
+
script:
|
37
|
+
- bundle exec rake test
|
38
|
+
|
39
|
+
test-2.2.6:
|
40
|
+
image: ruby:2.2.6
|
41
|
+
script:
|
42
|
+
- bundle exec rake test
|
43
|
+
|
44
|
+
test-2.2.5:
|
45
|
+
image: ruby:2.2.5
|
46
|
+
script:
|
47
|
+
- bundle exec rake test
|
48
|
+
|
49
|
+
test-2.2.4:
|
50
|
+
image: ruby:2.2.4
|
51
|
+
script:
|
52
|
+
- bundle exec rake test
|
53
|
+
|
54
|
+
test-2.2.3:
|
55
|
+
image: ruby:2.2.3
|
56
|
+
script:
|
57
|
+
- bundle exec rake test
|
58
|
+
|
59
|
+
test-2.2.2:
|
60
|
+
image: ruby:2.2.2
|
10
61
|
script:
|
11
62
|
- bundle exec rake test
|
12
63
|
|
data/.rubocop.yml
CHANGED
@@ -14,14 +14,11 @@ Style/ParallelAssignment:
|
|
14
14
|
Style/Documentation:
|
15
15
|
Enabled: false
|
16
16
|
|
17
|
-
Style/ModuleFunction:
|
18
|
-
Enabled: false
|
19
|
-
|
20
17
|
Style/ClassAndModuleChildren:
|
21
18
|
Enabled: false
|
22
19
|
|
23
20
|
Metrics:
|
24
21
|
Enabled: false
|
25
22
|
|
26
|
-
|
23
|
+
Style/FrozenStringLiteralComment:
|
27
24
|
Enabled: false
|
data/CHANGELOG
CHANGED
@@ -1,4 +1,10 @@
|
|
1
1
|
Current Repo Version:
|
2
|
+
- Cleaned up code for some built-in class modifications, replacing them with more limited in scope refinements. (#30)
|
3
|
+
- Allowed greater flexibility in compilation and precomilation by passing those methods the settings hash passed during route creation. (#27)
|
4
|
+
- Upgraded to comply with 2.4.0 by testing for Integer instead of Fixnum. To preserve backwards compatibility, we made different behaviors for older ruby versions. (#24)
|
5
|
+
- Completely rewrote README to be much easier to read and more informative. (#23)
|
6
|
+
- Added sinatra-esque helper methods instead of a ugly @http instance variable. (#18)
|
7
|
+
- Fixed some issues with request in main (DefaultApp). (#31, #32)
|
2
8
|
|
3
9
|
v0.4.0
|
4
10
|
- Fixed an issue where routes without blocks wouldn't return status codes (#9)
|
data/Guardfile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
guard :minitest do
|
19
|
+
# with Minitest::Unit
|
20
|
+
watch(%r{^test/(.*)\/?test_(.*)\.rb$})
|
21
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
22
|
+
watch(%r{^test/test_helper\.rb$}) { 'test' }
|
23
|
+
|
24
|
+
# with Minitest::Spec
|
25
|
+
# watch(%r{^spec/(.*)_spec\.rb$})
|
26
|
+
# watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
27
|
+
# watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
|
28
|
+
end
|
data/README.md
CHANGED
@@ -13,160 +13,123 @@
|
|
13
13
|
[gem version link]: https://rubygems.org/gems/atd
|
14
14
|
|
15
15
|
|
16
|
-
[ATD](#atd)
|
17
|
-
- [Installation](#installation)
|
18
|
-
- [Usage](#usage)
|
19
|
-
- [Setup](#setup)
|
20
|
-
- [Routing](#routing)
|
21
|
-
- [Basic Routing](#basic-routing)
|
22
|
-
- [Options Hash](#options-hash)
|
23
|
-
- [Serving Files](#serving-files)
|
24
|
-
- [Advanced Routing](#advanced-routing)
|
25
|
-
- [DefaultApp](#defaultapp)
|
26
|
-
- [Inheritance](#inheritance)
|
27
|
-
- [Blocks](#blocks)
|
28
|
-
- [@http](#http)
|
29
|
-
- [Controllers](#controllers)
|
30
|
-
- [Apps](#apps)
|
31
|
-
- [App Creation](#app-creation)
|
32
|
-
- [Starting the App](#starting-the-app)
|
33
|
-
- [App Routing](#app-routing)
|
34
|
-
- [Logging](#logging)
|
35
|
-
- [Compilation](#compilation)
|
36
|
-
- [Design Paradigms](#design-paradigms)
|
37
|
-
- [Documentation](#documentation)
|
38
|
-
- [Development](#development)
|
39
|
-
- [Contributing](#contributing)
|
40
|
-
|
41
|
-
|
42
16
|
# ATD
|
43
17
|
|
44
|
-
ATD is a
|
45
|
-
|
46
|
-
## Installation
|
18
|
+
Hello! I assume if you're reading this you really want to know about this really cool, interesting new framework that I made. Well, in that case you've come to the right place. ATD is a small modular framework meant to combine the benefits from [rails](http://rubyonrails.org/) and [sinatra](http://www.sinatrarb.com/). I originally used sinatra, and ran into issues with scaling, and so hence I did the only logical thing and wrote a new framework. I just found that because of sinatra's "build it from the ground up" philosophy, I couldn't make anything too large without making a mass of spaghetti code. That is why this framework allows you to use rails concepts like MVC and moduarly use different components in development and production. It supports the simple sinatra DSL syntax (for the most part) to make small apps, and allows big controllers and models for separation of concerns in larger apps, which is more of a rails philosophy.
|
47
19
|
|
48
|
-
|
20
|
+
Anyways, I've worked really hard on this, and I would love for you to try it out and maybe even contribute! Feel free to reach out to me at [atd@developingtechnician.com](mailto:atd@developingtechnician.com) with questions or concerns, or if it's something concrete you want me to change, just open an issue.
|
49
21
|
|
50
|
-
|
51
|
-
gem 'atd', :git => 'https://gitlab.com/izwick-schachter/atd.git'
|
52
|
-
```
|
22
|
+
## Installation
|
53
23
|
|
54
|
-
|
24
|
+
Pretty simple, the usual `gem install atd` if you want to use without bundler, or just add it to your Gemfile with bundler. The recommended setup is to use the master branch of the git repo by adding to your Gemfile:
|
55
25
|
|
56
26
|
```ruby
|
57
|
-
gem 'atd'
|
27
|
+
gem 'atd', git: 'https://gitlab.com/izwick-schachter/atd.git', branch: "master"
|
58
28
|
```
|
59
29
|
|
60
|
-
|
61
|
-
|
62
|
-
$ bundle
|
63
|
-
|
64
|
-
Or install it from the repository with:
|
30
|
+
Or if you want to live on the edge, where all the latest and greatest features are, change `branch` to `"development"`. Keep in mind, this may sometimes not work, so use at your own risk. When you do this, it's recommended that you go into the git repo and choose a specific commit and lock that in by setting `:ref` to the commit hash.
|
65
31
|
|
66
|
-
|
67
|
-
$ cd atd
|
68
|
-
$ bundle exec rake install
|
32
|
+
## The Basics
|
69
33
|
|
70
|
-
|
34
|
+
`ATD` is structured around a two basic constructs: `App`s and `Route`s.
|
71
35
|
|
72
|
-
|
73
|
-
|
74
|
-
Setup is as easy as `require "atd"` at the top of whatever file you need it in
|
36
|
+
### Apps
|
75
37
|
|
76
|
-
|
38
|
+
An `App` is a class which is a functional rack app. It has an instance method `call` as the rack spec requires, and it contains all the things you need to use `ATD`. An `App` is just a class that extends `ATD::App`. That said, for most usages you will never need to know anything about `App`s. Unless you need multiple different `App`s in the same file, you don't need to touch it. That is because `ATD` kindly treats `main` as an `App` called `DefaultApp`. All that means is that anything created in `main` is added to `DefaultApp`. So, whenever you do something that is not in an `App` you have created, you can just know in the back of your head that you are impacting `DefaultApp`.
|
77
39
|
|
78
|
-
###
|
40
|
+
### Routes
|
79
41
|
|
80
|
-
|
42
|
+
The second basic construct is a `Route`. These are things which you should understand, because these are how `ATD` processes most of the things you do. Every route belongs to an `App`, typically `DefaultApp` as mentioned [above](#apps). In it's most simple form, a `Route` simply says "When `/whatever` is requested, return this file and/or run this code". The syntax will certainly remind you of sinatra:
|
81
43
|
|
82
44
|
```ruby
|
83
|
-
request "/", "
|
45
|
+
request "/some/path", "my_file.html.erb"
|
46
|
+
# Also aliased to:
|
47
|
+
req "/some/path", "my_file.html.erb"
|
48
|
+
# And
|
49
|
+
r "/some/path", "my_file.html.erb"
|
50
|
+
# It's easier to type
|
84
51
|
```
|
85
52
|
|
86
|
-
|
53
|
+
Why not make the method named for the HTTP verbs? Because we want everything to be customizable. By default, calling `request` will make the route respond to every HTTP verb. If you want it to only respond to some verbs, you have several choices for how you want to do it:
|
87
54
|
|
88
55
|
```ruby
|
89
|
-
|
90
|
-
|
91
|
-
|
56
|
+
# If you only wanted to respond to GET, POST, and DELETE:
|
57
|
+
request "/some/path", "my_file.html.erb", respond_to: [:get, :post, :delete]
|
58
|
+
# Or this other syntax
|
59
|
+
get post delete "/some/path", "my_file.html.erb"
|
60
|
+
# Or a combination of the two
|
61
|
+
get "/some/path", "my_file.html.erb", respond_to: [:post, :delete]
|
62
|
+
# Or with dots too
|
63
|
+
get.post.delete "/some/path", "my_file.html.erb"
|
64
|
+
# Maybe mixed with request:
|
65
|
+
request.get.post.delete "/some/path", "my_file.html.erb"
|
66
|
+
# Or if you wanted to ignore those verbs and only respond to PUT and PATCH
|
67
|
+
request "/some/path", "my_file.html.erb", ignore: [:get, :post, :delete]
|
92
68
|
```
|
93
69
|
|
94
|
-
|
70
|
+
_N.B. See [precompilers](#precompilers) for how the file name you pass is manipulated before it is sent out._
|
95
71
|
|
96
|
-
You
|
72
|
+
You can also pass a block that will be run whenever the `Route` is matched:
|
97
73
|
|
98
74
|
```ruby
|
99
|
-
request
|
100
|
-
|
101
|
-
|
102
|
-
Or you could use a simpler (but equivalent) syntax:
|
103
|
-
|
104
|
-
```ruby
|
105
|
-
get "/", "Hello World"
|
75
|
+
request "/some/path", "my_file.html.erb" do
|
76
|
+
puts "Found me!"
|
77
|
+
end
|
106
78
|
```
|
107
79
|
|
108
|
-
|
80
|
+
and if you want to, you can make the blocks return value be the output of the route by omitting `"my_file.html.erb"`. You can also manipulate the output that you passed (see [helpers](#helpers)) by modifying the `view[:raw]` variable.
|
109
81
|
|
110
|
-
|
82
|
+
## Settings
|
111
83
|
|
112
|
-
|
113
|
-
request.get.post.put.patch "/", "Hello World"
|
114
|
-
```
|
84
|
+
This doesn't exist yet, but is in progress in issue #29.
|
115
85
|
|
116
|
-
|
86
|
+
## Advanced Routing
|
117
87
|
|
118
|
-
|
88
|
+
For simple project, [the basics](#the-basics) of routing will work fine. But if you want to create a full fledged application, then you probably are going to need some of the more advanced features, such as precompilation, compilation, compiler options, and using blocks to manipulate output.
|
119
89
|
|
120
|
-
|
121
|
-
request "/", "Hello World", respond_to: [:get, :post, :put, :patch]
|
122
|
-
```
|
90
|
+
### Options Hash
|
123
91
|
|
124
|
-
|
125
|
-
|
126
|
-
```ruby
|
127
|
-
request "/", "Hello World", ignore: :delete
|
128
|
-
```
|
129
|
-
|
130
|
-
#### Options Hash
|
131
|
-
|
132
|
-
You can pass various options through the options hash. The options hash is put as the last arguemtn when creating a route. For example: `request "/", "Hello World", option: value, other-option: value`
|
133
|
-
|
134
|
-
Here is a list of the option which are currently valid and default values:
|
92
|
+
The options hash is the hash provided at the end of the argument list when creating a route. For example, in `r "/", "some_file", option_A: "Value", option_B: 35` the options hash will be `{option_A: "Value", option_B: 35}`. Here is a list of options that ATD looks at internally and their default values (other keys can be passed in the options hash, they will just be passed to compilation methods):
|
135
93
|
|
136
94
|
```ruby
|
137
95
|
status: 200 # Integer > 99 and < 1000. This will be the status code returned unless it is overridden by
|
138
96
|
status_code: 200 # Same as status, but overrides. A slightly more verbose syntax.
|
139
|
-
respond_to: nil # A
|
140
|
-
ignore: nil # A
|
97
|
+
respond_to: nil # A array of HTTP methods as lowercase symbols which the route should respond to.
|
98
|
+
ignore: nil # A array of HTTP methods as lowercase symbols which the route should not respond to. This takes highest precedence.
|
99
|
+
precompile: true # This determines if a route will be precompiled. Unless it is == false it will be precompiled.
|
100
|
+
compile: true # This determines if a route will be compiled. Unless it is == false it will be compiled.
|
141
101
|
```
|
142
102
|
|
143
|
-
|
103
|
+
### Serving Files
|
144
104
|
|
145
|
-
|
146
|
-
|
147
|
-
```ruby
|
148
|
-
request "/", "index.html"
|
149
|
-
```
|
105
|
+
If you want to serve files, just place them in an assets directory in the app directory and pass the file name with the file extension as the second argument to your routes. In the future you will be able to access this through the [`App` settings](#settings).
|
150
106
|
|
151
|
-
|
107
|
+
### Helpers
|
152
108
|
|
153
|
-
|
109
|
+
In the block passed to a `Route` there are helpers available to it. You can add new helper methods by adding them to the `ATD::Helpers` module. By default there are a few defined, and here they are:
|
154
110
|
|
155
|
-
|
156
|
-
|
157
|
-
|
111
|
+
```ruby
|
112
|
+
http[:request] # The Rack::Request object
|
113
|
+
http[:response] # The Rack::Response object
|
114
|
+
http[:view] # The same thing as view[:raw]
|
115
|
+
http[:method] # The HTTP verb used to access the route
|
116
|
+
http[:headers] # The headers which will be sent, by default only "content-type".
|
117
|
+
http[:status_code] # The status code which the app will respond with, by default 200
|
118
|
+
params # The params hash we all know and love.
|
119
|
+
view[:raw] # The precompiled (unless precompile:false) and compiled (unless compile:false) view.
|
120
|
+
```
|
158
121
|
|
159
|
-
|
122
|
+
## Apps
|
160
123
|
|
161
|
-
Whenever you create a route, it is given to an
|
124
|
+
Whenever you create a route, it is given to an `App`. This isn't apparent when you create a route in `main`, but even when you do that the route is added to `DefaultApp`, as you may remember from the [intro](#the-basics). If you are using Apps, then when you create a route in an `App`, that route belongs to the `App`. When you start the server, it then creates an instance of the `App` and starts it because it is a rack app. But that is not what the purpose of `App`s are.
|
162
125
|
|
163
|
-
The intention of Apps are to allow you to use one App
|
126
|
+
The intention of `Apps` are to allow you to use one `App` as a template, from which you can create many different `App`s. An `App` is not a rack app all by itself. Every instance of an App Class is a rack app. But the rack app doesn't actually start until `start` is called. This means that you can create an instance of an `App`, and then you can modify it before starting it. So for example, you can have an `App` which is impacted by an instance variable:
|
164
127
|
|
165
128
|
```ruby
|
166
129
|
class MyApp < ATD::App
|
167
130
|
attr_accessor :my_name
|
168
131
|
request "/", "Hi! This is my_name's App!" do
|
169
|
-
|
132
|
+
view[:raw] = view[:raw].gsub("my_name", @my_name)
|
170
133
|
end
|
171
134
|
end
|
172
135
|
```
|
@@ -181,51 +144,47 @@ app.start
|
|
181
144
|
|
182
145
|
Then when you try to access the website, it will respond to `/` with `Hi! This is Fredrick's App!`.
|
183
146
|
|
184
|
-
|
147
|
+
### App Creation
|
185
148
|
|
186
|
-
|
149
|
+
To create an app you can use `ATD.new("AppName")`. It is important to note that **ATD.new is not a constructor, although I will refer to it as one**. It simply behaves like one because you can use it to "construct" a new app. The app creation process creates a new class which you can open anywhere in your app file, with the name you pass. The name must `respond_to?(:to_sym)`, and must be a valid class name. You must call the constructor before you begin adding routes to the class, or open the class at all.
|
150
|
+
|
151
|
+
You can also use the more intuitive way to create an app, which would be by declaring a class which extends `ATD::App`, like so:
|
187
152
|
|
188
153
|
```ruby
|
189
|
-
|
190
|
-
|
154
|
+
class MyAppClass < ATD::App
|
155
|
+
# All of my routes, settings, etc.
|
191
156
|
end
|
192
157
|
```
|
193
158
|
|
194
|
-
|
159
|
+
### Starting the App
|
160
|
+
|
161
|
+
There are two basic ways to start the app. You can start it by calling `AppName.new.start` or more simply `AppName.start` which will create an instance and call start on it, or you can use the more common syntax. For `DefaultApp` that would be:
|
195
162
|
|
196
163
|
```ruby
|
197
|
-
request "/", "
|
198
|
-
|
199
|
-
end
|
164
|
+
request "/", "Hello World!"
|
165
|
+
start
|
200
166
|
```
|
201
167
|
|
202
|
-
|
203
|
-
|
204
|
-
You could also run some complex method created outside the route. This route is called in the same scope as any block you were to declare. Here is an example:
|
168
|
+
And for `MyApp` that would be:
|
205
169
|
|
206
170
|
```ruby
|
207
|
-
|
208
|
-
|
171
|
+
class MyApp < ATD::App
|
172
|
+
request "/", "Hello World!"
|
173
|
+
start
|
209
174
|
end
|
210
175
|
```
|
211
176
|
|
212
|
-
|
177
|
+
## Controllers
|
213
178
|
|
214
|
-
|
179
|
+
> **Caution:**
|
180
|
+
>
|
181
|
+
> This entire section is experimental right now and as partial or no support. Don't trust this section and tread carefully.
|
215
182
|
|
216
|
-
|
217
|
-
@http[:status_code] = 200 # By default the status code is 200. You can manipulate this.
|
218
|
-
@http[:headers] = {} # By defualt there are no headers. You can add whatever you want.
|
219
|
-
@http[:method] = env["REQUEST_METHOD"] # Writing to this does nothing. Just a way for you to see the request method.
|
220
|
-
@http[:output] # This is set to the output you give in the args for request
|
221
|
-
@http[:request] # The associated Rack::Request for the request.
|
222
|
-
```
|
223
|
-
|
224
|
-
While you can use `@http[:status_code]` to change the status code, you can also set a status code with `r "/", status_code: 200`. That status code must be >= 100, as per the Rack Specification.
|
183
|
+
Because we understand how important it is to have flexibility in how you work, we provide support of a number of different configurations, and one of the ways we do that is with controllers. A controller is simply a module full of methods which can be referenced from a `Route` by passing `controller_name#action` instead of the file name or by putting it in the options hash with `to: controller_name#action` or `to: :action, controller: "controller_name"`.
|
225
184
|
|
226
|
-
|
185
|
+
If you want to add all the methods from one controller to an `App` you can do that by calling the `controller` method in the `App` with the name of the controller as a parameter.
|
227
186
|
|
228
|
-
|
187
|
+
Here is an example of an `App` using controllers:
|
229
188
|
|
230
189
|
```ruby
|
231
190
|
module MyController
|
@@ -237,96 +196,41 @@ end
|
|
237
196
|
request "/", "MyController#test_route" #=> "You've reached test_route"
|
238
197
|
```
|
239
198
|
|
240
|
-
|
241
|
-
|
242
|
-
ATD also allows you to use different "apps". Each app is independent of the others and lives is a class with the same name as the app. A file can have any number of apps, each of which can have it's own settings, files, and routes. By default, adding routes to `main` will make them a part of the app `DefaultApp`, which will work fine if you only need one app.
|
199
|
+
## Compilation
|
243
200
|
|
244
|
-
|
201
|
+
_N.B. At some point in the indefinite future there might (but probably will) be a [tilt](https://github.com/rtomayko/tilt) integration_
|
245
202
|
|
246
|
-
|
203
|
+
### The Basics
|
247
204
|
|
248
|
-
|
205
|
+
Because `ATD` attempts to practice separation of concerns, there is a special module, `ATD::Compilation` which is responsible for dealing with compilation. In `ATD` there are two types of compilers: First, there are precompilers, which run during the apps startup process and do things like minify assets and compile things which do not need to by dynamic. Second, there are compilers, which deal with dynamic assets and run whenever a `Route` is reached. To create them you can use the following syntax (the example is for compiling ERB files):
|
249
206
|
|
250
207
|
```ruby
|
251
|
-
|
252
|
-
|
208
|
+
to_compile "erb" do |file = "", *opts|
|
209
|
+
ERB.new(file).result
|
253
210
|
end
|
254
211
|
```
|
255
212
|
|
256
|
-
|
257
|
-
|
258
|
-
There are two basic ways to start the app. You can start it by calling `AppName.new.start` or more simply `AppName.start` which will create an instance and call start on it, or you can use the more common syntax. For `DefaultApp` that would be:
|
213
|
+
Or if you wanted to define a precompiler to remove all the newlines from a JS file:
|
259
214
|
|
260
215
|
```ruby
|
261
|
-
|
262
|
-
|
263
|
-
```
|
264
|
-
|
265
|
-
And for `MyApp` that would be:
|
266
|
-
|
267
|
-
```ruby
|
268
|
-
class MyApp
|
269
|
-
request "/", "Hello World!"
|
270
|
-
start
|
216
|
+
to_precompile "js" do |file = "", *opts|
|
217
|
+
file.gsub("\n", "")
|
271
218
|
end
|
272
219
|
```
|
273
220
|
|
274
|
-
|
221
|
+
If you notices, the precompilers and compilers take the contents of the file as the first argument (`file`) and they take the compiler options as the second argument (`opts`). The options are just whatever was passed with the route, for example in `r "/", "my_file.erb", hi: true` `opts == {hi: true}`.
|
275
222
|
|
276
|
-
|
223
|
+
> **Under the Hood**
|
224
|
+
>
|
225
|
+
> The compilation works by going through the file extensions from last to first and running the compilations for each extension in that order. For example `file.html.erb` will first be compiled by the "erb" compiler, then the output of the "erb" compiler will be compiled by the "html" compiler.
|
277
226
|
|
278
|
-
|
279
|
-
class Name < ATD::App
|
280
|
-
request "/", "Hello World"
|
281
|
-
end
|
282
|
-
```
|
227
|
+
### Precompilation
|
283
228
|
|
284
|
-
|
285
|
-
|
286
|
-
Currently there is no specified logging interface. Just use `puts` or `p` to log to `STDOUT`.
|
229
|
+
> Precompilers are not sufficiently advanced to get their own special section yet, but will be soon.
|
287
230
|
|
288
231
|
### Compilation
|
289
232
|
|
290
|
-
|
291
|
-
|
292
|
-
```ruby
|
293
|
-
module ATD::Compilation::Precomiler
|
294
|
-
def extension(file, *opts) # This will work with any file that has .extension
|
295
|
-
# File is the contents of the file being compiled
|
296
|
-
# Whatever you return is what the file will be compiled to
|
297
|
-
file
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
module ATD::Compilation::Compiler
|
302
|
-
# Same thing here
|
303
|
-
def extension(file, *opts) # This will work with any file that has .extension
|
304
|
-
file
|
305
|
-
end
|
306
|
-
end
|
307
|
-
```
|
308
|
-
|
309
|
-
The compilation works by going through the file extensions from last to first and running the compilations for each extension in that order. For example `file.html.erb` will first be compiled by the ERB compiler, then the output of the ERB compiler will be compiled by the HTML compiler.
|
310
|
-
|
311
|
-
When you include a file with then `request "/route", "file.ext` it will be precompiled, but not compiled. If you want live compilation there are a few options. If you want live compilation and precompilation you will have to use the following syntax:
|
312
|
-
|
313
|
-
```ruby
|
314
|
-
request "/", "file.ext" do
|
315
|
-
live_compilation_method @http[:output]
|
316
|
-
end
|
317
|
-
```
|
318
|
-
|
319
|
-
If you don't need precompilation and just want live compilation (the ususal use case) then you can just use the following format:
|
320
|
-
|
321
|
-
```ruby
|
322
|
-
request "/" do
|
323
|
-
live_compilation_method File.read("/assets/file.ext")
|
324
|
-
end
|
325
|
-
```
|
326
|
-
|
327
|
-
## Design Paradigms
|
328
|
-
|
329
|
-
ATD is designed to fit into many different design paradigms, and to allow each person to adopt their own styles while leaving code readable to everyone. Do do this, the code was left fairly unstructured, but [here are a few examples from well known frameworks](https://gitlab.com/izwick-schachter/atd-examples.git).
|
233
|
+
> Compilers are not sufficiently advanced to get their own special section yet, but will be soon.
|
330
234
|
|
331
235
|
## Documentation
|
332
236
|
|
@@ -336,12 +240,16 @@ You can find the YARD docs at http://izwick-schachter.gitlab.io/atd/YARD/.
|
|
336
240
|
|
337
241
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
338
242
|
|
339
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
243
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to the gem page on [rubygems.org](https://rubygems.org/gems/atd).
|
244
|
+
|
245
|
+
### Some notes about Semantic Versioning
|
246
|
+
|
247
|
+
[Semantic versioning]() is pretty nice. I like it. But, I really only want to hit a major version when it's production ready, so for now will we will follow semver in that bugfix releases (x.x.*) will be backwards compatible, but minor versions will not be. As soon as this is production ready we will hit 1.0.0 and start using semver.
|
340
248
|
|
341
249
|
## Contributing
|
342
250
|
|
343
|
-
Bug reports and
|
251
|
+
[Bug reports](https://gitlab.com/izwick-schachter/atd/issues) and [merge requests](https://gitlab.com/izwick-schachter/atd/merge_requests) are welcome on GitLab. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
|
344
252
|
|
345
|
-
|
253
|
+
## Contribution Policies
|
346
254
|
|
347
|
-
Every
|
255
|
+
Every contribution should correspond to a relevant issue which you keep up to date with notes on what you are working on. Each issues gets branched off of development and is named `issue/#{issue_number}`. When you are ready to merge it back in, make sure it passes both rubocop and all the tests. Each issue should get additional tests if necessary.
|
data/atd.gemspec
CHANGED
@@ -22,9 +22,12 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.bindir = "exe"
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ["lib"]
|
25
|
+
spec.required_ruby_version = '>= 2.2.2'
|
26
|
+
spec.required_rubygems_version = '>= 2.6.4'
|
25
27
|
|
26
28
|
spec.add_runtime_dependency "rack", "~> 2.0"
|
27
29
|
spec.add_runtime_dependency "webrick", "~> 1.3"
|
30
|
+
spec.add_runtime_dependency "mime-types", "~> 3.0"
|
28
31
|
|
29
32
|
spec.add_development_dependency "bundler", "~> 1.12"
|
30
33
|
spec.add_development_dependency "rake", "~> 10.0"
|
@@ -34,4 +37,6 @@ Gem::Specification.new do |spec|
|
|
34
37
|
spec.add_development_dependency "yard", "~> 0.9"
|
35
38
|
spec.add_development_dependency "rack-test", "~> 0.6"
|
36
39
|
spec.add_development_dependency "simplecov", "~> 0.12"
|
40
|
+
spec.add_development_dependency "guard", "~> 2.14"
|
41
|
+
spec.add_development_dependency "guard-minitest", "~> 2.4"
|
37
42
|
end
|
data/lib/atd.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
require_relative "atd/version"
|
2
2
|
require "rack"
|
3
3
|
require "webrick"
|
4
|
+
require "mime-types"
|
5
|
+
require_relative "atd/internal_helpers"
|
4
6
|
require_relative "atd/builtin_class_modifications"
|
7
|
+
require_relative "atd/internal_helpers"
|
5
8
|
require_relative "atd/routes"
|
6
9
|
# Extension packs
|
7
10
|
# require_relative "extensions/precompilers"
|
8
11
|
|
9
12
|
# The assistant technical director of your website. It does the dirty work so you can see the big picture.
|
10
13
|
module ATD
|
14
|
+
using Refinements
|
11
15
|
# Creates a new ATD App based on the template of {ATD::App}.
|
12
16
|
# @return [ATD::App]
|
13
17
|
# @param [Symbol] name The name of the new app and new class generated.
|
@@ -20,11 +24,16 @@ module ATD
|
|
20
24
|
# So called because each instance stores a route, and will be called if that route is reached.
|
21
25
|
# A route for the purposes of {ATD} is a parser that will be fed env in {ATD::App#call the rack app}.
|
22
26
|
class Route
|
23
|
-
|
27
|
+
include InternalHelpers
|
28
|
+
|
29
|
+
attr_reader :app
|
30
|
+
attr_accessor :args, :method, :block, :path, :output, :actions, :status_code, :filename, :headers
|
24
31
|
|
25
32
|
# The first two arguments must me the path and the output.
|
26
33
|
def initialize(*args, &block)
|
34
|
+
@args, @block, @path, @output, @actions, @status_code, @filename = nil
|
27
35
|
@status_code = 200
|
36
|
+
@headers = {}
|
28
37
|
@method = [:get, :post, :put, :patch, :delete]
|
29
38
|
@method = [] if args.last.is_a?(Hash) && !(args.last[:respond_to].nil? || args.last[:ignore].nil?)
|
30
39
|
@app = :DefaultApp
|
@@ -71,6 +80,7 @@ module ATD
|
|
71
80
|
# the variables up through the different method calls.
|
72
81
|
if args.first.is_a?(ATD::Route)
|
73
82
|
@method = args.first.method
|
83
|
+
@filename = args.first.filename
|
74
84
|
@output = args.first.output
|
75
85
|
@path = args.first.path
|
76
86
|
@args = args.first.args
|
@@ -85,67 +95,65 @@ module ATD
|
|
85
95
|
end
|
86
96
|
end
|
87
97
|
|
88
|
-
# Converts an instance of {ATD::Route} into it's Hash representation.
|
89
|
-
# The format for the Hash is listed {ATD::App#initialize here}
|
90
|
-
# @api private
|
91
|
-
def to_h
|
92
|
-
routes = {}
|
93
|
-
routes[@path] = {}
|
94
|
-
routes[@path][@method] = {}
|
95
|
-
routes[@path][@method] = {
|
96
|
-
status_code: @status_code,
|
97
|
-
output: @output,
|
98
|
-
block: @block,
|
99
|
-
args: @args,
|
100
|
-
route: self
|
101
|
-
}
|
102
|
-
routes
|
103
|
-
end
|
104
|
-
|
105
98
|
private
|
106
99
|
|
107
100
|
# This should also manage @method at some point
|
108
|
-
def parse_args(*args, &block)
|
109
|
-
args.
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
@
|
115
|
-
|
116
|
-
|
101
|
+
def parse_args(path = "", output = "", *args, &block)
|
102
|
+
args = Hash(args[0]) if args.is_a? Array
|
103
|
+
if output.is_a? Hash
|
104
|
+
args.merge(output)
|
105
|
+
output = ""
|
106
|
+
end
|
107
|
+
@args = Hash(@args).merge(args)
|
108
|
+
# rubocop:disable Style/EmptyElse
|
109
|
+
if !path.is_a? ATD::Route
|
110
|
+
@block = block
|
111
|
+
@path = path
|
112
|
+
@output = output
|
113
|
+
else
|
114
|
+
# Maybe here it would make sense to assign path and output into args
|
115
|
+
end
|
116
|
+
# rubocop:enable Style/EmptyElse
|
117
117
|
# @output should be whatever the input is unless the input is a controller/action or the input is_file_string?
|
118
118
|
if @output =~ /^\w*#\w*$/ # Check if @path is a controller#action combo
|
119
119
|
controller, action = @output.split("#")
|
120
120
|
@action = Object.const_get(controller.to_sym).method(action.to_sym)
|
121
121
|
@output = @action.call
|
122
122
|
end
|
123
|
-
#
|
124
|
-
#
|
125
|
-
|
126
|
-
caller_locations.each do |caller_location|
|
127
|
-
target_dir = File.dirname(caller_location.absolute_path.to_s)
|
128
|
-
target_location.push(target_dir) unless target_dir.include?(__dir__)
|
129
|
-
end
|
130
|
-
# Method 2:
|
131
|
-
target_location = caller_locations.reject do |caller_location|
|
132
|
-
File.dirname(caller_location.absolute_path.to_s).include? __dir__
|
133
|
-
end
|
134
|
-
output_full_path = "#{File.dirname(target_location[0].absolute_path)}/assets/#{@output}"
|
123
|
+
# These next few lines are working on the assumption that if the file exists we want it, and the precompiler is
|
124
|
+
# counting on that.
|
125
|
+
output_full_path = asset @output
|
135
126
|
@output = File.new(output_full_path) if File.exist?(output_full_path) && !Dir.exist?(output_full_path)
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
127
|
+
@method += Array(args[:respond_to]) unless args[:respond_to].nil?
|
128
|
+
@method -= Array(args[:ignore]) unless args[:ignore].nil?
|
129
|
+
@status_code = args[:status] unless args[:status].nil?
|
130
|
+
@status_code = args[:status_code] unless args[:status_code].nil?
|
131
|
+
Compilation.precompile(self)
|
132
|
+
@headers["content-type"] = MIME::Types.of(@filename)[0].to_s unless @filename.nil?
|
133
|
+
@block = @actions unless @actions.nil?
|
142
134
|
self
|
143
135
|
end
|
144
136
|
end
|
145
137
|
|
138
|
+
module Helpers
|
139
|
+
def http
|
140
|
+
{ request: @request, response: @response, view: @view, method: @method, headers: @headers, status_code: @status_code }
|
141
|
+
end
|
142
|
+
|
143
|
+
def params
|
144
|
+
@request.params
|
145
|
+
end
|
146
|
+
|
147
|
+
def view
|
148
|
+
@view
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
146
152
|
# A template {App} that all Apps extend. When a new App is created with {ATD.new ATD.new} it extends this class.
|
147
153
|
class App
|
148
|
-
|
154
|
+
include Helpers
|
155
|
+
include Compilation
|
156
|
+
|
149
157
|
class << self
|
150
158
|
attr_accessor :routes # An array of instances of {ATD::Route} that belong to this {App}.
|
151
159
|
|
@@ -192,30 +200,15 @@ module ATD
|
|
192
200
|
# }
|
193
201
|
# @param [Array] routes An array of instances of {ATD::Route}.
|
194
202
|
def initialize(routes = [])
|
195
|
-
@routes =
|
196
|
-
|
197
|
-
route = route.clone
|
198
|
-
filename = ATD::Compilation.precompile(route, (route.args.last.is_a?(Hash) ? route.args.last[:precompile] : nil))
|
199
|
-
route_hash = route.to_h
|
200
|
-
current_route = route_hash[route.path][route.method]
|
201
|
-
current_route[:filename] = filename
|
202
|
-
block = current_route[:block]
|
203
|
-
# An instance method must be defined from the block make it the same as the controller actions. We don't want to
|
204
|
-
# convert the controller actions to blocks because if we did that, we would have to take them out of scope to allow
|
205
|
-
# them to use the @http variables.
|
206
|
-
current_route[:block] = define_singleton_method(block.object_id.to_s.tr("0-9", "a-j").to_sym, &block) unless block.nil?
|
207
|
-
current_route[:block] = route.actions unless route.actions.nil?
|
208
|
-
@routes = @routes.to_h.deep_merge(route_hash)
|
203
|
+
@routes = (routes + Array(self.class.routes)).each do |route|
|
204
|
+
Compilation.precompile(route) unless route.output.is_a? Hash
|
209
205
|
end
|
210
206
|
end
|
211
207
|
|
212
208
|
# Allows instance method route creation. Just another way of creating routes.
|
213
209
|
def request(*args, &block)
|
214
210
|
route = ATD::Route.new(*args, &block)
|
215
|
-
|
216
|
-
route_hash = route.to_h
|
217
|
-
route_hash[route.path][route.method][:filename] = filename
|
218
|
-
@routes = @routes.to_h.deep_merge(route_hash)
|
211
|
+
@routes += Array(route)
|
219
212
|
route
|
220
213
|
end
|
221
214
|
alias req request
|
@@ -232,27 +225,29 @@ module ATD
|
|
232
225
|
# It will return status code 200 and whatever output corresponds the that route if it exists, and if it doesn't
|
233
226
|
# it will return status code 404 and the message "Error 404"
|
234
227
|
def call(env)
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
228
|
+
routes = @routes.where(path: env["PATH_INFO"], method: env["REQUEST_METHOD"].downcase.to_sym)
|
229
|
+
warn "WARNING: Multiple routes matched the request" if routes.length > 1
|
230
|
+
route = routes.first
|
231
|
+
return error 404 if route.nil?
|
232
|
+
output = route.output
|
233
|
+
output = Compilation.compile(route)[:content] unless route.args[:compile] == false
|
234
|
+
return [route.status_code.to_i, Hash(route.headers), Array(output)] if route.block.nil?
|
235
|
+
generate_variables(env, route)
|
236
|
+
return_val = instance_eval(&route.block) if route.block.is_a? Proc
|
237
|
+
return_val = method(route.block).call if route.block.is_a? Method
|
238
|
+
@view[:raw] = return_val if @view[:raw].nil? || @view[:raw].empty?
|
239
|
+
[@status_code.to_i, Hash(@headers), Array(@view[:raw])]
|
244
240
|
end
|
245
241
|
|
246
242
|
private
|
247
243
|
|
248
|
-
def
|
249
|
-
|
250
|
-
|
251
|
-
@
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
@http = { status_code: 200, headers: {} }.merge(additional_params)
|
244
|
+
def generate_variables(env, route)
|
245
|
+
@status_code = 200
|
246
|
+
@headers = {}
|
247
|
+
@view = { raw: route.output }
|
248
|
+
@request = Rack::Request.new(env)
|
249
|
+
@method = env["REQUEST_METHOD"]
|
250
|
+
@response = Rack::Response.new(env)
|
256
251
|
end
|
257
252
|
|
258
253
|
def error(number)
|
@@ -264,7 +259,7 @@ end
|
|
264
259
|
|
265
260
|
# @return [ATD::Route]
|
266
261
|
def request(*args, &block)
|
267
|
-
ATD::App.request(args, block)
|
262
|
+
ATD::App.request(*args, &block)
|
268
263
|
end
|
269
264
|
alias req request
|
270
265
|
alias r request
|
@@ -1,38 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
module ATD::Refinements
|
2
|
+
# @!visibility private
|
3
|
+
refine Hash do
|
4
|
+
# Not only merges two hashes, but also merges the hashes that may be nested in.
|
5
|
+
#
|
6
|
+
# For example:
|
7
|
+
# {a: {b: "c"}}
|
8
|
+
# Is a nested hash
|
9
|
+
def deep_merge(second)
|
10
|
+
merger = proc do |_, v1, v2|
|
11
|
+
if v1.is_a?(Hash) && v2.is_a?(Hash) then v1.merge(v2, &merger)
|
12
|
+
elsif v1.is_a?(Array) && v2.is_a?(Array) then v1 | v2
|
13
|
+
elsif [:undefined, nil, :nil].include?(v2) then v1
|
14
|
+
else v2
|
15
|
+
end
|
14
16
|
end
|
17
|
+
merge(second.to_h, &merger)
|
15
18
|
end
|
16
|
-
merge(second.to_h, &merger)
|
17
|
-
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def include_in_key?(search)
|
21
|
+
each do |key, val|
|
22
|
+
return val if key.is_a?(Array) && key.include?(search)
|
23
|
+
end
|
22
24
|
end
|
23
25
|
end
|
24
|
-
end
|
25
26
|
|
26
|
-
# This method only exists for the test suite, specifically {ATDTest#test_route_creation}.
|
27
|
-
# @!visibility private
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
# This method only exists for the test suite, specifically {ATDTest#test_route_creation}.
|
28
|
+
# @!visibility private
|
29
|
+
refine Object do
|
30
|
+
# Returns the instance variables of a class
|
31
|
+
def class_instance_variables
|
32
|
+
instance_variables.map { |var| [var, instance_variable_get(var)] }.to_h
|
33
|
+
end
|
32
34
|
end
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
refine Array do
|
37
|
+
def where(criteria)
|
38
|
+
select do |element|
|
39
|
+
criteria.all? do |criterion, expected_value|
|
40
|
+
Array(element.public_send(criterion)).include?(expected_value)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
37
44
|
end
|
38
45
|
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module InternalHelpers
|
2
|
+
def asset(name)
|
3
|
+
return "" if name.to_s.empty?
|
4
|
+
target_location = caller_locations.reject do |caller_location|
|
5
|
+
File.dirname(caller_location.absolute_path.to_s).include? File.expand_path("..", __dir__)
|
6
|
+
end
|
7
|
+
"#{File.dirname(target_location[0].absolute_path)}/assets/#{name}"
|
8
|
+
end
|
9
|
+
end
|
data/lib/atd/routes.rb
CHANGED
@@ -1,60 +1,59 @@
|
|
1
|
-
|
2
1
|
module ATD
|
3
2
|
# This module holds everything related to the compilation of routes.
|
4
3
|
module Compilation
|
5
|
-
|
6
|
-
module Precompiler
|
7
|
-
extend self
|
4
|
+
include InternalHelpers
|
8
5
|
|
9
|
-
|
10
|
-
|
11
|
-
instance_methods(true) - [:filetypes]
|
12
|
-
end
|
6
|
+
def respond_to_missing?(method, include_private = false)
|
7
|
+
ATD::Compilation.compilers.key?(method.to_sym) || super
|
13
8
|
end
|
14
9
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# Lists all file extentions which have defined compiler methods
|
20
|
-
def filetypes
|
21
|
-
instance_methods(true) - [:filetypes]
|
22
|
-
end
|
10
|
+
def method_missing(method, *args, &block)
|
11
|
+
return super unless ATD::Compilation.compilers.key?(method.to_sym)
|
12
|
+
filename = args.pop
|
13
|
+
ATD::Compilation.compilers[method].call(ATD::Compilation.parse(ATD::Compilation.compilers, filename, File.read(asset(filename)), Hash(args).merge(run: false)), *args)
|
23
14
|
end
|
24
15
|
|
25
|
-
|
26
|
-
|
27
|
-
# It will also take the file and call the corresponding compilation method on it.
|
28
|
-
def self.compile(name, contents)
|
29
|
-
return contents if name.nil?
|
30
|
-
contents = File.read(contents) if contents.is_a? File
|
31
|
-
parse(Compiler, name, contents)
|
32
|
-
end
|
16
|
+
class << self
|
17
|
+
attr_accessor :compilers, :precompilers
|
33
18
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
return nil if route.output.nil?
|
40
|
-
if route.output.is_a?(File)
|
41
|
-
name = route.output.is_a?(File) ? File.basename(route.output) : route.output.dup
|
42
|
-
file = route.output.is_a?(File) ? route.output.dup : File.new(route.output)
|
43
|
-
route.output = parse(Precompiler, name, File.read(file)) if opts[0].nil? || opts[0]
|
44
|
-
return name
|
19
|
+
# This method is responsible for live compilation. It takes an ATD::Route as input, and returns either
|
20
|
+
# the filename if Route.output is a file or the Route.output string if Route.output is a string.
|
21
|
+
# It will also take the file and call the corresponding compilation method on it.
|
22
|
+
def compile(route, *opts)
|
23
|
+
{ content: parse(@compilers, route.filename, route.output, opts), name: route.filename }
|
45
24
|
end
|
46
|
-
route.output
|
47
|
-
end
|
48
25
|
|
49
|
-
|
50
|
-
|
26
|
+
# This method is responsible for precompilation. It takes an ATD::Route as input and compilation options. It
|
27
|
+
# sets the routes filename and output properties to be the filename and the contents of that file or, if route.output
|
28
|
+
# doesn't correspond to a valid file, it sets both to be route.output. It returns the precompiled version of the file
|
29
|
+
# regardless, and sets route.ouput to be the precompiled version unless the opt precompile: false is passed.
|
30
|
+
def precompile(route, *opts)
|
31
|
+
opts = Hash(opts[0]) unless opts.is_a? Hash
|
32
|
+
opts = opts.merge(route.args)
|
33
|
+
# route.output should always be a String, File, or NilClass
|
34
|
+
name = route.output.to_s
|
35
|
+
name = File.basename(route.output) if route.output.is_a?(File)
|
36
|
+
# name should always be a String
|
37
|
+
content = if route.output.is_a?(File) || (route.output.is_a?(String) && !route.output.empty? && File.exist?(route.output) && !Dir.exist?(route.output))
|
38
|
+
File.read(route.output)
|
39
|
+
elsif !route.output.nil?
|
40
|
+
route.output
|
41
|
+
else
|
42
|
+
""
|
43
|
+
end
|
44
|
+
# content should always be a String
|
45
|
+
route.output = parse(@precompilers, name, content, opts) unless opts[:precompile] == false
|
46
|
+
route.filename = name
|
47
|
+
{ content: parse(@precompilers, name, content, opts), name: route.filename }
|
48
|
+
end
|
51
49
|
|
52
|
-
def parse(type, name, contents)
|
53
|
-
|
54
|
-
extensions = name
|
50
|
+
def parse(type, name, contents, *opts)
|
51
|
+
return contents if name.include?("\n")
|
52
|
+
extensions = name.to_s.split(".")
|
53
|
+
extensions.shift
|
55
54
|
extensions.each do |extension|
|
56
|
-
if type.
|
57
|
-
contents = type.
|
55
|
+
if type.key? extension.to_sym
|
56
|
+
contents = type[extension.to_sym].call(contents, *opts) if Hash(opts.last)[:run] != false
|
58
57
|
extensions -= [extension]
|
59
58
|
end
|
60
59
|
end
|
@@ -64,6 +63,19 @@ module ATD
|
|
64
63
|
end
|
65
64
|
end
|
66
65
|
|
66
|
+
ATD::Compilation.compilers = {}
|
67
|
+
ATD::Compilation.precompilers = {}
|
68
|
+
|
69
|
+
def to_compile(name, &block)
|
70
|
+
warn "You have overriden the #{name.to_sym} compiler" if ATD::Compilation.compilers.key? name.to_sym
|
71
|
+
ATD::Compilation.compilers[name.to_sym] = block
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_precompile(name, &block)
|
75
|
+
warn "You have overriden the #{name.to_sym} precompiler" if ATD::Compilation.compilers.key? name.to_sym
|
76
|
+
ATD::Compilation.precompilers[name.to_sym] = block
|
77
|
+
end
|
78
|
+
|
67
79
|
class Object
|
68
|
-
include ATD::Compilation
|
80
|
+
include ATD::Compilation
|
69
81
|
end
|
data/lib/atd/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: atd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ACecretMaster
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mime-types
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +164,34 @@ dependencies:
|
|
150
164
|
- - "~>"
|
151
165
|
- !ruby/object:Gem::Version
|
152
166
|
version: '0.12'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: guard
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '2.14'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '2.14'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: guard-minitest
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '2.4'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '2.4'
|
153
195
|
description:
|
154
196
|
email:
|
155
197
|
- izwick.schachter@gmail.com
|
@@ -164,6 +206,7 @@ files:
|
|
164
206
|
- CHANGELOG
|
165
207
|
- CODE_OF_CONDUCT.md
|
166
208
|
- Gemfile
|
209
|
+
- Guardfile
|
167
210
|
- LICENSE
|
168
211
|
- README.md
|
169
212
|
- Rakefile
|
@@ -172,6 +215,7 @@ files:
|
|
172
215
|
- bin/setup
|
173
216
|
- lib/atd.rb
|
174
217
|
- lib/atd/builtin_class_modifications.rb
|
218
|
+
- lib/atd/internal_helpers.rb
|
175
219
|
- lib/atd/routes.rb
|
176
220
|
- lib/atd/version.rb
|
177
221
|
- lib/extensions/precompilers.rb
|
@@ -188,15 +232,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
188
232
|
requirements:
|
189
233
|
- - ">="
|
190
234
|
- !ruby/object:Gem::Version
|
191
|
-
version:
|
235
|
+
version: 2.2.2
|
192
236
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
193
237
|
requirements:
|
194
238
|
- - ">="
|
195
239
|
- !ruby/object:Gem::Version
|
196
|
-
version:
|
240
|
+
version: 2.6.4
|
197
241
|
requirements: []
|
198
242
|
rubyforge_project:
|
199
|
-
rubygems_version: 2.
|
243
|
+
rubygems_version: 2.6.10
|
200
244
|
signing_key:
|
201
245
|
specification_version: 4
|
202
246
|
summary: The assistant technical director of your website. It does the dirty work
|