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

Added CacheControl headers #124

Closed
wants to merge 1 commit into from
Closed

Conversation

nyarly
Copy link
Contributor

@nyarly nyarly commented Oct 16, 2013

Was considering a more flexible API for Response#set_cache_control, but that can wait, seems to me.

@@ -5,6 +5,7 @@
require 'webmachine'
require 'rspec'
require 'logger'
require 'webmachine/adapters/rack'
Copy link

Choose a reason for hiding this comment

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

This seems to be an artifact of local testing, same for the changes to .gitignore and Gemfile. I'm in favor of the latter two, but that should be dealt with in a separate pull request.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Artifact of trying to run a single spec file - spec_helper explicitly uses Webmachine::Adapters::Rack in a before(:suite). Having the require happen accidentally in a spec file seemed like an oversight

Copy link
Member

Choose a reason for hiding this comment

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

Why not run the entire suite? It takes less than 2 seconds for me. 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, for whatever reason in my env, I get a fail. Also, single file takes <.1sec so...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The point remains: spec_helper makes use of constants that are defined in that file - it should therefore require the file itself. As it stands, the whole suite works properly only because of how rspec loads files (which might change?) as opposed to how Ruby loads files.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with nyarly. We use the Rack adapter in spec_helper.rb, we should require it in spec_helper.rb. What confuses me is why we use the Rack adapter in spec_helper.rb.

Copy link

Choose a reason for hiding this comment

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

Ah right I didn't see that when I made the remark about this line. @samwgoldman it should be rather easy to move these adapter references away, think config.let(:options) { ... } and using that in the adapter specs. Different topic/PR though.

Also agree that it's useful being able to run individual spec files. When running the whole suite, the overhead is not just from the rest of the suite, but also from booting Rake (in the case of bundle exec rake). You also get to enjoy RSpec's CLI options. But again, different topic, and should be dealt with in another PR, for the sake of this PR's clarity.

Copy link
Member

Choose a reason for hiding this comment

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

Let's just make it consistent and clean, however that works. I'm of little opinion on the matter. 😄

@ghost
Copy link

ghost commented Oct 17, 2013

Very cool work, can't wait to give it a try on master!

@nyarly
Copy link
Contributor Author

nyarly commented Oct 17, 2013

Any more comment? I think I've got clear through lines on everything.

@ghost
Copy link

ghost commented Oct 17, 2013

That was everything from me 🚢 🌟

directives[:extensions].merge!( name => parsed_value )
end
end
cache_control = self.new(directives)
Copy link

Choose a reason for hiding this comment

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

this method is really big. can it be condensed into smaller responsibilities? (even with private methods)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since it's a class method, I'd lean towards composing into a separate class and have ::parse instantiate that... Seems a little heavyweight unless it's going to get other use parsing headers.

Copy link

Choose a reason for hiding this comment

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

fair...
I guess I see two parts in this method. strings.each and down might be better as its own method.

Copy link

Choose a reason for hiding this comment

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

heavyweight in what way? i'd be fine with a class too, but i think a method is fine also.
it might make it easier to reason about, and possibly even test, because you can test each part individually if you want, which can let you get more specific in some tests. I don't think a class is needed, but that'd be fine too for me. parsing isn't usually something that fits into one method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Heh - I'm used to the god method being a giant switch parser 😄

As I've been working with this today, I'm thinking that the best refactor would be to create per-directive objects, classed based on their values - parse, render, validate all on those. Which thought led to the "is there a HTTP header value parser" question.

@ghost
Copy link

ghost commented Oct 17, 2013

looks good, +1

@seancribbs
Copy link
Member

Could we get some more documentation on this? Install yard if you haven't already and check that the doc % doesn't go down after your patch. I think the command is yard --stats, but I don't recall exactly.

@@ -16,6 +16,10 @@ group :webservers do
gem 'hatetepe', '~> 0.5.2'
end

group :test do
gem "fuubar"
Copy link
Member

Choose a reason for hiding this comment

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

What is this dependency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's my local testing setup. I'll pull that out. Nice rspec formatter though - progress bar except for failing specs.

Copy link

Choose a reason for hiding this comment

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

Indeed, using it as well, it's sweet.

@nyarly
Copy link
Contributor Author

nyarly commented Oct 17, 2013

I've got yard installed - I'll need to add some docs for sure.

Should yard be in the Gemfile, though?

@ghost
Copy link

ghost commented Oct 17, 2013

