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

Add pipelines command #550

Merged
merged 6 commits into from
Feb 1, 2018

Conversation

NeverHappened
Copy link
Contributor

@NeverHappened NeverHappened commented Jan 20, 2018

Description of the Change

Added 'pipelines' command. It shows the pipelines and the plugs for them

Also have --no-plugs option if you want to just see the pipelines

fixes #550

Copy link
Contributor

@eliasjpr eliasjpr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the awesome PR and the work done. I have tested this locally and it looks great.
🎉 🎆
screen shot 2018-01-20 at 11 13 08 am

I have left some suggestions to improve the PR

parse_routes
print_pipelines
rescue
puts "Error: Not valid project root directory.".colorize(:red)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use CLI.logger.error instead of puts

help
end

LABELS = ["Pipe", "Plug"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use %w(Pipe Plug) instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I did it, just forgot to push last updates)

module Amber::CLI
class MainCommand < ::Cli::Supercommand
class Pipelines < Command
# TODO: use hash with fixed order?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to print the pipes in the same order they are used and maybe somehow reflect that in the output

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, they should display in order - maybe that is already happening. The test should reflect that.

end

private def parse_routes
lines = File.read_lines("config/routes.cr")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make "config/routes.cr" a constant

Copy link
Contributor

@marksiemers marksiemers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work. See inline comments.

If you figure out how to test the order of the plugs, that would be great.

If not, let me know and I can spend some time on it.

set_plug(line)
end
end
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For parse_routes, I like this style a little better @eliasjpr - WDYT?

private def parse_routes
  lines = File.read_lines("config/routes.cr")
  lines.map(&.strip).each do |line|
    case line
    when .starts_with?("pipeline") then set_pipe(line)
    when .starts_with?("plug")     then set_plug(line)
    end
  end
end

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no preference honestly

end

private def set_pipe(line)
match = line.match(PIPE_REGEX)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on this comment: # TODO: raise if no match?
What about: raise [some error] unless match = line.match(PIPE_REGEX)
On the first line of the method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment below with a complete proposed solution.

end

private def set_plug(line)
match = line.match(PLUG_REGEX)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here:
Based on this comment: # TODO: raise if no match?
What about: raise [some error] unless match = line.match(PLUG_REGEX)
On the first line of the method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment below with a complete proposed solution.

result[@current_pipe] << plug
end
end
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, maybe something like this would work:

      private def set_plug(line)
        if (match = line.match(PLUG_REGEX)) && (plug = match[2]) && @current_pipe
          result[@current_pipe] << plug
        else
          raise "Could not parse pipeline/plugs in 'config/routes.cr'"
        end
      end

If we had the line number that would be super helpful

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll try to put more work on error handling.

module Amber::CLI
class MainCommand < ::Cli::Supercommand
class Pipelines < Command
# TODO: use hash with fixed order?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, they should display in order - maybe that is already happening. The test should reflect that.

)

plugs.each do |plug|
output.should contain plug
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't immediately think how to do this, but there should be a test to make sure the plugs are in the correct order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I though of that. Just that didn't come up to a solution right away. I'll think about it

@eliasjpr
Copy link
Contributor

@marksiemers I thought we agree not raising errors with the CLI but printing an error with a precise msg msg

@marksiemers
Copy link
Contributor

@eliasjpr - I either forgot that or wasn't there for the convo.

Yeah, not raising an error is fine with me, but I think then reporting a message and existing would go where raise ... is now. WDYT?

@eliasjpr
Copy link
Contributor

Agree the cli has an exit method or simple log.error will suffice

@NeverHappened
Copy link
Contributor Author

Fixed the stuff you requested. I did the test for ensuring the order. The only problem is that test will fail if you change the default routes. If there is a better way to test please tell me.

eliasjpr
eliasjpr previously approved these changes Jan 21, 2018
drujensen
drujensen previously approved these changes Jan 22, 2018
Copy link
Member

@drujensen drujensen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link
Member

@robacarp robacarp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a nice command. I would really love to see extended regex with some comments. <3

LABELS_WITHOUT_PLUGS = %w(Pipe)

