atd 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.gitlab-ci.yml +4 -0
- data/.rubocop.yml +12 -0
- data/.yardopts +1 -0
- data/CHANGELOG +10 -0
- data/README.md +37 -2
- data/atd.gemspec +1 -1
- data/lib/atd/routes.rb +26 -30
- data/lib/atd/version.rb +1 -1
- data/lib/atd.rb +51 -17
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4b052a0c402077fa55a5c00e96db96c1963430a
|
4
|
+
data.tar.gz: 487ab2f6ce28280effbf0a1faff0d9b9959cd389
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db837886aa4f434403f51f8f1d6cf796c6cfd0cc1e4ef095c336b967a0c5d22dc15f6d93e20afe7e3f8f897da6f7659d0e13be366a5fb4b73730c886f4996640
|
7
|
+
data.tar.gz: 4075dd51b6e64f6977c13b6d6d684ab66c5abca0c992b5efa0752c07b251835bd78e13984716c1fb7c3b1dbfee7cd90a04b851813da7b291a4b1fb53e222e7be
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
data/.rubocop.yml
CHANGED
@@ -12,4 +12,16 @@ Style/ParallelAssignment:
|
|
12
12
|
Enabled: false
|
13
13
|
|
14
14
|
Style/Documentation:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
Style/ModuleFunction:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Style/ClassAndModuleChildren:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Metrics:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Lint/UnifiedInteger:
|
15
27
|
Enabled: false
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--exclude lib/extensions/
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
Current Repo Version:
|
2
|
+
|
3
|
+
v0.4.0
|
4
|
+
- Fixed an issue where routes without blocks wouldn't return status codes (#9)
|
5
|
+
- Allowed inline compilation as an alternative to the currently not working live compilation (#13), new inline version allows a hacky alternative (#17)
|
6
|
+
- Added new spaced syntax instead of dotted (`get post put "/", "Hello World"` instead of `get.post.put "/", "Hello World"`) (#10)
|
7
|
+
- Allowed the return value of the block to be the output if none is given (#15)
|
8
|
+
- Added Rack::Response to the @http variable (#13)
|
9
|
+
- Removed the `#is_file_string?` method in favor of simply checking if the file exists (#8)
|
10
|
+
|
1
11
|
v0.3.2:
|
2
12
|
- Really began using Gitlab issues
|
3
13
|
- Synchronized development branch with master
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
[![build status][build status badge]][build status link] [![coverage report][coverage report badge]][coverage report link] [![gem version][gem version badge]][gem version link] [![documentation coverage][documentation coverage badge]][documentation coverage link]
|
2
2
|
|
3
|
-
[documentation coverage badge]: https://img.shields.io/badge/YARD%20coverage-
|
3
|
+
[documentation coverage badge]: https://img.shields.io/badge/YARD%20coverage-100%-brightgreen.svg?style=flat-square
|
4
4
|
[documentation coverage link]: http://izwick-schachter.gitlab.io/atd/YARD/coverage
|
5
5
|
|
6
6
|
[build status badge]: https://gitlab.com/izwick-schachter/atd/badges/master/build.svg?style=flat-square
|
@@ -19,6 +19,7 @@
|
|
19
19
|
- [Setup](#setup)
|
20
20
|
- [Routing](#routing)
|
21
21
|
- [Basic Routing](#basic-routing)
|
22
|
+
- [Options Hash](#options-hash)
|
22
23
|
- [Serving Files](#serving-files)
|
23
24
|
- [Advanced Routing](#advanced-routing)
|
24
25
|
- [DefaultApp](#defaultapp)
|
@@ -37,6 +38,7 @@
|
|
37
38
|
- [Development](#development)
|
38
39
|
- [Contributing](#contributing)
|
39
40
|
|
41
|
+
|
40
42
|
# ATD
|
41
43
|
|
42
44
|
ATD is a new web development backend framework for Ruby. It is built on top of rack, and is meant to be small easy and light. That is why it provides a very minimalist way of writing your backend. It treats a request as a request, without splitting it based on the HTTP method.
|
@@ -125,6 +127,19 @@ Or, a simpler way would be with `:ignore`:
|
|
125
127
|
request "/", "Hello World", ignore: :delete
|
126
128
|
```
|
127
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:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
status: 200 # Integer > 99 and < 1000. This will be the status code returned unless it is overridden by
|
138
|
+
status_code: 200 # Same as status, but overrides. A slightly more verbose syntax.
|
139
|
+
respond_to: nil # A list of HTTP methods as lowercase symbols which the route should respond to.
|
140
|
+
ignore: nil # A list of HTTP methods as lowercase symbols which the route should not respond to.
|
141
|
+
```
|
142
|
+
|
128
143
|
#### Serving Files
|
129
144
|
|
130
145
|
All files that you want to serve must be in the `assets` directory if they are, then it is simple to just create a route, and put the filename as the output. For example this will serve `assets/index.html`:
|
@@ -293,6 +308,22 @@ end
|
|
293
308
|
|
294
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.
|
295
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
|
+
|
296
327
|
## Design Paradigms
|
297
328
|
|
298
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).
|
@@ -309,4 +340,8 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
309
340
|
|
310
341
|
## Contributing
|
311
342
|
|
312
|
-
Bug reports and pull requests are welcome on GitLab at https://gitlab.com/izwick-schachter/atd/issues. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
343
|
+
Bug reports and pull requests are welcome on GitLab at https://gitlab.com/izwick-schachter/atd/issues. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
344
|
+
|
345
|
+
### Contribution Policies
|
346
|
+
|
347
|
+
Every constribution should correspond to a relevent 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 merege it back in, make sure it passes both rubocop and all the tests. Each issue should get additional test if necessary.
|
data/atd.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["izwick.schachter@gmail.com"]
|
11
11
|
|
12
12
|
spec.summary = 'The assistant technical director of your website. It does the dirty work so you can see the big picture.'
|
13
|
-
spec.homepage = "https://
|
13
|
+
spec.homepage = "https://gitlab.com/izwick-schachter/atd"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
data/lib/atd/routes.rb
CHANGED
@@ -16,53 +16,45 @@ module ATD
|
|
16
16
|
module Compiler
|
17
17
|
extend self
|
18
18
|
|
19
|
-
# Lists all file
|
19
|
+
# Lists all file extentions which have defined compiler methods
|
20
20
|
def filetypes
|
21
21
|
instance_methods(true) - [:filetypes]
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
# This method is responsible for live compilation.
|
25
|
+
# This method is responsible for live compilation. It takes an ATD::Route as input, and returns either
|
26
|
+
# the filename if Route.output is a file or the Route.output string if Route.output is a string.
|
27
|
+
# It will also take the file and call the corresponding compilation method on it.
|
26
28
|
def self.compile(name, contents)
|
27
|
-
return contents if name.nil?
|
28
|
-
|
29
|
-
name
|
30
|
-
extensions = name - [name.first]
|
31
|
-
extensions.each do |extension|
|
32
|
-
contents = Compiler.send(extension, contents) if Compiler.filetypes.include? extension.to_sym
|
33
|
-
end
|
34
|
-
contents
|
29
|
+
return contents if name.nil?
|
30
|
+
contents = File.read(contents) if contents.is_a? File
|
31
|
+
parse(Compiler, name, contents)
|
35
32
|
end
|
36
33
|
|
37
|
-
# This method is responsible for precompilation
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
if
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
34
|
+
# This method is responsible for precompilation. It takes an ATD::Route as input, and returns either
|
35
|
+
# the filename if Route.output is a file or the Route.output string if Route.output is a string.
|
36
|
+
# It will also take the file and call the corresponding precompilation method on it.
|
37
|
+
# route.output is either a full, expanded file path, a file, or a string
|
38
|
+
def self.precompile(route, *opts)
|
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
|
48
45
|
end
|
49
|
-
|
46
|
+
route.output
|
50
47
|
end
|
51
48
|
|
52
49
|
class << self
|
53
50
|
private
|
54
51
|
|
55
|
-
def
|
56
|
-
return File.read(File.join("assets/#{output}")) if output.is_a?(String) && File.exist?("assets/#{output}")
|
57
|
-
return File.read(output) if output.is_a? File
|
58
|
-
end
|
59
|
-
|
60
|
-
def precompile(name, contents)
|
52
|
+
def parse(type, name, contents)
|
61
53
|
name = name.split(".")
|
62
54
|
extensions = name - [name.first]
|
63
55
|
extensions.each do |extension|
|
64
|
-
if
|
65
|
-
contents =
|
56
|
+
if type.filetypes.include? extension.to_sym
|
57
|
+
contents = type.send(extension, contents)
|
66
58
|
extensions -= [extension]
|
67
59
|
end
|
68
60
|
end
|
@@ -71,3 +63,7 @@ module ATD
|
|
71
63
|
end
|
72
64
|
end
|
73
65
|
end
|
66
|
+
|
67
|
+
class Object
|
68
|
+
include ATD::Compilation::Compiler
|
69
|
+
end
|
data/lib/atd/version.rb
CHANGED
data/lib/atd.rb
CHANGED
@@ -4,7 +4,7 @@ require "webrick"
|
|
4
4
|
require_relative "atd/builtin_class_modifications"
|
5
5
|
require_relative "atd/routes"
|
6
6
|
# Extension packs
|
7
|
-
require_relative "extensions/precompilers"
|
7
|
+
# require_relative "extensions/precompilers"
|
8
8
|
|
9
9
|
# The assistant technical director of your website. It does the dirty work so you can see the big picture.
|
10
10
|
module ATD
|
@@ -20,10 +20,11 @@ module ATD
|
|
20
20
|
# So called because each instance stores a route, and will be called if that route is reached.
|
21
21
|
# A route for the purposes of {ATD} is a parser that will be fed env in {ATD::App#call the rack app}.
|
22
22
|
class Route
|
23
|
-
attr_accessor :args, :method, :block, :path, :output, :app, :actions
|
23
|
+
attr_accessor :args, :method, :block, :path, :output, :app, :actions, :status_code
|
24
24
|
|
25
25
|
# The first two arguments must me the path and the output.
|
26
26
|
def initialize(*args, &block)
|
27
|
+
@status_code = 200
|
27
28
|
@method = [:get, :post, :put, :patch, :delete]
|
28
29
|
@method = [] if args.last.is_a?(Hash) && !(args.last[:respond_to].nil? || args.last[:ignore].nil?)
|
29
30
|
@app = :DefaultApp
|
@@ -66,6 +67,17 @@ module ATD
|
|
66
67
|
|
67
68
|
[:get, :post, :put, :delete, :patch].each do |method|
|
68
69
|
define_method(method) do |*args, &block|
|
70
|
+
# This conditional allows the syntax get post put "/", "Hello" because it passes
|
71
|
+
# the variables up through the different method calls.
|
72
|
+
if args.first.is_a?(ATD::Route)
|
73
|
+
@method = args.first.method
|
74
|
+
@output = args.first.output
|
75
|
+
@path = args.first.path
|
76
|
+
@args = args.first.args
|
77
|
+
@block = args.first.block
|
78
|
+
@app = args.first.app
|
79
|
+
@actions = args.first.actions
|
80
|
+
end
|
69
81
|
@method = [method] if @method.length == 5
|
70
82
|
@method += [method]
|
71
83
|
@method.uniq!
|
@@ -81,6 +93,7 @@ module ATD
|
|
81
93
|
routes[@path] = {}
|
82
94
|
routes[@path][@method] = {}
|
83
95
|
routes[@path][@method] = {
|
96
|
+
status_code: @status_code,
|
84
97
|
output: @output,
|
85
98
|
block: @block,
|
86
99
|
args: @args,
|
@@ -91,22 +104,41 @@ module ATD
|
|
91
104
|
|
92
105
|
private
|
93
106
|
|
94
|
-
# This should also manage @
|
107
|
+
# This should also manage @method at some point
|
95
108
|
def parse_args(*args, &block)
|
96
109
|
args.compact!
|
97
110
|
args.flatten!
|
98
|
-
args.reject! { |arg| arg.is_a?(File) ? false : arg.empty? } # File doesn't respond to empty
|
111
|
+
args.reject! { |arg| arg.is_a?(File) || arg.is_a?(Proc) || arg ? false : arg.empty? } # File doesn't respond to empty
|
99
112
|
@block = block
|
113
|
+
# This requires the format ATD::Route.new(path, route, args)
|
100
114
|
@path ||= args.shift
|
101
115
|
@output ||= args.shift
|
102
116
|
@args = Array(@args).concat(args) unless args.nil?
|
117
|
+
# @output should be whatever the input is unless the input is a controller/action or the input is_file_string?
|
103
118
|
if @output =~ /^\w*#\w*$/ # Check if @path is a controller#action combo
|
104
119
|
controller, action = @output.split("#")
|
105
120
|
@action = Object.const_get(controller.to_sym).method(action.to_sym)
|
106
121
|
@output = @action.call
|
107
122
|
end
|
108
|
-
|
109
|
-
|
123
|
+
# TODO: Choose one! They all work... I think...
|
124
|
+
# Method 1:
|
125
|
+
target_location = []
|
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}"
|
135
|
+
@output = File.new(output_full_path) if File.exist?(output_full_path) && !Dir.exist?(output_full_path)
|
136
|
+
if args.is_a?(Hash) || args.last.is_a?(Hash)
|
137
|
+
@method += Array(args.last[:respond_to]) unless args.last[:respond_to].nil?
|
138
|
+
@method -= Array(args.last[:ignore]) unless args.last[:ignore].nil?
|
139
|
+
@status_code = args.last[:status] unless args.last[:status].nil?
|
140
|
+
@status_code = args.last[:status_code] unless args.last[:status_code].nil?
|
141
|
+
end
|
110
142
|
self
|
111
143
|
end
|
112
144
|
end
|
@@ -129,7 +161,7 @@ module ATD
|
|
129
161
|
|
130
162
|
[:get, :post, :put, :patch, :delete].each do |i|
|
131
163
|
define_method(i) do |*args, &block|
|
132
|
-
request.send(i, *args, &block)
|
164
|
+
request.send(i, *args, &block) # Makes get == r.get, post == r.post, etc.
|
133
165
|
end
|
134
166
|
end
|
135
167
|
|
@@ -162,20 +194,25 @@ module ATD
|
|
162
194
|
def initialize(routes = [])
|
163
195
|
@routes = {}
|
164
196
|
Array(routes + self.class.routes).each do |route|
|
165
|
-
|
197
|
+
route = route.clone
|
198
|
+
filename = ATD::Compilation.precompile(route, (route.args.last.is_a?(Hash) ? route.args.last[:precompile] : nil))
|
166
199
|
route_hash = route.to_h
|
167
200
|
current_route = route_hash[route.path][route.method]
|
168
201
|
current_route[:filename] = filename
|
169
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.
|
170
206
|
current_route[:block] = define_singleton_method(block.object_id.to_s.tr("0-9", "a-j").to_sym, &block) unless block.nil?
|
171
207
|
current_route[:block] = route.actions unless route.actions.nil?
|
172
208
|
@routes = @routes.to_h.deep_merge(route_hash)
|
173
209
|
end
|
174
210
|
end
|
175
211
|
|
212
|
+
# Allows instance method route creation. Just another way of creating routes.
|
176
213
|
def request(*args, &block)
|
177
214
|
route = ATD::Route.new(*args, &block)
|
178
|
-
filename = ATD::Compilation.
|
215
|
+
filename = ATD::Compilation.precompile(route, (route.args.last.is_a?(Hash) ? route.args.last[:precompile] : nil))
|
179
216
|
route_hash = route.to_h
|
180
217
|
route_hash[route.path][route.method][:filename] = filename
|
181
218
|
@routes = @routes.to_h.deep_merge(route_hash)
|
@@ -198,10 +235,12 @@ module ATD
|
|
198
235
|
@http = nil
|
199
236
|
route = route(env)
|
200
237
|
return error(404) if route.nil?
|
201
|
-
route[:output] =
|
238
|
+
route[:output] = Compilation.compile(route[:filename], route[:output]) unless !route[:args].nil? && !route[:args].empty? && route[:args][0].is_a?(Hash) && route[:args][0][:compile] == false
|
202
239
|
return [route[:status_code].to_i, Hash(route[:headers]), Array(route[:output])] if route[:block].nil?
|
203
|
-
http output: route[:output], request: Rack::Request.new(env), method: env["REQUEST_METHOD"]
|
204
|
-
|
240
|
+
http output: route[:output], request: Rack::Request.new(env), method: env["REQUEST_METHOD"], response: Rack::Response.new(env)
|
241
|
+
return_val = method(route[:block]).call
|
242
|
+
@http[:output] = return_val if @http[:output].nil?
|
243
|
+
[@http[:status_code].to_i, Hash(@http[:headers]), Array(@http[:output])]
|
205
244
|
end
|
206
245
|
|
207
246
|
private
|
@@ -212,11 +251,6 @@ module ATD
|
|
212
251
|
@routes[env["PATH_INFO"]].include_in_key?(env["REQUEST_METHOD"].downcase.to_sym)
|
213
252
|
end
|
214
253
|
|
215
|
-
def run_block(block)
|
216
|
-
method(block).call
|
217
|
-
[@http[:status_code].to_i, Hash(@http[:headers]), Array(@http[:output])]
|
218
|
-
end
|
219
|
-
|
220
254
|
def http(additional_params)
|
221
255
|
@http = { status_code: 200, headers: {} }.merge(additional_params)
|
222
256
|
end
|
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.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ACecretMaster
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -160,6 +160,7 @@ files:
|
|
160
160
|
- ".gitignore"
|
161
161
|
- ".gitlab-ci.yml"
|
162
162
|
- ".rubocop.yml"
|
163
|
+
- ".yardopts"
|
163
164
|
- CHANGELOG
|
164
165
|
- CODE_OF_CONDUCT.md
|
165
166
|
- Gemfile
|
@@ -174,7 +175,7 @@ files:
|
|
174
175
|
- lib/atd/routes.rb
|
175
176
|
- lib/atd/version.rb
|
176
177
|
- lib/extensions/precompilers.rb
|
177
|
-
homepage: https://
|
178
|
+
homepage: https://gitlab.com/izwick-schachter/atd
|
178
179
|
licenses:
|
179
180
|
- MIT
|
180
181
|
metadata:
|
@@ -195,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
196
|
version: '0'
|
196
197
|
requirements: []
|
197
198
|
rubyforge_project:
|
198
|
-
rubygems_version: 2.5.
|
199
|
+
rubygems_version: 2.5.2
|
199
200
|
signing_key:
|
200
201
|
specification_version: 4
|
201
202
|
summary: The assistant technical director of your website. It does the dirty work
|