Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite #36

Merged
merged 41 commits into from
Jan 12, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
cd61297
Initial commit to rewrite of Athena
Blacksmoke16 Nov 26, 2019
f2c6646
First pass of argument parsing
Blacksmoke16 Nov 27, 2019
57dea0f
Refactor exception classes
Blacksmoke16 Nov 28, 2019
ab6b1e2
Some minor cleanup
Blacksmoke16 Nov 28, 2019
bf7d642
Initial exception handling implementation
Blacksmoke16 Nov 29, 2019
5164ef5
Support optional services within DI
Blacksmoke16 Dec 1, 2019
68ba7f7
Support resolving services within an array
Blacksmoke16 Dec 6, 2019
78f0f30
Support param converters again
Blacksmoke16 Dec 7, 2019
63cf295
Move routing annotations to their own file
Blacksmoke16 Dec 8, 2019
e21cdb8
Refactor how param converters work
Blacksmoke16 Dec 9, 2019
6e7dd9f
Better handle param converters
Blacksmoke16 Dec 12, 2019
1440d94
Remove legacy specs
Blacksmoke16 Dec 12, 2019
5dd02cb
Initial spec
Blacksmoke16 Dec 14, 2019
36c75e9
Refactor argument_resolver specs
Blacksmoke16 Dec 14, 2019
e8f6624
Add additional specs
Blacksmoke16 Dec 14, 2019
2f615c4
Fix some lint issues
Blacksmoke16 Dec 14, 2019
844bdbc
Support named or positional annotation args
Blacksmoke16 Dec 15, 2019
38d17dd
Make controllers classes again :S
Blacksmoke16 Dec 15, 2019
1b19071
Fix specs
Blacksmoke16 Dec 15, 2019
de3a709
Change implementation of route prefixes
Blacksmoke16 Dec 16, 2019
5baae3f
Initial CORS listener work
Blacksmoke16 Dec 21, 2019
5fa86ea
Support having access to the response in an action
Blacksmoke16 Dec 29, 2019
af071c2
implement configurationResolver into the cors listener
Blacksmoke16 Jan 1, 2020
ede63a1
Add spec for cors listener requests
Blacksmoke16 Jan 1, 2020
3241c03
Add specs for the response listener
Blacksmoke16 Jan 2, 2020
bc6e237
Fix lint issue
Blacksmoke16 Jan 2, 2020
da94386
Add some more specs for routing component
Blacksmoke16 Jan 2, 2020
ac4a1a4
Fix request store logic
Blacksmoke16 Jan 2, 2020
68bd235
Update config dep
Blacksmoke16 Jan 8, 2020
f79a91a
implement athena-di shard
Blacksmoke16 Jan 8, 2020
6bf1678
Install deps before running specs
Blacksmoke16 Jan 8, 2020
89ec146
Add support for PATCH endpoints
Blacksmoke16 Jan 8, 2020
2db1a4e
Add HEAD request for each GET request
Blacksmoke16 Jan 9, 2020
bbc721c
Support query param constraints
Blacksmoke16 Jan 10, 2020
5c54fd9
Refactor param converters to use an interface (module)
Blacksmoke16 Jan 11, 2020
8e7cfc3
Cors => CORS
Blacksmoke16 Jan 11, 2020
37a1308
Refactors param conversion to be more simple/flexible
Blacksmoke16 Jan 12, 2020
e5fd792
Use param versus name in ART::ParamConverter
Blacksmoke16 Jan 12, 2020
655a84a
Reraise the exception if in the development env and it's a 500
Blacksmoke16 Jan 12, 2020
9ddf23a
Update deps to use versions
Blacksmoke16 Jan 12, 2020
fd9cc01
Mention HEAD request on GET requests
Blacksmoke16 Jan 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion spec/compiler_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe Athena::Routing do
end

it "param converter annotation but missing action argument" do
assert_error "compiler/param_converter_missing_name.cr", "Route action 'CompileController#action's ParamConverter annotation is missing the argument's name. It was not provided as the first positional argument nor via the 'name' field."
assert_error "compiler/param_converter_missing_name.cr", "Route action 'CompileController#action's ParamConverter annotation is missing the argument's name. It was not provided as the first positional argument nor via the 'param' field."
end

it "when action argument count does not equal expected count" do
Expand Down
2 changes: 1 addition & 1 deletion spec/controllers/param_converter_controller.cr
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ParamConverterController < ART::Controller
doubled_num
end

@[ART::QueryParam(name: "num", converter: DoubleConverter(Int32))]
@[ART::QueryParam("num", converter: DoubleConverter(Int32))]
@[ART::Get(path: "/double-query")]
def double_query(num : Int32) : Int32
num
Expand Down
87 changes: 38 additions & 49 deletions src/annotations.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,10 @@ module Athena::Routing
# * path : `String` - The path for the endpoint, may also be provided as the first positional argument.
# * constraints : `Hash(String, Regex)` - A mapping between a route argument and its constraints.
#
# ## Examples
# ## Example
# ```
# @[Athena::Routing::Get(path: "/users/:id")]
# def get_user : Nil
# end
#
# # Or
#
# @[Athena::Routing::Get("/users/:id")]
# def get_user : Nil
# @[ART::Get(path: "/users/:id")]
# def get_user(id : Int32) : Nil
# end
# ```
annotation Get; end
Expand All @@ -23,15 +17,9 @@ module Athena::Routing
# * path : `String` - The path for the endpoint, may also be provided as the first positional argument.
# * constraints : `Hash(String, Regex)` - A mapping between a route argument and its constraints.
#
# ## Examples
# ## Example
# ```
# @[Athena::Routing::Post(path: "/users")]
# def new_user : Nil
# end
#
# # Or
#
# @[Athena::Routing::Post("/users")]
# @[ART::Post(path: "/users")]
# def new_user : Nil
# end
# ```
Expand All @@ -42,16 +30,10 @@ module Athena::Routing
# * path : `String` - The path for the endpoint, may also be provided as the first positional argument.
# * constraints : `Hash(String, Regex)` - A mapping between a route argument and its constraints.
#
# ## Examples
# ## Example
# ```
# @[Athena::Routing::Put(path: "/users/:id")]
# def update_user : Nil
# end
#
# # Or
#
# @[Athena::Routing::Put("/users/:id")]
# def update_user : Nil
# @[ART::Put(path: "/users/:id")]
# def update_user(id : Int32) : Nil
# end
# ```
annotation Put; end
Expand All @@ -61,16 +43,10 @@ module Athena::Routing
# * path : `String` - The path for the endpoint, may also be provided as the first positional argument.
# * constraints : `Hash(String, Regex)` - A mapping between a route argument and its constraints.
#
# ## Examples
# ## Example
# ```
# @[Athena::Routing::Patch(path: "/users/:id")]
# def partial_update_user : Nil
# end
#
# # Or
#
# @[Athena::Routing::Patch("/users/:id")]
# def partial_update_user : Nil
# @[ART::Patch(path: "/users/:id")]
# def partial_update_user(id : Int32) : Nil
# end
# ```
annotation Patch; end
Expand All @@ -80,21 +56,15 @@ module Athena::Routing
# * path : `String` - The path for the endpoint, may also be provided as the first positional argument.
# * constraints : `Hash(String, Regex)` - A mapping between a route argument and its constraints.
#
# ## Examples
# ## Example
# ```
# @[Athena::Routing::Delete(path: "/users/:id")]
# def delete_user : Nil
# end
#
# # Or
#
# @[Athena::Routing::Delete("/users/:id")]
# def delete_user : Nil
# @[ART::Delete(path: "/users/:id")]
# def delete_user(id : Int32) : Nil
# end
# ```
annotation Delete; end

# Applies a `ART::ParamConverterInterface` to a given parameter.
# Applies an `ART::ParamConverterInterface` to a given parameter.
#
# NOTE: The related action argument's type must be compatible with the converter's return type.
#
Expand All @@ -106,13 +76,32 @@ module Athena::Routing
#
# ## Example
# ```
# @[Athena::Routing::ParamConverter(param: "value", converter: FloatConverter)]
# @[ART::ParamConverter(param: "user", converter: DBConverter(User))]
# @[ART::Get(path: "/users/:id")]
# def get_user(user : User) : Nil
# end
# ```
annotation ParamConverter; end

# Defines a `ART::Parameters::QueryParameter` tied to a given route.
#
# # Or
# The type of the query param is derived from the type restriction of the associated controller action argument.
#
# @[Athena::Routing::ParamConverter("user", converter: Exists(User))]
# A non-nilable type denotes it as required. If the parameter is not supplied, and no default value is assigned, an `ART::Exceptions::BadRequest` exception is raised.
# A nilable type denotes it as optional. If the parameter is not supplied (or could not be converted), and no default value is assigned, it is `nil`.
#
# ## Fields
# * name : `String` - The name of the query parameter, may also be provided as the first positional argument.
# * constraints : `Regex` - A pattern the query pram must match to be considered valid.
Blacksmoke16 marked this conversation as resolved.
Show resolved Hide resolved
# * converter : `ART::ParamConverterInterface.class` - The `ART::ParamConverterInterface` that should be used to convert this parameter.
#
# ## Example
# ```
# @[ART::QueryParam(name: "value")]
# @[ART::Get(path: "/example")]
# def get_user(name : String) : Nil
# end
# ```
annotation ParamConverter; end
annotation QueryParam; end

# Apply a *prefix* to all actions within `self`.
Expand Down
7 changes: 6 additions & 1 deletion src/athena.cr
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,16 @@ module Athena::Routing
# within the exception that ultimately could be used in a `ART::Events::Exception` listener.
module Athena::Routing::Exceptions; end

# The `AED::EventListenerInterface` that act upon `ART::Events` to handle a request.
# The `AED::EventListenerInterface` that act upon `ART::Events` to handle a request. Custom listeners can also be defined, see `AED::EventListenerInterface`.
#
# See each listener for more detailed information.
module Athena::Routing::Listeners; end

# Namespace for types related to handling route action parameters.
#
# See `ART::Parameters::Parameter`.
module Athena::Routing::Parameters; end

# Parent type of a route just used for typing.
#
# See `ART::Route`.
Expand Down
4 changes: 3 additions & 1 deletion src/controller.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# The core of any framework is routing; how a route is tied to an action. Athena takes an annotation based approach; an annotation is applied to an instance method of a controller class,
# The core of any framework is routing; how a route is tied to an action. Athena takes an annotation based approach; an annotation, such as `ART::Get` is applied to an instance method of a controller class,
# which will be executed when that endpoint receives a request. The annotation includes the path as well as any constraints that a parameter must meet in order for the route to be invoked.
#
# Additional annotations also exist for setting a query param or a parm converter. See `ART::QueryParam` and `ART::ParamConverter` respectively.
Blacksmoke16 marked this conversation as resolved.
Show resolved Hide resolved
#
# Child controllers must inherit from `ART::Controller` (or an abstract child of it). Each request gets its own instance of the controller to better allow for DI via `Athena::DI`.
#
# The return type of an action does _NOT_ have to be `String`. Currently the response value is serialized by calling `.to_json` on the value.
Expand Down
3 changes: 3 additions & 0 deletions src/listeners/routing_listener.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ struct Athena::Routing::Listeners::Routing
}
end

# Assigns the resolved `ART::Route` and path parameters to the request.
#
# The resolved route is dupped to avoid mutating the master copy in the singleton.
def call(event : ART::Events::Request, dispatcher : AED::EventDispatcherInterface) : Nil
# The route_resolver must be called here for controller DI to work.
# Other option would be to new up a route resolver for every request. :shrug:
Expand Down
2 changes: 1 addition & 1 deletion src/param_converter_interface.cr
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
# num
# end
#
# @[ART::ParamConverter("user", converter: DBConverter(User))]
# @[ART::ParamConverter(param: "user", converter: DBConverter(User))]
# @[ART::Get("user/:id")]
# def get_user(user : User) : User
# user
Expand Down
5 changes: 5 additions & 0 deletions src/parameters/parameter.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
require "./parameter"

module Athena::Routing::Parameters
# Parent type of a parameter just used for typing.
#
# See `ART::Parameters::Parameter`.
module Param
def extract(request : HTTP::Request) : String?; end

Expand All @@ -17,6 +20,7 @@ module Athena::Routing::Parameters
def type; end
end

# Base type of a parameter. Implements logic common to every parameter.
abstract struct Parameter(T)
include Param

Expand All @@ -26,6 +30,7 @@ module Athena::Routing::Parameters
# The value to use if it was not provided
getter default : T?

# The type of the parameter, i.e. what the type restriction in the action is.
getter type : T.class

def initialize(@name : String, @default : T? = nil, @type : T.class = T); end
Expand Down
1 change: 1 addition & 0 deletions src/parameters/path_parameter.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Represents a parameter within an action's path such as `id` in the path `"/user/:id"`
struct Athena::Routing::Parameters::PathParameter(T) < Athena::Routing::Parameters::Parameter(T)
include Athena::Routing::Parameters::Convertable(T)

Expand Down
1 change: 1 addition & 0 deletions src/parameters/query_parameter.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Represents a query parameter defined from an `ART::QueryParam` annotation.
struct Athena::Routing::Parameters::QueryParameter(T) < Athena::Routing::Parameters::Parameter(T)
include Athena::Routing::Parameters::Convertable(T)

Expand Down
2 changes: 1 addition & 1 deletion src/parameters/request_parameter.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# A `HTTP::Request` controller action argument.
# A special parameter that represents a `HTTP::Request` typed controller action argument.
struct Athena::Routing::Parameters::RequestParameter(T) < Athena::Routing::Parameters::Parameter(T)
def extract(request : HTTP::Request) : String?
end
Expand Down
2 changes: 1 addition & 1 deletion src/parameters/response_parameter.cr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# A `HTTP::Request` controller action argument.
# A special parameter that represents a `HTTP::Server::Response` typed controller action argument.
struct Athena::Routing::Parameters::ResponseParameter(T) < Athena::Routing::Parameters::Parameter(T)
def extract(request : HTTP::Request) : String?
end
Expand Down