grape_sinatra_helpers 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in grape_sinatra_helpers.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Derek Lindahl
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Grape Sinatra Helpers
2
+
3
+ This is a small subset of helper methods available in `Sinatra::Base` that I
4
+ have ported over to [Grape]
5
+
6
+ * `cache_control`
7
+ * `expires`
8
+ * `last_modified`
9
+ * `etag`
10
+
11
+ And a few others that the above directly depend on.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'grape_sinatra_helpers'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install grape_sinatra_helpers
26
+
27
+ ## Usage
28
+
29
+ Use these methods as you normally would with the Sinatra equivalent.
30
+
31
+ ## TODO
32
+
33
+ * Port over Sinatra test cases
34
+
35
+ ## Contributing
36
+
37
+ 1. Fork it
38
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
39
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
40
+ 4. Push to the branch (`git push origin my-new-feature`)
41
+ 5. Create new Pull Request
42
+
43
+ [grape]: https://github.com/intridea/grape
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/grape_sinatra_helpers/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Derek Lindahl"]
6
+ gem.email = ["dlindahl@customink.com"]
7
+ gem.description = %q{Small subset of Sinatra helper methods ported to Grape}
8
+ gem.summary = %q{A small subset of Sinatra::Base helper methods that have been ported over to Grape}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "grape_sinatra_helpers"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = GrapeSinatraHelpers::VERSION
17
+ end
@@ -0,0 +1,3 @@
1
+ module GrapeSinatraHelpers
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,158 @@
1
+ require "grape_sinatra_helpers/version"
2
+
3
+ module GrapeSinatraHelpers
4
+
5
+ def self.included( klass )
6
+ klass.send(:helpers) do
7
+
8
+ # Specify response freshness policy for HTTP caches (Cache-Control header).
9
+ # Any number of non-value directives (:public, :private, :no_cache,
10
+ # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
11
+ # a Hash of value directives (:max_age, :min_stale, :s_max_age).
12
+ #
13
+ # cache_control :public, :must_revalidate, :max_age => 60
14
+ # => Cache-Control: public, must-revalidate, max-age=60
15
+ #
16
+ # See RFC 2616 / 14.9 for more on standard cache control directives:
17
+ # http://tools.ietf.org/html/rfc2616#section-14.9.1
18
+ def cache_control(*values)
19
+ if values.last.kind_of?(Hash)
20
+ hash = values.pop
21
+ hash.reject! { |k,v| v == false }
22
+ hash.reject! { |k,v| values << k if v == true }
23
+ else
24
+ hash = {}
25
+ end
26
+
27
+ values.map! { |value| value.to_s.tr('_','-') }
28
+ hash.each do |key, value|
29
+ key = key.to_s.tr('_', '-')
30
+ value = value.to_i if key == "max-age"
31
+ values << [key, value].join('=')
32
+ end
33
+
34
+ header 'Cache-Control', values.join(', ') if values.any?
35
+ end
36
+
37
+ # Set the Expires header and Cache-Control/max-age directive. Amount
38
+ # can be an integer number of seconds in the future or a Time object
39
+ # indicating when the response should be considered "stale". The remaining
40
+ # "values" arguments are passed to the #cache_control helper:
41
+ #
42
+ # expires 500, :public, :must_revalidate
43
+ # => Cache-Control: public, must-revalidate, max-age=60
44
+ # => Expires: Mon, 08 Jun 2009 08:50:17 GMT
45
+ #
46
+ def expires(amount, *values)
47
+ values << {} unless values.last.kind_of?(Hash)
48
+
49
+ if amount.is_a? Integer
50
+ time = Time.now + amount.to_i
51
+ max_age = amount
52
+ else
53
+ time = time_for amount
54
+ max_age = time - Time.now
55
+ end
56
+
57
+ values.last.merge!(:max_age => max_age)
58
+ cache_control(*values)
59
+
60
+ header 'Expires', time.httpdate
61
+ end
62
+
63
+ # Set the last modified time of the resource (HTTP 'Last-Modified' header)
64
+ # and halt if conditional GET matches. The +time+ argument is a Time,
65
+ # DateTime, or other object that responds to +to_time+.
66
+ #
67
+ # When the current request includes an 'If-Modified-Since' header that is
68
+ # equal or later than the time specified, execution is immediately halted
69
+ # with a '304 Not Modified' response.
70
+ def last_modified(time)
71
+ return unless time
72
+ time = time_for time
73
+ header 'Last-Modified', time.httpdate
74
+ return if request.env['HTTP_IF_NONE_MATCH']
75
+
76
+ if request.env['HTTP_IF_MODIFIED_SINCE']
77
+ # compare based on seconds since epoch
78
+ since = Time.httpdate(request.env['HTTP_IF_MODIFIED_SINCE']).to_i
79
+ error!('304 Not Modified', 304) if since >= time.to_i
80
+ end
81
+
82
+ if request.env['HTTP_IF_UNMODIFIED_SINCE']
83
+ # compare based on seconds since epoch
84
+ since = Time.httpdate(request.env['HTTP_IF_UNMODIFIED_SINCE']).to_i
85
+ error!('412 Precondition Failed', 412) if since < time.to_i
86
+ end
87
+ rescue ArgumentError
88
+ end
89
+
90
+ # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
91
+ # GET matches. The +value+ argument is an identifier that uniquely
92
+ # identifies the current version of the resource. The +kind+ argument
93
+ # indicates whether the etag should be used as a :strong (default) or :weak
94
+ # cache validator.
95
+ #
96
+ # When the current request includes an 'If-None-Match' header with a
97
+ # matching etag, execution is immediately halted. If the request method is
98
+ # GET or HEAD, a '304 Not Modified' response is sent.
99
+ def etag(value, options = {})
100
+ # Before touching this code, please double check RFC 2616 14.24 and 14.26.
101
+ options = {:kind => options} unless Hash === options
102
+ kind = options[:kind] || :strong
103
+ new_resource = options.fetch(:new_resource) { request.post? }
104
+
105
+ unless [:strong, :weak].include?(kind)
106
+ raise ArgumentError, ":strong or :weak expected"
107
+ end
108
+
109
+ # value = '"%s"' % value
110
+ value = "%s" % value
111
+ value = 'W/' + value if kind == :weak
112
+ header 'ETag', value
113
+
114
+ if etag_matches? request.env['HTTP_IF_NONE_MATCH'], new_resource
115
+ request.safe? ? error!('304 Not Modified', 304) : error!('412 Precondition Failed', 412)
116
+ end
117
+
118
+ if request.env['HTTP_IF_MATCH']
119
+ error!('412 Precondition Failed', 412) unless etag_matches? request.env['HTTP_IF_MATCH'], new_resource
120
+ end
121
+ end
122
+
123
+ # Helper method checking if a ETag value list includes the current ETag.
124
+ def etag_matches?(list, new_resource = request.post?)
125
+ return !new_resource if list == '*'
126
+ list.to_s.split(/\s*,\s*/).include? header['ETag']
127
+ end
128
+
129
+ # Generates a Time object from the given value.
130
+ # Used by #expires and #last_modified.
131
+ def time_for(value)
132
+ if value.respond_to? :to_time
133
+ value.to_time
134
+ elsif value.is_a? Time
135
+ value
136
+ elsif value.respond_to? :new_offset
137
+ # DateTime#to_time does the same on 1.9
138
+ d = value.new_offset 0
139
+ t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
140
+ t.getlocal
141
+ elsif value.respond_to? :mday
142
+ # Date#to_time does the same on 1.9
143
+ Time.local(value.year, value.mon, value.mday)
144
+ elsif value.is_a? Numeric
145
+ Time.at value
146
+ else
147
+ Time.parse value.to_s
148
+ end
149
+ rescue ArgumentError => boom
150
+ raise boom
151
+ rescue Exception
152
+ raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
153
+ end
154
+
155
+ end
156
+ end
157
+
158
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grape_sinatra_helpers
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Derek Lindahl
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-17 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Small subset of Sinatra helper methods ported to Grape
15
+ email:
16
+ - dlindahl@customink.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - LICENSE
24
+ - README.md
25
+ - Rakefile
26
+ - grape_sinatra_helpers.gemspec
27
+ - lib/grape_sinatra_helpers.rb
28
+ - lib/grape_sinatra_helpers/version.rb
29
+ homepage: ''
30
+ licenses: []
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 1.8.10
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: A small subset of Sinatra::Base helper methods that have been ported over
53
+ to Grape
54
+ test_files: []