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
Allow multiple pipelines to be added with one block #359
Allow multiple pipelines to be added with one block #359
Conversation
@amberframework/core-team |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm good with this. Can be merge
spec/amber/dsl/server_spec.cr
Outdated
it "have added pipes in the pipeline" do | ||
plugs = server.settings.handler.pipeline[:custom] | ||
plugs.first.should be_a Pipe::Logger | ||
plugs[1].should be_a Pipe::Error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dismissing to request another spec use case
Was looking at the specs in more detail and noticed that it was missing the use case that prompted this changed in the specs. What do you think if we add a spec for the following case? ‘’’crystal pipeline :web do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like what @eliasjpr suggested.
pipeline :web do
plug Amber::Pipe::Error.new
...
plug Amber::Pipe::CSRF.new
end
pipeline :auth do
use :web
plug Authenticate.new
end
as it allows for one pipeline to inherit from another.
I'm not quite sure what the use case of defining two pipelines with the exact same handlers in them would be? Could you enlighten me on that? @marksiemers
@elorest pipeline :web, :auth do
plug Amber::Pipe::Error.new
...
plug Amber::Pipe::CSRF.new
end
pipeline :auth do
plug Authenticate.new
end and this: pipeline :web do
plug Amber::Pipe::Error.new
...
plug Amber::Pipe::CSRF.new
end
pipeline :auth do
use :web
plug Authenticate.new
end Are functionally the same. The auth pipeline has all the same pipes as web - plus Authenticate.new It may not be as memory efficient, as I think the approach in this PR will double the instances of the shared pipes. Our small sample size of 2 people on gitter seemed to prefer the syntax of this approach (allowing two pipes setup at the same time). |
I just seems like a really unorthodox inheritance pattern to me. Typically you create A and then inherit from it when creating B. I've never seen the pattern of creating A and B then adding to B. Similar to this in Ruby, Crystal class A, B
def hello
puts "hello world"
end
end
class B
def hello_name(name)
puts "hello #{name}."
end
end |
I wasn't thinking of it as inheritance - there is no class being defined or instantiated for |
Hmm, I uncovered something, not sure if it is a bug or desired behavior, put if you call Would we rather have it overwrite or append? This PR will not work if we stick with overwriting (not for the use case specified). |
src/amber/router/pipe/pipeline.cr
Outdated
@@ -29,7 +29,7 @@ module Amber | |||
# Connects pipes to a pipeline to process requests | |||
def build(valve : Symbol, &block) | |||
@valve = valve | |||
@pipeline[valve] = [] of HTTP::Handler unless pipeline.key? valve | |||
@pipeline[valve] = [] of HTTP::Handler unless pipeline.has_key? valve |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes what I think is a bug. Hash#key?
expects a value as an argument, so this has been re-initializing the @pipeline[valve]
every time. Essentially over-writing any existing pipes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I incorporated the same change in another branch! Nice Catch
We didn't assume anyone would have a reason to define the same pipeline
twice, so this was intentional. I still can't think of why we should expect a pipeline to be defined twice.
|
@marksiemers to me it seems like a similar thing to inheriting blocks of yaml into new yaml blocks. Whether we think of it as inheritance or merging of hashes/arrays it still seems strange to create 2 at once and then add to one later instead of merging an existing one into a new one. Is this standard somewhere else? I don't recall phoenix allowing this for pipelines but I might not have dug in far enough. To me @eliasjpr method of using WDYT? |
The way I visualized the -->error -->looger-->use :web (runs web pipeliine )-->other handler This way pipeline :api do
use :common
plug SomePlug
use :web
plug Auth
end WDYGT? |
@Eliasjp I really like that.
…On Nov 5, 2017 7:17 PM, "Elias Perez" ***@***.***> wrote:
The way I visualized the use keyword to be implement is some sort of
flag/check where is define in the pipeline. So for say that the pipeline
linked list finds a use link, it diverts to run the pipeline the
specified by use and then continue the original link list avoiding to
define the pipeline alltogether
-->error -->looger-->use :web (runs web pipeliine )-->other handler
This way use can be define in any order
pipeline :api do
use :common
plug SomePlug
use :web
plug Authend
WDYGT?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#359 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAHmpF_v3JaXcSNjmBxdkxaQUl7hSODtks5szmw_gaJpZM4QSPnK>
.
|
A few things in this comment:
Full ExampleA user was creating a "reddit clone" so for the PostController, the index and show actions were to be public, all others required a user to be signed in (authenticated). With the current version of crystal, 5 plugs had to be duplicated for Amber::Server.configure do |app|
pipeline :web do
plug Amber::Pipe::Error.new
plug Amber::Pipe::Logger.new
plug Amber::Pipe::Session.new
plug Amber::Pipe::Flash.new
plug Amber::Pipe::CSRF.new
end
pipeline :auth do
plug Amber::Pipe::Error.new
plug Amber::Pipe::Logger.new
plug Amber::Pipe::Session.new
plug Amber::Pipe::Flash.new
plug Amber::Pipe::CSRF.new
plug Authenticate.new
end
# All static content will run these transformations
pipeline :static do
plug Amber::Pipe::Error.new
plug HTTP::StaticFileHandler.new("./public")
plug HTTP::CompressHandler.new
end
routes :static do
# Each route is defined as follow
# verb resource : String, controller : Symbol, action : Symbol
get "/*", Amber::Controller::Static, :index
end
routes :web do
resources "/posts", PostController, only: [:index, :show]
get "/signin", SessionController, :new
post "/session", SessionController, :create
get "/signout", SessionController, :delete
get "/signup", RegistrationController, :new
post "/registration", RegistrationController, :create
get "/", PostController, :index
end
routes :auth do
resources "/posts", PostController, except: [:index, :show]
end
end This seemed like a clean syntax for DRY'ing up the code, and one that would be approachable ...
pipeline :web, :auth do
plug Amber::Pipe::Error.new
plug Amber::Pipe::Logger.new
plug Amber::Pipe::Session.new
plug Amber::Pipe::Flash.new
plug Amber::Pipe::CSRF.new
end
pipeline :auth do
plug Authenticate.new
end
... The place I've seen this style of syntax before is with ActiveRecord validations - if you have the same validation for more than one field, you can "share" that validation: validates :name, :email, presence: true
validates :email, uniqueness: true See: http://guides.rubyonrails.org/active_record_validations.html#presence ActiveRecord also has the pattern with a block for This approach vs. a
|
I am wondering if there is a way to keep 1 copy of the handlers, and use something like union at the time of processing. I am happy with this first implementation since it is simple and achieves the desired expectation, but would like to open a ticket to mark it as something that we can maybe improve in the future. common_handlers[:web,:api] = [ ...array of common handlers... ]
pipelines[:api] = [ Authenticator.new ]
common_handlers[:api] | pipelines[:api] https://crystal-lang.org/api/0.23.1/Array.html#%7C%28other%3AArray%28U%29%29forallU-instance-method |
@elorest @eliasjpr (pipelines.values.flatten.find{|p| p.class == pipe.class } || pipe) See full example here: https://play.crystal-lang.org/#/r/31pq In the case of this PR though, it would probably be better to take care of it in the macro, which now that I think about it, should be possible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I think this syntax is a bit weird, and would rather wait until we actually have more that 5 pipes that end up duplicated across pipelines before trying to find a solution here. Active Record validations don't seem like a parallel to this to me, but I can see the similarities.
I really liked @eliasjpr use
syntax, but if he's fine with this I'll approve it too.
If I stare at it for a long time it sort of grows on me.
👍
I agree it is sub-optimal. For now, I think the pros (DRY route file, decent tests added for pipelines) outweigh the cons (questionable design pattern, duplicate instances of pipes when used). |
- Using Amber::Server.new was breaking and Amber::Server.configure will be deprecated in favor of settings - Also added some nil checks to get failures in test suite instead of errors
@amberframework/core-team - Some changes based on the commits from master that were merged in. Please re-approve. |
👍 Feel free to merge this |
* Allow multiple pipelines to be added with one block * Add tests to DSL::Server pipeline * Fix bug where `key?` was return `nil` - creating a false negative * Refactor some Amber::DSL::Server tests * Switch server_spec to use `instance` and `settings` - Using Amber::Server.new was breaking and Amber::Server.configure will be deprecated in favor of settings - Also added some nil checks to get failures in test suite instead of errors
* Allow multiple pipelines to be added with one block * Add tests to DSL::Server pipeline * Fix bug where `key?` was return `nil` - creating a false negative * Refactor some Amber::DSL::Server tests * Switch server_spec to use `instance` and `settings` - Using Amber::Server.new was breaking and Amber::Server.configure will be deprecated in favor of settings - Also added some nil checks to get failures in test suite instead of errors Former-commit-id: aeef499
Description of the Change
This allows doing the following in a routes file:
Alternate Designs
@eliasjpr suggested allowing this syntax:
The two approaches are not mutually exclusive - implementing this PR doesn't prevent implementing the
use
or another keyword approach.Comparing the two - this PR provides a clean way to setup two pipelines at the same time.
The
use
approach allows you to create an additional pipeline "around" and existing one. If that is needed in the future, the alternate design will also need to be implemented.Benefits
Keeps route code dry when different pipelines share a lot of the same pipes.
Possible Drawbacks
None that I'm aware of.