it's a development/developer dependency, i think it should be.

@nyarly
Copy link
Contributor Author

nyarly commented Oct 17, 2013

As I'm working though this, I'm also wondering: is there not a ready-made HTTP Header parse/generate gem? If so, is it a good idea to be adding a bunch of code here rather than depending on that?

@ghost
Copy link

ghost commented Oct 17, 2013

i haven't used it, don't know how well it will apply to what we're doing here, but i've seen a few projects use https://github.com/tmm1/http_parser.rb

@ghost
Copy link

ghost commented Oct 17, 2013

https://github.com/tmm1/http_parser.rb

Will parse entire HTTP messages, but not the individual header fields, i.e. you'll get a hash like { 'Cache-Control': 'no-store foo bar' }.

@ghost
Copy link

ghost commented Oct 17, 2013

might be useful to use in Webmachine::Header.

@nyarly
Copy link
Contributor Author

nyarly commented Oct 17, 2013

Yeah - I'm thinking the value parsing - maybe there's not that many HTTP headers with complex values? Authorization comes to mind as needing parsing.

@nyarly
Copy link
Contributor Author

nyarly commented Oct 17, 2013

Doc stats at branch point: 87%. This PR definitely hurts that - working on that now.

@nyarly
Copy link
Contributor Author

nyarly commented Oct 17, 2013

Back up to 87%. I believe I've addressed all comments

Ready to merge? Or did I miss something?

#
#@return [true|false]
def valid?
validate!
Copy link

Choose a reason for hiding this comment

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

is there any reason to not implement validate! as a private method and return true/false from there?
seems strange to implement valid? on top of a rescue from a method that could return true/false instead.
is there a use case for the raise(validate!) method?

@ghost
Copy link

ghost commented Oct 18, 2013

@nyarly looks good to me.
+1

directives[:extensions].merge!( name => parsed_value )
end
end
return self.new(directives)
Copy link

Choose a reason for hiding this comment

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

i think this method actually bigger, but i won't hold that against you :D

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think I added to it :)

@ghost
Copy link

ghost commented Oct 20, 2013

should we be worried about the failure on jruby?
@seancribbs @lgierth seems good for merge?

# )
def initialize(hash=nil)
@directives = {}
unless hash.nil?
Copy link

Choose a reason for hiding this comment

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

hash = {}. update_from_hash(hash). it'd be a no-op and remove the special-casing here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Any time you use an object (including {}) as a default value for an argument, the object is instantiated on every call to that method. So using nil here reduces allocation somewhat.

I also find that uniformly using nil as a default value has some benefits in terms passing arguments down the chain - you don't have to remember what the default value is if you want to mirror arguments down.

Copy link

Choose a reason for hiding this comment

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

I don't see that argument as having a strong basis, but happy to subside on this one.

Copy link
Member

Choose a reason for hiding this comment

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

I don't see any reason to guard this, one could simply call update_from_hash(hash || {}).

@nyarly
Copy link
Contributor Author

nyarly commented Oct 21, 2013

Cursory look makes it seem like the jruby and 1.9.3 fails have to do with Adapters - does Reel work in those environments?

@seancribbs
Copy link
Member

Have you updated your bundle or local branch? Those both pass for me.

Sean Cribbs

On Oct 21, 2013, at 8:04 PM, Judson Lester [email protected] wrote:

Cursory look makes it seem like the jruby and 1.9.3 fails have to do with Adapters - does Reel work in those environments?


Reply to this email directly or view it on GitHub.

@nyarly
Copy link
Contributor Author

nyarly commented Oct 21, 2013

Was responding to @robgleeson about Travis. They pass for me as well - I just have the random single fail. But that's on the current master as well.

@nyarly
Copy link
Contributor Author

nyarly commented Oct 23, 2013

Merge? Or something else needs fixing?

@ghost
Copy link

ghost commented Oct 23, 2013

I think we can disregard the build failure as unrelated, and handle it in #126. There are a couple of code comments without spacing left, and it'd be good to squash the commits into one. 👍 from my side then, good work!

@nyarly
Copy link
Contributor Author

nyarly commented Oct 24, 2013