PIPE_REGEX = /(pipeline)\s+\:(\w+)(?:,\s+\"([^\"]+)\")?/
PLUG_REGEX = /(plug)\s+([\w:]+)?/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've talked elsewhere about using extended regular expressions to add comments, clarity, and maintainability to our regular expressions. Can we do that here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robacarp - I remember seeing your example in a previous PR, but I've never written an extended regex. Is this the idea?

 /
   (plug)    # match and capture the word plug
   \s+       # followed by at least one whitespace character
   ([\w:]+)? # greedy optional match and capture one or more word characters or colons ':'
 /x

@NeverHappened - Does this description match what the regex is doing?

This was a fun little exercise, assuming the above is accurate, I would suggest a few changes:

 /
   plug\s+  # match 'plug` and at least one whitespace character
   ([\w:]+) # match and capture one or more word characters or colons ':'
 /x

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I messed around a little more, WDYT of this:

PLUG_REGEX = 
  /^         # start at beginning of line
    plug\s+  # match 'plug` and at least one whitespace character
    ([\w:]+) # match and capture one or more word characters or colons ':'
   $         # matches must come before end of line
  /x

See https://carc.in/#/r/3fhw

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marksiemers that's the right idea! The only functional difference between regular regex and this is that whitespace is basically ignored outside of character classes, and that comments are allowed. I believe it adds a major maintainability component. :]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, and it makes you think about what you are doing.

I used to teach at a bootcamp and we always told people - you'll never be able to read a regular expression. When you come back to it in 6 months, it will take 20 minutes to figure out - like starting from scratch. If only we had known about this!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marksiemers and @robacarp we can probably add a commit with these changes, this way we can have it merge sooner

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pipeline one I'm giving up on for now, but you can see a WIP here: https://carc.in/#/r/3fiv

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this works for the plug, I would say swap this in with this PR, as an example and a reminder:

PLUG_REGEX = 
  /^         # start at beginning of line
    plug\s+  # match 'plug` and at least one whitespace character
    ([\w:]+) # match and capture one or more word characters or colons ':'
  $/x        # matches must come before end of line

end

it "maintains the correct order of pipelines and plugs" do
in_correct_order = %w(Plug) + %w(web) + web_default_plugs + %w(static) + static_default_plugs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally I'm all in for %w syntax but this feels weird

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a comment previously that caused this. @NeverHappened my apologies I was not explicit on my comments.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this, the order of the pipelines should not matter, just the order of the plugs within a pipeline. @eliasjpr can you confirm that?

If that is the case, I think we should consider a setup like this for the test:

pipelines = {
  "web" => web_default_plugs,
  "static" => static_default_plugs,
}

It will take me a little more time to figure out the iteration, but this leads to another question for everyone - @amberframework/core-team :

Should the output of amber pipelines be a one-level hierarchy (as is in the screenshot on this PR) or should it be tabular, where the name of the pipeline is repeated for each plug?

The existing output is easier to read and parse as a human.

The second way would be easier to test, and it would be more consistent with amber routes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer the second way. This kind of data isn't well represented in a table.

@marksiemers
Copy link
Contributor

marksiemers commented Jan 23, 2018

@NeverHappened - Before merging this I'd like the see the following:

  1. (Required) List the pipeline name on each row with each plug (see table below)

  2. (Required) Modify tests to cover format shown in table below (from requirement No. 1)

  3. (Strongly encouraged) Refactor regex for plugs using extended regex (see comments above)

  4. (Optional) Refactor parse_routes per suggestion

Sample table:

Pipeline Plug
web Amber::Pipe::Error
web Amber::Pipe::Logger
... ...
static Amber::Pipe::Error
static Amber::Pipe::Static

@eliasjpr @drujensen @robacarp - Agreed?

@drujensen
Copy link
Member

@marksiemers I agree with your requirement list. I think the table output makes it easy to grep as well amber pipelines | grep web

@NeverHappened are you ok with the requested changes?

@NeverHappened
Copy link
Contributor Author

Yeah, of course! Thanks for the help :) I'll do the changes as soon as I can.

My only concern is the regular expressions are not 100% robust.
Isn't there a way to wait for the amber to load to fetch real pipes and plugs?

@drujensen
Copy link
Member

@NeverHappened I believe at one point we implemented the routes that way, but performance wise, we found the RegEx's faster and just as accurate.

@marksiemers
Copy link
Contributor

Yeah, I believe loading them from the actual application may take seconds and longer as the app grows bigger. The regex approach is much much faster.

It does seem a little strange to have a second system for reporting the routes, but so far it has worked well.

@eliasjpr
Copy link
Contributor

My only concern with the regex is that people can register routes outside of the routes file (This actually has been done) and those routes will not be reported by the command.

@NeverHappened
Copy link
Contributor Author

Yeah, and it's not that uncommon. For example, for projects in rails I often split admin and user routes in separate files.

@robacarp
Copy link
Member

This would be a bit bigger of a lift, but Crystal files can compile pretty fast if there isn't a big project. It might be possible to make a tiny compilation environment which requires in the routes file and actually evaluates it, and spit out the output.

As for multiple routes files, I've done that in many many Rails projects and I've always been surprised that Rails didn't provide any sort of helper function for drawing more routes from a different file into the router. Should that be on the roadmap for Amber?

@eliasjpr
Copy link
Contributor

You can define routes in a different file other than routes if you follow the same Amber::Server.configure do |app| and making sure is require in config/application.cr

@marksiemers
Copy link
Contributor

marksiemers commented Jan 25, 2018

I agree that amber routes should be handled in a different way. The regex is a bit of a hack.

I also think that is outside the scope of this issue: Add pipelines command

A separate issue should be opened for having the CLI evaluate the actual routes, rather than the code.

In other words, we should finish this hack in a way that is currently consistent with amber routes and they can both be addressed in separate issues.

@eliasjpr
Copy link
Contributor

Can we get the final touches to this PR so we can merge it. We would like to release soon

@NeverHappened
Copy link
Contributor Author

Yes, I'll finish it soon

@NeverHappened NeverHappened dismissed stale reviews from drujensen and eliasjpr via 5db93a7 January 26, 2018 18:12
@NeverHappened NeverHappened changed the title Add pipelines command WIP Add pipelines command Jan 26, 2018
@NeverHappened NeverHappened changed the title WIP Add pipelines command Add pipelines command Jan 27, 2018
@NeverHappened
Copy link
Contributor Author

Done with what you were asking for!

Note:

  • I didn't sort the pipes in the output. So the order of plugs IS correct, but pipes/plugs can be mixed dependent on the routes.cr file. (Can sort by pipes, if that's critical)
  • There is no way to capture contiguous patterns like "api_v1", "api_v2", "api_v2", etc.. That's why It was necessary to capture entire group and split it later

Have some questions, I would be happy if you can answer.

  1. why crystal spec ./path/to/pipeline_spec.cr shows a lot more specs than it's really in the file?
  2. If the spec fails because of a bad regex, it just didn't execute. I understand that it's not that good, but didn't see why this happens.

marksiemers
marksiemers previously approved these changes Jan 31, 2018
plug # match 'plug'
\s+ # require at least one whitespace character after 'plug'
(
[\w:]+ # match at least one words with maybe a colon
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NeverHappened Thank you for doing this, it increases maintainability so much!

drujensen
drujensen previously approved these changes Jan 31, 2018
Copy link
Contributor

@faustinoaq faustinoaq left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this 👍 I will approve. Please see my comments

)?
/x

FAILED_TO_PARSE_ERROR = "Could not parse pipeline/plugs in #{ROUTES_PATH}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NeverHappened Looks like syntax highlighting is broken here, I tried to remove quotes (') in comments and syntax highlighting worked again:

With quotes (') in comments

screenshot_20180131_112412

Without quotes (') in comments:

screenshot_20180131_112500

@NeverHappened
Copy link
Contributor Author

@faustinoaq Fixed

@robacarp
Copy link
Member

@faustinoaq that's probably an issue with the syntax highlighter rules in your editor

@faustinoaq
Copy link
Contributor

faustinoaq commented Jan 31, 2018

that's probably an issue with the syntax highlighter rules in your editor

@robacarp Not just my editor, but github diff view itself 👇

screenshot_20180131_154305

Look at FAILED_TO_PARSE_ERROR ^

@drujensen drujensen merged commit 7e96132 into amberframework:master Feb 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants