rocket_pants 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rocket_pants.rb CHANGED
@@ -32,17 +32,21 @@ module RocketPants
32
32
  autoload :JSONP, 'rocket_pants/controller/jsonp'
33
33
  autoload :Rescuable, 'rocket_pants/controller/rescuable'
34
34
  autoload :Respondable, 'rocket_pants/controller/respondable'
35
+ autoload :HeaderMetadata, 'rocket_pants/controller/header_metadata'
36
+ autoload :Linking, 'rocket_pants/controller/linking'
35
37
  autoload :Versioning, 'rocket_pants/controller/versioning'
36
38
  autoload :FormatVerification, 'rocket_pants/controller/format_verification'
37
39
  autoload :UrlFor, 'rocket_pants/controller/url_for'
38
40
 
39
- mattr_accessor :caching_enabled
41
+ mattr_accessor :caching_enabled, :header_metadata
40
42
  self.caching_enabled = false
43
+ self.header_metadata = false
41
44
 
42
45
  mattr_writer :cache
43
46
 
44
47
  class << self
45
48
  alias caching_enabled? caching_enabled
49
+ alias header_metadata? header_metadata
46
50
 
47
51
  def cache
48
52
  @@cache ||= Moneta::Memory.new
@@ -18,6 +18,8 @@ module RocketPants
18
18
  ActionController::HttpAuthentication::Token::ControllerMethods,
19
19
  UrlFor,
20
20
  Respondable,
21
+ HeaderMetadata,
22
+ Linking,
21
23
  Versioning,
22
24
  Instrumentation,
23
25
  Caching,
@@ -2,6 +2,7 @@ module RocketPants
2
2
  class CacheMiddleware
3
3
 
4
4
  NOT_MODIFIED = [304, {}, []]
5
+ DIVIDER = ":"
5
6
 
6
7
  def initialize(app)
7
8
  @app = app
@@ -29,7 +30,6 @@ module RocketPants
29
30
 
30
31
  def has_valid_etag?
31
32
  return false if (etags = request_etags).blank?
32
- debug ""
33
33
  etags.any? do |etag|
34
34
  cache_key, value = extract_cache_key_and_value etag
35
35
  debug "Processing cache key for path #{request_path}"
@@ -39,7 +39,7 @@ module RocketPants
39
39
  end
40
40
 
41
41
  def extract_cache_key_and_value(etag)
42
- etag.to_s.split(":", 2)
42
+ etag.to_s.split(DIVIDER, 2)
43
43
  end
44
44
 
45
45
  def fresh?(key, value)
@@ -48,7 +48,7 @@ module RocketPants
48
48
 
49
49
  def request_etags
50
50
  stored = @env['HTTP_IF_NONE_MATCH']
51
- stored.present? && stored.to_s.scan(/"([^"]+)"/)
51
+ stored.present? && stored.to_s.scan(/"([^"]+)"/).flatten.select { |i| i.include?(DIVIDER) }
52
52
  end
53
53
 
54
54
  def debug(message)
@@ -0,0 +1,38 @@
1
+ module RocketPants
2
+ module HeaderMetadata
3
+
4
+ # Given a hash of request metadata, will:
5
+ # 1. Write out the headers for the current metadata
6
+ # 2. Return the hash, suitable for merging into the response hash
7
+ # 3. Start a dance party.
8
+ #
9
+ # @param [Hash{Symbol => Object}] metadata the hash of the request metadata.
10
+ # @return [Hash{Symbol => Object}] the passed in metadata
11
+ def expose_metadata(metadata)
12
+ metadata_headers { build_header_hash(metadata) }
13
+ super # Call any other versions of the method.
14
+ end
15
+
16
+
17
+ # Given a block which returns a Hash, will call and merge the block iff header metadata
18
+ # is enabled. This is to avoid the overhead of generating headers on every request when
19
+ # it's disabled.
20
+ def metadata_headers(&blk)
21
+ headers.merge! blk.call if RocketPants.header_metadata?
22
+ end
23
+
24
+ def build_header_hash(options, hash = {}, prefix = 'X-Api')
25
+ options.each_pair do |k, v|
26
+ current = "#{prefix}-#{k.to_s.titleize.tr(" ", "-")}"
27
+ if v.is_a?(Hash)
28
+ build_header_hash v, hash, current
29
+ else
30
+ value = Array(v).join(", ")
31
+ hash[current] = value if value.present?
32
+ end
33
+ end
34
+ hash
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ module RocketPants
2
+ module Linking
3
+
4
+ # Generates a Link: header with the specified rel, uri and attributes.
5
+ # @param [String, Symbol] rel the relation of the given link
6
+ # @param [String] uri the full uri to the specified link resource
7
+ # @param [Hash] attributes any other attributes for the link
8
+ def link(rel, uri, attributes = {})
9
+ headers['Link'] ||= []
10
+ attributes = {:rel => rel}.merge(attributes)
11
+ link = "<#{uri}>"
12
+ attributes.each_pair { |k, v| link << "; #{k}=\"#{v}\"" }
13
+ headers['Link'] << link
14
+ end
15
+
16
+ # Lets you add a series of links for the current resource.
17
+ # @param [Hash{Symbol => String}] links a hash of links. Those with nil as the value are skipped.
18
+ def links(links = {})
19
+ links.each_pair do |rel, uri|
20
+ link rel, uri if uri
21
+ end
22
+ end
23
+
24
+ # Hook method - Implement this to link to the current resource and we'll automatically add header links.
25
+ def page_url(page)
26
+ nil
27
+ end
28
+
29
+ def expose_metadata(metadata)
30
+ super.tap do |meta|
31
+ if RocketPants.header_metadata? && (pagination = meta[:pagination])
32
+ links :next => (pagination[:next] && page_url(pagination[:next])),
33
+ :prev => (pagination[:previous] && page_url(pagination[:previous])),
34
+ :last => (pagination[:pages] && page_url(pagination[:pages])),
35
+ :first => page_url(1)
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -96,10 +96,11 @@ module RocketPants
96
96
  def collection(collection, options = {})
97
97
  pre_process_exposed_object collection, :collection, false
98
98
  options = options.reverse_merge(:compact => true)
99
+ meta = expose_metadata(:count => collection.length)
99
100
  render_json({
100
101
  :response => normalise_object(collection, options),
101
102
  :count => collection.length
102
- }, options)
103
+ }.merge(meta), options)
103
104
  post_process_exposed_object collection, :collection, false
104
105
  end
105
106
 
@@ -107,11 +108,13 @@ module RocketPants
107
108
  def paginated(collection, options = {})
108
109
  pre_process_exposed_object collection, :paginated, false
109
110
  options = options.reverse_merge(:compact => true)
110
- render_json({
111
- :response => normalise_object(collection, options),
111
+ meta = expose_metadata({
112
112
  :count => collection.length,
113
113
  :pagination => Respondable.extract_pagination(collection)
114
- }, options)
114
+ })
115
+ render_json({
116
+ :response => normalise_object(collection, options),
117
+ }.merge(meta), options)
115
118
  post_process_exposed_object collection, :paginated, false
116
119
  end
117
120
 
@@ -146,5 +149,16 @@ module RocketPants
146
149
  def post_process_exposed_object(resource, type, singular)
147
150
  end
148
151
 
152
+ # Given a hash of request metadata, will:
153
+ # 1. Do anything special with the request metadata
154
+ # 2. Return the hash, suitable for merging into the response hash
155
+ # 3. Start a dance party.
156
+ #
157
+ # @param [Hash{Symbol => Object}] metadata the hash of the request metadata.
158
+ # @return [Hash{Symbol => Object}] the passed in metadata
159
+ def expose_metadata(metadata)
160
+ metadata
161
+ end
162
+
149
163
  end
150
164
  end
@@ -1,8 +1,9 @@
1
1
  module RocketPants
2
2
  class Railtie < Rails::Railtie
3
3
 
4
- config.rocket_pants = ActiveSupport::OrderedOptions.new
5
- config.rocket_pants.use_caching = nil
4
+ config.rocket_pants = ActiveSupport::OrderedOptions.new
5
+ config.rocket_pants.use_caching = nil
6
+ config.rocket_pants.header_metadata = nil
6
7
 
7
8
  config.i18n.railties_load_path << File.expand_path('../locale/en.yml', __FILE__)
8
9
 
@@ -14,6 +15,7 @@ module RocketPants
14
15
  rp_config = app.config.rocket_pants
15
16
  rp_config.use_caching = Rails.env.production? if rp_config.use_caching.nil?
16
17
  RocketPants.caching_enabled = rp_config.use_caching
18
+ RocketPants.header_metadata = rp_config.header_metadata unless rp_config.header_metadata.nil?
17
19
  # Set the rocket pants cache if present.
18
20
  RocketPants.cache = rp_config.cache if rp_config.cache
19
21
  end
@@ -52,7 +52,7 @@ module RocketPants
52
52
  end
53
53
 
54
54
  def decoded_count
55
- response.decoded_body.try :count
55
+ response.decoded_body[:count]
56
56
  end
57
57
 
58
58
  def decoded_error_class
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rocket_pants
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-03 00:00:00.000000000 Z
12
+ date: 2012-05-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -250,8 +250,10 @@ files:
250
250
  - lib/rocket_pants/controller/caching.rb
251
251
  - lib/rocket_pants/controller/error_handling.rb
252
252
  - lib/rocket_pants/controller/format_verification.rb
253
+ - lib/rocket_pants/controller/header_metadata.rb
253
254
  - lib/rocket_pants/controller/instrumentation.rb
254
255
  - lib/rocket_pants/controller/jsonp.rb
256
+ - lib/rocket_pants/controller/linking.rb
255
257
  - lib/rocket_pants/controller/rescuable.rb
256
258
  - lib/rocket_pants/controller/respondable.rb
257
259
  - lib/rocket_pants/controller/url_for.rb