Finished spaces in comments. How strong is the feeling that the commits need to be squashed? (It'll take me a bit since it's a process I don't normally use)

@nyarly
Copy link
Contributor Author

nyarly commented Oct 24, 2013

Nevermind, that was much easier than I thought it'd be.

raise ArgumentError, "Invalid Cache Control directive value: #{name.inspect} can only be 'true' not #{value.inspect}"
end
if not (ALL_DIRECTIVES.has_key?(name) or
[:cache_extension, :extension, :extensions, :cache_extensions].include?(name))
Copy link

Choose a reason for hiding this comment

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

we could cache this array in a constant.
can we wrap the has_key?/include? calls in a descriptive method name?

Copy link

Choose a reason for hiding this comment

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

maybe, directive?(), and extension?().

Copy link
Member

Choose a reason for hiding this comment

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

👍 @robgleeson

@ghost
Copy link

ghost commented Oct 26, 2013

@nyarly I merged your other PR and it brought conflicts into this one. should be easy to fix, but FYI.

@ghost
Copy link

ghost commented Oct 27, 2013

@robgleeson I'd vote for addressing these in a future PR and merging this one with the conflicts resolved, I'm seeing us run down the rabbit-hole.

@ghost
Copy link

ghost commented Oct 27, 2013

@lgierth +1

# Parse the value of a Cache-Control header into a CacheControl object
# @param [String] string The string-format header value
# @param [Array|nil] expected_extension_tokens Any tokens expected in a
# Cache Control header
Copy link
Member

Choose a reason for hiding this comment

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

Indent this line by 2-4 spaces from the comment hash or it will appear on its own in the docs and look strange.

@nyarly
Copy link
Contributor Author

nyarly commented Oct 27, 2013

I have to say, I'm agreeing with @lgierth that this is starting to feel like a rabbit hole. It's clear that there are strong feelings about how it should be integrated with the rest of Webmachine, but some of those motivations are opaque to me. So: this patch is available to merge and improve as-is or not.

If not, and I need the functionality in my client app, I can clone the code there and make it fit that project.

@ghost
Copy link

ghost commented Oct 27, 2013

@nyarly
yeah the turnaround on this PR has been slow. I think a followup PR works well.
I have very strong concerns about set_cache_control. could you address my comments? I think we may have overlooked its implementation.

the concerns may seem opaque but some of the code does not fit well with ruby idioms, it's not a big deal, it can be addressed later.

@ghost
Copy link

ghost commented Oct 27, 2013

the implementation of set_cache_control does not validate a string (at all). this is not opaque.

@nyarly
Copy link
Contributor Author

nyarly commented Oct 27, 2013

I can see fixing up the issue on set_cache_control. Something like:

coerce all values to a CacheControl
either raise an error, or return vacantly if the cc is empty (the latter on the grounds that CacheControl: is equivalent to not adding the CC header)
otherwise, set the value of the header to cc.to_s

Regarding the to_i in to_s: validating the well-formedness of every directive value is definitely past the limit of the amount of work I had in mind for this PR.

@ghost
Copy link

ghost commented Oct 27, 2013

@nyarly
thats fine, lets address set_cache_control & iterate from there in other PRs.
I can help address some of the comments in this PR.

This PR adds a CacheControl object that aids in the manipulation of
HTTP CacheControl headers, and a method Response#set_cache_control to
make it easy to use.

CacheControl::parse takes a list of acceptable "tokens" which will be
parsed as symbols if they were provided as unquoted tokens.
@ghost ghost mentioned this pull request Dec 3, 2013
@nyarly
Copy link
Contributor Author

nyarly commented Jan 8, 2014

So, can I merge this now?

@ghost
Copy link

ghost commented Jan 8, 2014

if github doesn't go down…
it's been a long time since I went through this code, I think there's still some outstanding issues. I'm not sure if #124 (comment) has happened or not.

@bethesque
Copy link
Contributor

Year old PR, can we close this if nobody wants it/is working on it? (We can always reopen it if somebody wants it.)

@Asmod4n
Copy link
Member

Asmod4n commented Apr 24, 2016

Sorry @nyarly for the long wait here. I know how this must feel, but i am closing it now.
Could you extract your ideas into a separate gem?

What webmachine currently lacks is a stable way to extend it i believe.
Rack is built around the idea to the extensible but i for one completely dislike it how its implemented.
I believe we can find a better way here in webmachine.

@seancribbs @bethesque @lgierth @samwgoldman

@Asmod4n Asmod4n closed this Apr 24, 2016
@nyarly
Copy link
Contributor Author

nyarly commented Apr 24, 2016

I appreciate the sympathy. It's been some time since I used ruby-webmachine, to be honest - I remember I was a little irritated at the time... If I come back to webmachine (in any incarnation) I'll look into it again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants