lib/atd.rb in atd-0.3.2 vs lib/atd.rb in atd-0.4.0
- old
+ new
@@ -2,11 +2,11 @@
require "rack"
require "webrick"
require_relative "atd/builtin_class_modifications"
require_relative "atd/routes"
# Extension packs
-require_relative "extensions/precompilers"
+# require_relative "extensions/precompilers"
# The assistant technical director of your website. It does the dirty work so you can see the big picture.
module ATD
# Creates a new ATD App based on the template of {ATD::App}.
# @return [ATD::App]
@@ -18,14 +18,15 @@
end
# So called because each instance stores a route, and will be called if that route is reached.
# A route for the purposes of {ATD} is a parser that will be fed env in {ATD::App#call the rack app}.
class Route
- attr_accessor :args, :method, :block, :path, :output, :app, :actions
+ attr_accessor :args, :method, :block, :path, :output, :app, :actions, :status_code
# The first two arguments must me the path and the output.
def initialize(*args, &block)
+ @status_code = 200
@method = [:get, :post, :put, :patch, :delete]
@method = [] if args.last.is_a?(Hash) && !(args.last[:respond_to].nil? || args.last[:ignore].nil?)
@app = :DefaultApp
parse_args(*args, &block)
end
@@ -64,10 +65,21 @@
# @return ATD::Route
# Sets route to receive a delete request to path and execute the block provided (if one is provided)
[:get, :post, :put, :delete, :patch].each do |method|
define_method(method) do |*args, &block|
+ # This conditional allows the syntax get post put "/", "Hello" because it passes
+ # the variables up through the different method calls.
+ if args.first.is_a?(ATD::Route)
+ @method = args.first.method
+ @output = args.first.output
+ @path = args.first.path
+ @args = args.first.args
+ @block = args.first.block
+ @app = args.first.app
+ @actions = args.first.actions
+ end
@method = [method] if @method.length == 5
@method += [method]
@method.uniq!
parse_args(*args, &block)
end
@@ -79,36 +91,56 @@
def to_h
routes = {}
routes[@path] = {}
routes[@path][@method] = {}
routes[@path][@method] = {
+ status_code: @status_code,
output: @output,
block: @block,
args: @args,
route: self
}
routes
end
private
- # This should also manage @message at some point
+ # This should also manage @method at some point
def parse_args(*args, &block)
args.compact!
args.flatten!
- args.reject! { |arg| arg.is_a?(File) ? false : arg.empty? } # File doesn't respond to empty
+ args.reject! { |arg| arg.is_a?(File) || arg.is_a?(Proc) || arg ? false : arg.empty? } # File doesn't respond to empty
@block = block
+ # This requires the format ATD::Route.new(path, route, args)
@path ||= args.shift
@output ||= args.shift
@args = Array(@args).concat(args) unless args.nil?
+ # @output should be whatever the input is unless the input is a controller/action or the input is_file_string?
if @output =~ /^\w*#\w*$/ # Check if @path is a controller#action combo
controller, action = @output.split("#")
@action = Object.const_get(controller.to_sym).method(action.to_sym)
@output = @action.call
end
- @method += Array(args.last[:respond_to]) if args.last.is_a?(Hash) && !args.last[:respond_to].nil?
- @method -= Array(args.last[:ignore]) if args.last.is_a?(Hash) && !args.last[:ignore].nil?
+ # TODO: Choose one! They all work... I think...
+ # Method 1:
+ target_location = []
+ caller_locations.each do |caller_location|
+ target_dir = File.dirname(caller_location.absolute_path.to_s)
+ target_location.push(target_dir) unless target_dir.include?(__dir__)
+ end
+ # Method 2:
+ target_location = caller_locations.reject do |caller_location|
+ File.dirname(caller_location.absolute_path.to_s).include? __dir__
+ end
+ output_full_path = "#{File.dirname(target_location[0].absolute_path)}/assets/#{@output}"
+ @output = File.new(output_full_path) if File.exist?(output_full_path) && !Dir.exist?(output_full_path)
+ if args.is_a?(Hash) || args.last.is_a?(Hash)
+ @method += Array(args.last[:respond_to]) unless args.last[:respond_to].nil?
+ @method -= Array(args.last[:ignore]) unless args.last[:ignore].nil?
+ @status_code = args.last[:status] unless args.last[:status].nil?
+ @status_code = args.last[:status_code] unless args.last[:status_code].nil?
+ end
self
end
end
# A template {App} that all Apps extend. When a new App is created with {ATD.new ATD.new} it extends this class.
@@ -127,11 +159,11 @@
alias req request
alias r request
[:get, :post, :put, :patch, :delete].each do |i|
define_method(i) do |*args, &block|
- request.send(i, *args, &block)
+ request.send(i, *args, &block) # Makes get == r.get, post == r.post, etc.
end
end
# Starts the rack server
# @param [Class] server The server that you would like to use.
@@ -160,24 +192,29 @@
# }
# @param [Array] routes An array of instances of {ATD::Route}.
def initialize(routes = [])
@routes = {}
Array(routes + self.class.routes).each do |route|
- filename = ATD::Compilation.pre_parse(route) if route.args.last.nil? || route.args.last[:precompile].nil? || route.args.last[:precompile]
+ route = route.clone
+ filename = ATD::Compilation.precompile(route, (route.args.last.is_a?(Hash) ? route.args.last[:precompile] : nil))
route_hash = route.to_h
current_route = route_hash[route.path][route.method]
current_route[:filename] = filename
block = current_route[:block]
+ # An instance method must be defined from the block make it the same as the controller actions. We don't want to
+ # convert the controller actions to blocks because if we did that, we would have to take them out of scope to allow
+ # them to use the @http variables.
current_route[:block] = define_singleton_method(block.object_id.to_s.tr("0-9", "a-j").to_sym, &block) unless block.nil?
current_route[:block] = route.actions unless route.actions.nil?
@routes = @routes.to_h.deep_merge(route_hash)
end
end
+ # Allows instance method route creation. Just another way of creating routes.
def request(*args, &block)
route = ATD::Route.new(*args, &block)
- filename = ATD::Compilation.pre_parse(route) if route.args.last.nil? || route.args.last[:precompile].nil? || route.args.last[:precompile]
+ filename = ATD::Compilation.precompile(route, (route.args.last.is_a?(Hash) ? route.args.last[:precompile] : nil))
route_hash = route.to_h
route_hash[route.path][route.method][:filename] = filename
@routes = @routes.to_h.deep_merge(route_hash)
route
end
@@ -196,26 +233,23 @@
# it will return status code 404 and the message "Error 404"
def call(env)
@http = nil
route = route(env)
return error(404) if route.nil?
- route[:output] = ATD::Compilation.compile(route[:filename], route[:output])
+ 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
return [route[:status_code].to_i, Hash(route[:headers]), Array(route[:output])] if route[:block].nil?
- http output: route[:output], request: Rack::Request.new(env), method: env["REQUEST_METHOD"]
- run_block(route[:block])
+ http output: route[:output], request: Rack::Request.new(env), method: env["REQUEST_METHOD"], response: Rack::Response.new(env)
+ return_val = method(route[:block]).call
+ @http[:output] = return_val if @http[:output].nil?
+ [@http[:status_code].to_i, Hash(@http[:headers]), Array(@http[:output])]
end
private
def route(env)
return nil if @routes[env["PATH_INFO"]].nil?
# return @routes[env["PATH_INFO"]][[]] unless @routes[env["PATH_INFO"]][[]].nil?
@routes[env["PATH_INFO"]].include_in_key?(env["REQUEST_METHOD"].downcase.to_sym)
- end
-
- def run_block(block)
- method(block).call
- [@http[:status_code].to_i, Hash(@http[:headers]), Array(@http[:output])]
end
def http(additional_params)
@http = { status_code: 200, headers: {} }.merge(additional_params)
end