rooftop 0.0.6 → 0.0.7.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f7b51337bae47ac92f6fb41b5716bbe292f3f19b
4
- data.tar.gz: c0ee1e20299e906b1fcf0358e820ddf05ae3d31a
3
+ metadata.gz: bf9827cc2965fecd6ca71d8ab14d8977e156691b
4
+ data.tar.gz: 15b808878885da0a5888bd862eae95f5fcf03e46
5
5
  SHA512:
6
- metadata.gz: 440657fbf5aa59c58464a661e57f04c7459f78b4efe4e7796c0180ed2c6e0064f83097e17cccbb397d5de151636ba128c1f45b06e2c8a443092faf3d3c65d62f
7
- data.tar.gz: 2c0e502abd0d09977f803d6354a22b4ae3b762044513d7793847d46d225879e6a1ceb57136b98bbb288fb71fa8629e31bd40d1112bb29b41ac28602126afbf37
6
+ metadata.gz: 07d4ef3d546e300f33c99887658d0efe9e3a231e13e7f7bd1e519d879d3b64c108ba21ce3e7720eac5e4d1dc534b2780e462b54463624872113e51686aed6825
7
+ data.tar.gz: d1817d197e0a617c5321a833a6cf547023bdb93ce5ceb80e1cd73904bf8dece8c0d7a243f1e993402d749a5de1bc1378346a8220b39a6feb72657d59bf82dc89
data/lib/rooftop.rb CHANGED
@@ -9,8 +9,8 @@ module Rooftop
9
9
  DEFAULT_API_VERSION = 2
10
10
 
11
11
  class << self
12
- #accessor to set the preview API for use instead of the production one
13
- attr_accessor :use_preview_api
12
+ #accessor to set whether we're in privew mode
13
+ attr_accessor :preview, :debug_requests, :debug_responses
14
14
 
15
15
  #access the configuration class as Rooftop.configuration
16
16
  attr_accessor :configuration
@@ -25,7 +25,7 @@ module Rooftop
25
25
  end
26
26
 
27
27
  class Configuration
28
- attr_accessor :api_token, :url, :site_name, :perform_caching, :cache_store, :cache_logger, :ssl_options, :proxy, :post_type_mapping
28
+ attr_accessor :api_token, :url, :site_name, :perform_caching, :cache_store, :cache_logger, :ssl_options, :proxy, :post_type_mapping, :logger
29
29
  attr_reader :connection,
30
30
  :connection_path,
31
31
  :api_path, #actually writeable with custom setter
@@ -45,16 +45,13 @@ module Rooftop
45
45
  @ssl_options = {}
46
46
  @proxy = nil
47
47
  @post_type_mapping = {}
48
+ @logger = nil
48
49
  end
49
50
 
50
51
  def api_path=(path)
51
52
  @api_path = path || @api_path
52
53
  end
53
54
 
54
- def user_agent=(agent)
55
- @user_agent = agent || @user_agent
56
- end
57
-
58
55
  def extra_headers=(headers)
59
56
  @extra_headers = headers || @extra_headers
60
57
  end
@@ -80,7 +77,16 @@ module Rooftop
80
77
 
81
78
  @connection_path = "#{@url}#{@api_path}"
82
79
 
83
- @connection.setup url: @connection_path, ssl: @ssl_options, proxy: @proxy do |c|
80
+ @connection.setup url: @connection_path, ssl: @ssl_options, proxy: @proxy, send_only_modified_attributes: true do |c|
81
+ c.use Rooftop::EmbedMiddleware
82
+
83
+ c.use Rooftop::PaginationMiddleware
84
+
85
+ if @logger
86
+ c.use Rooftop::DebugMiddleware
87
+ end
88
+
89
+
84
90
  #Headers
85
91
  c.use Rooftop::Headers
86
92
 
data/lib/rooftop/base.rb CHANGED
@@ -19,16 +19,26 @@ module Rooftop
19
19
  base.include Rooftop::Queries
20
20
  # Links mixin handles the _links key in a response
21
21
  base.include Rooftop::ResourceLinks
22
+
23
+ # Pagination mixin - uses pagination metadata set in the PaginationMiddleware
24
+ base.include Rooftop::Pagination
25
+
22
26
  # Use the API instance we have configured - in a proc because we can't control load order
23
27
  base.send(:use_api,->{Rooftop.configuration.connection})
24
28
 
25
29
  # Turn calls to `content` into a collection of Rooftop::ContentField objects
26
30
  base.include Rooftop::Content
27
31
 
32
+ # Add some useful scopes
33
+ base.include Rooftop::Scopes
34
+
35
+ # Coerce the title field from an object to a string
36
+ base.include Rooftop::Coercions::TitleCoercion
37
+
28
38
  # Date and Modified fields are pretty universal in responses from WP, so we can automatically
29
39
  # coerce these to DateTime.
30
- base.send(:coerce_field,date: ->(date) {DateTime.parse(date)})
31
- base.send(:coerce_field,modified: ->(modified) {DateTime.parse(modified)})
40
+ base.send(:coerce_field,date: ->(date) {DateTime.parse(date.to_s) unless date.nil?})
41
+ base.send(:coerce_field,modified: ->(modified) {DateTime.parse(modified.to_s) unless modified.nil?})
32
42
 
33
43
  # Having coerced the fields, we can alias them (order is important - coerce first.)
34
44
  base.send(:alias_field, date: :created_at)
@@ -64,7 +74,6 @@ module Rooftop
64
74
  def setup_path!
65
75
  @api_endpoint ||= collection_path
66
76
  self.collection_path "#{@api_namespace}/v#{@api_version}/#{@api_endpoint}"
67
-
68
77
  end
69
78
 
70
79
  # Allow calling 'first'
@@ -72,10 +81,9 @@ module Rooftop
72
81
  all.first
73
82
  end
74
83
 
75
-
76
-
77
-
84
+ def reload!
85
+ self.class.find(self.id) if self.id
86
+ end
78
87
  end
79
-
80
88
  end
81
- end
89
+ end
@@ -15,6 +15,11 @@ module Rooftop
15
15
  end
16
16
  end
17
17
  })
18
+ base.send(:before_save, ->(r) {
19
+ r.coercions.each do |field,coercion|
20
+ r.send(:"restore_#{field}!") unless r.new?
21
+ end
22
+ })
18
23
  end
19
24
 
20
25
  module ClassMethods
@@ -0,0 +1,26 @@
1
+ module Rooftop
2
+ module Coercions
3
+ module TitleCoercion
4
+ def self.included(base)
5
+ base.send(:after_find, :coerce_title_to_string)
6
+ base.send(:after_save, :coerce_title_to_string)
7
+
8
+ base.send(:before_save, ->(record) {
9
+ if record.respond_to?(:title) && record.respond_to?(:title_object)
10
+ record.title_object ||= {}
11
+ record.title_object[:rendered] = record.title
12
+ end
13
+ })
14
+
15
+ end
16
+
17
+ def coerce_title_to_string
18
+ record = self
19
+ if record.respond_to?(:title) && record.title.is_a?(ActiveSupport::HashWithIndifferentAccess)
20
+ record.title_object = record.title
21
+ record.title = record.title[:rendered]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -2,9 +2,20 @@ module Rooftop
2
2
  module Content
3
3
  class Collection < ::Array
4
4
  def initialize(content_fields)
5
- content_fields.each do |field|
6
- self << Rooftop::Content::Field.new(field)
5
+ content_fields.each do |field|
6
+ # if the field has a 'fields' key, it is a repeater field. Collect the sub-fields and
7
+ # set the field content to the collection of repeated fields
8
+ if field.has_key?('fields')
9
+ repeated_fields = field[:fields].collect do |repeated_fields|
10
+ repeated_fields.collect{|field| Rooftop::Content::Field.new(field)}
11
+ end
12
+
13
+ field.delete(:fields)
14
+ field[:value] = repeated_fields
7
15
  end
16
+
17
+ self << Rooftop::Content::Field.new(field)
18
+ end
8
19
  end
9
20
 
10
21
  # Find content_fields by attribute. Assume there will only be one attribute in the search
@@ -42,4 +53,4 @@ module Rooftop
42
53
 
43
54
  end
44
55
  end
45
- end
56
+ end
@@ -34,6 +34,11 @@ module Rooftop
34
34
  r.fields = Rooftop::Content::Collection.new((basic_fields + advanced_fields))
35
35
  end
36
36
  })
37
+
38
+ base.send(:before_save, ->(r) {
39
+ r.restore_fields! unless r.new?
40
+ #TODO we need to write these back into the actual fields.
41
+ })
37
42
  end
38
43
  end
39
- end
44
+ end
@@ -2,11 +2,33 @@ module Rooftop
2
2
  module Content
3
3
  class Field < ::OpenStruct
4
4
 
5
+ #todo - this would be nice to get working. For a relationship, we should be returning the object not a big hash
6
+ # def initialize(hash=nil)
7
+ # if hash.has_key?(:type) && hash[:type] == "relationship"
8
+ # related_objects = [hash[:value]].flatten
9
+ # hash[:value] = related_objects.inject([]) do |array,object|
10
+ # begin
11
+ # klass = Rooftop.configuration.post_type_mapping[object[:post_type].to_sym] || object[:post_type].to_s.classify.constantize
12
+ # array << klass.new(object).run_callbacks(:find)
13
+ # rescue
14
+ # array << object
15
+ # end
16
+ # end
17
+ # super
18
+ # else
19
+ # super
20
+ # end
21
+ # end
22
+
23
+
5
24
 
6
25
  def to_s
7
- value if respond_to?(:value)
26
+ if respond_to?(:value) && value.is_a?(String)
27
+ value
28
+ else
29
+ inspect
30
+ end
8
31
  end
9
-
10
32
  end
11
33
  end
12
34
  end
@@ -0,0 +1,5 @@
1
+ module Rooftop
2
+ module ResourceLinks
3
+ class UnresolvableLinkError < NoMethodError; end
4
+ end
5
+ end
@@ -19,6 +19,12 @@ module Rooftop
19
19
  end
20
20
  })
21
21
 
22
+ base.send(:before_save, ->(r) {
23
+ r.field_aliases.each do |old,new|
24
+ r.send(:"restore_#{new}!") unless r.new?
25
+ end
26
+ })
27
+
22
28
  end
23
29
 
24
30
  module ClassMethods
@@ -0,0 +1,60 @@
1
+ # Code courtesty https://github.com/envylabs/faraday-detailed_logger - MIT licence
2
+
3
+ module Rooftop
4
+ class DebugMiddleware < Faraday::Response::Middleware
5
+
6
+ def self.default_logger
7
+ require "logger"
8
+ ::Logger.new(STDOUT)
9
+ end
10
+
11
+ # Public: Initialize a new Logger middleware.
12
+ #
13
+ # app - A Faraday-compatible middleware stack or application.
14
+ # logger - A Logger-compatible object to which the log information will
15
+ # be recorded.
16
+ # progname - A String containing a program name to use when logging.
17
+ #
18
+ # Returns a Logger instance.
19
+ #
20
+ def initialize(app, logger = nil, progname = nil)
21
+ super(app)
22
+ @logger = logger || self.class.default_logger
23
+ @progname = progname
24
+ end
25
+
26
+
27
+ def call(env)
28
+ if Rooftop.debug_requests
29
+ @logger.info(@progname) { "#{env[:method].upcase} #{env[:url]}" }
30
+ @logger.debug(@progname) { curl_output(env[:request_headers], env[:body]).inspect }
31
+ end
32
+ super
33
+ end
34
+
35
+ def on_complete(env)
36
+ if Rooftop.debug_responses
37
+ status = env[:status]
38
+ log_response_status(@progname, status) { "HTTP #{status}" }
39
+ @logger.debug(@progname) { curl_output(env[:response_headers], env[:body]).inspect }
40
+ end
41
+ end
42
+
43
+ private
44
+ def curl_output(headers, body)
45
+ string = headers.collect { |k,v| "#{k}: #{v}" }.join("\n")
46
+ string + "\n\n#{body}"
47
+ end
48
+
49
+ def log_response_status(progname, status, &block)
50
+ case status
51
+ when 200..399
52
+ @logger.info(progname, &block)
53
+ else
54
+ @logger.warn(progname, &block)
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,18 @@
1
+ # This is a bit hacky. It looks like Her.rb strips querystrings with an underscore, and WP requires
2
+ # '?_embed' in order to embed child links. We look for a query param called 'embed' and change it
3
+ # to _embed (as well as sending the original)
4
+
5
+ module Rooftop
6
+ class EmbedMiddleware < Faraday::Middleware
7
+
8
+ def call(env)
9
+ query = Faraday::Utils.parse_query(env.url.query) || {}
10
+ query["_embed"] = true if query.has_key?("include_embedded_resources")
11
+ query["per_page"] = 99999999 unless query.has_key?("per_page")
12
+ env.url.query = Faraday::Utils.build_query(query.except("include_embedded_resources"))
13
+ @app.call env
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -5,6 +5,10 @@ module Rooftop
5
5
  env[:request_headers]["Api-Token"] = Rooftop.configuration.api_token
6
6
  end
7
7
 
8
+ if Rooftop.preview
9
+ env[:request_headers]['preview'] = "true"
10
+ end
11
+
8
12
  Rooftop.configuration.extra_headers.each do |key,value|
9
13
  env[:request_headers][key.to_s] = value
10
14
  end
@@ -0,0 +1,26 @@
1
+ module Rooftop
2
+ class PaginationMiddleware < Faraday::Response::Middleware
3
+ def on_complete(env)
4
+ @env = env
5
+
6
+ pagination = {
7
+ total_count: header("x-wp-total").to_i,
8
+ total_pages: header("x-wp-totalpages").to_i,
9
+ per_page: (header("x-wp-per-page").to_i || 10),
10
+ page: header("x-wp-page").to_i || 1
11
+ }
12
+
13
+ env[:body].merge!(pagination: pagination)
14
+ end
15
+
16
+ private
17
+
18
+ # Returns a response header value.
19
+ #
20
+ # @param [String] name of the header attribute
21
+ # @return [String] the response header value
22
+ def header(name)
23
+ @env.response_headers[name]
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,18 @@
1
1
  module Rooftop
2
2
  module Nested
3
+
4
+ def self.included(base)
5
+ @nested_classes ||= []
6
+ @nested_classes << base unless @nested_classes.include?(base)
7
+ end
8
+
9
+ def self.nested_classes
10
+ @nested_classes
11
+ end
12
+
13
+ def root
14
+ ancestors.last || resource_links.find_by(link_type: 'self').first
15
+ end
3
16
 
4
17
  def ancestors
5
18
  if respond_to?(:resource_links)
@@ -18,9 +31,13 @@ module Rooftop
18
31
  end
19
32
 
20
33
  def parent
21
- if respond_to?(:resource_links)
22
- resource_links.find_by(link_type: "up").first
34
+ if respond_to?(:resource_links) && resource_links
35
+ ancestors.first
23
36
  end
24
37
  end
38
+
39
+ def siblings
40
+ self.class.find(parent.id).children.reject! {|c| c.id == self.id}
41
+ end
25
42
  end
26
43
  end
data/lib/rooftop/page.rb CHANGED
@@ -1,11 +1,17 @@
1
1
  module Rooftop
2
2
  module Page
3
3
  def self.included(base)
4
+ @page_classes ||= []
5
+ @page_classes << base unless @page_classes.include?(base)
4
6
  base.include Rooftop::Base
5
7
  base.include Rooftop::Nested
6
8
  base.extend ClassMethods
7
9
  end
8
10
 
11
+ def self.page_classes
12
+ @page_classes
13
+ end
14
+
9
15
  module ClassMethods
10
16
 
11
17
 
@@ -0,0 +1,16 @@
1
+ module Rooftop
2
+ module Pagination
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def new_collection(parsed_data)
9
+ parsed_data[:metadata] ||= {}
10
+ parsed_data[:metadata][:pagination] = parsed_data[:pagination]
11
+
12
+ Her::Model::Attributes.initialize_collection(self, parsed_data)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,12 +1,13 @@
1
1
  module Rooftop
2
2
  module Queries
3
+ PER_PAGE = 99999999
3
4
  def self.included(base)
4
5
  base.extend ClassMethods
5
6
  end
6
7
 
7
8
  module ClassMethods
8
9
  # We need to fix up the `where()` filter. WP-API expects a url format for filters like this:
9
- # /?filter[something]=foo.
10
+ # /?filter[something]=foo. But we have a magic hash key to allow us to send things which aren't mangled.
10
11
  def where(args)
11
12
  args = HashWithIndifferentAccess.new(args)
12
13
  # the fact that 'slug' is referred to in the db as 'name' is irritating. Let's fix that
@@ -34,7 +35,27 @@ module Rooftop
34
35
  end
35
36
  args.delete(:id)
36
37
  end
37
- filters = args.inject({}) {|hash,pair| hash["filter[#{pair.first}]"] = pair.last; hash}
38
+
39
+ if args.keys.include?('per_page')
40
+ per_page = args['per_page']
41
+ args[:no_filter] ||= []
42
+ args[:no_filter] << :per_page unless args[:no_filter].include?('per_page')
43
+ else
44
+ per_page = Rooftop::Queries::PER_PAGE
45
+ end
46
+
47
+ if args.keys.collect(&:to_sym).include?(:no_filter)
48
+ args_to_filter = args.except(*args[:no_filter]).except(:no_filter)
49
+ args_not_to_filter = args.except(args_to_filter).except(:no_filter)
50
+ filters = args_to_filter.inject({}) {|hash,pair| hash["filter[#{pair.first}]"] = pair.last; hash}
51
+ filters = {per_page: per_page}.merge(filters).merge(args_not_to_filter)
52
+ else
53
+ #TODO DRY
54
+ filters = args.inject({}) {|hash,pair| hash["filter[#{pair.first}]"] = pair.last; hash}
55
+ filters = {per_page: per_page}.merge(filters)
56
+ end
57
+
58
+ # we probably want every result without pagination, unless we specify otherwise
38
59
  #Call the Her `where` method with our new filters
39
60
  super().where(filters)
40
61
  end
@@ -49,6 +70,11 @@ module Rooftop
49
70
  raise Rooftop::RecordNotFoundError
50
71
  end
51
72
  end
73
+
74
+ # 'all' needs to have a querystring param passed to really get all. It should be -1 but for some reason that's not working.
75
+ def all(args = {})
76
+ super({per_page: Rooftop::Queries::PER_PAGE}.merge(args))
77
+ end
52
78
  end
53
79
  end
54
- end
80
+ end
@@ -2,10 +2,10 @@ module Rooftop
2
2
  module ResourceLinks
3
3
  class Collection < ::Array
4
4
  attr_reader :links
5
- def initialize(links)
5
+ def initialize(links, klass=nil)
6
6
  links.each do |link_type,links|
7
7
  links.each do |link|
8
- self << Rooftop::ResourceLinks::Link.new(link_type,link)
8
+ self << Rooftop::ResourceLinks::Link.new(link_type,link, klass)
9
9
  end
10
10
  end
11
11
  end
@@ -2,13 +2,29 @@ module Rooftop
2
2
  module ResourceLinks
3
3
  class Link < ::OpenStruct
4
4
  attr_accessor :link_type
5
- def initialize(link_type,args)
5
+ def initialize(link_type,args, klass=nil)
6
6
  @link_type = link_type
7
+ @mapped_class = klass.try(:resource_link_mapping).try(:[],@link_type)
7
8
  super(args)
8
9
  end
9
10
 
10
- def resolve
11
- raise NotImplementedError, "TODO: resolve the link."
11
+ def resolve(klass=nil)
12
+ # We need to figure out what we're going to instantiate. If it's in the resource link mapping, use that. If not, try the klass passed into the resolve() method. Failing that, make an attempt to constantize something; otherwise we're going to have to raise
13
+ @mapped_class ||= klass || @link_type.camelize.classify.constantize
14
+ if @mapped_class
15
+ # If this link has an ID, we can call find() on the class
16
+ if respond_to?(:id)
17
+ return @mapped_class.send(:find, id)
18
+ else
19
+ # otherwise we're going to have make a call to the link's href.
20
+ result = @mapped_class.get(href)
21
+ result.run_callbacks(:find)
22
+ return result
23
+ end
24
+ else
25
+ raise Rooftop::ResourceLinks::UnresolvableLinkError, "Couldn't resolve a link of type #{@link_type}. Try passing the class you want to resolve to."
26
+ end
27
+
12
28
  end
13
29
  end
14
30
  end
@@ -1,15 +1,41 @@
1
1
  module Rooftop
2
2
  module ResourceLinks
3
3
  CUSTOM_LINK_RELATION_BASE = "http://docs.rooftopcms.com/link_relations"
4
+
4
5
  def self.included(base)
5
6
  # set up an attribute called resource_links, which is a collection of links
6
7
  # to other resources in the API.
7
- base.send(:after_find, ->(r) {
8
- if r.respond_to?(:"_links")
9
- r.resource_links = Rooftop::ResourceLinks::Collection.new(r._links)
10
- end
11
- })
8
+ base.send(:after_find, :generate_resource_links)
9
+ base.send(:after_save, :generate_resource_links)
10
+ base.extend ClassMethods
11
+ base.configure_resource_link_mapping
12
+ end
13
+
14
+ def generate_resource_links
15
+ if self.respond_to?(:"_links")
16
+ self.resource_links = Rooftop::ResourceLinks::Collection.new(self._links, self.class)
17
+ end
12
18
  end
13
19
 
20
+ module ClassMethods
21
+ # This class-level attribute allows us to set a mapping between a resource link name (which is probably
22
+ # an href, but might be "up" or something) and a class. It means that when we try to resolve a link of a
23
+ # given name, we know what type of class to instantiate
24
+ attr_accessor :resource_link_mapping
25
+
26
+ def configure_resource_link_mapping
27
+ @resource_link_mapping ||= {}
28
+ @resource_link_mapping.merge!({
29
+ "author" => Rooftop::Author,
30
+ "https://api.w.org/attachment" => Rooftop::MediaItem,
31
+ "self" => self,
32
+ "up" => self,
33
+ "http://docs.rooftopcms.com/link_relations/ancestors" => self,
34
+ "http://docs.rooftopcms.com/link_relations/children" => self
35
+ })
36
+ end
37
+ end
38
+
39
+
14
40
  end
15
41
  end
@@ -0,0 +1,9 @@
1
+ module Rooftop
2
+ module Scopes
3
+ def self.included(base)
4
+ base.send(:scope, :with_embedded_resources, -> {where(include_embedded_resources: true)})
5
+ end
6
+
7
+
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module Rooftop
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rooftop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ed Jones
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-27 00:00:00.000000000 Z
11
+ date: 2016-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -112,28 +112,35 @@ files:
112
112
  - lib/rooftop/coercions.rb
113
113
  - lib/rooftop/coercions/author_coercion.rb
114
114
  - lib/rooftop/coercions/parent_coercion.rb
115
+ - lib/rooftop/coercions/title_coercion.rb
115
116
  - lib/rooftop/content/collection.rb
116
117
  - lib/rooftop/content/content_fields.rb
117
118
  - lib/rooftop/content/field.rb
118
119
  - lib/rooftop/errors/field_not_found_error.rb
119
120
  - lib/rooftop/errors/record_not_found_error.rb
120
121
  - lib/rooftop/errors/unmapped_object_error.rb
122
+ - lib/rooftop/errors/unresolveable_link_error.rb
121
123
  - lib/rooftop/field_aliases.rb
122
- - lib/rooftop/headers.rb
123
124
  - lib/rooftop/hook_calls.rb
124
125
  - lib/rooftop/menus/item.rb
125
126
  - lib/rooftop/menus/menu.rb
127
+ - lib/rooftop/middleware/debug_middleware.rb
128
+ - lib/rooftop/middleware/embed_middleware.rb
129
+ - lib/rooftop/middleware/headers.rb
130
+ - lib/rooftop/middleware/pagination_middleware.rb
126
131
  - lib/rooftop/models/author.rb
127
132
  - lib/rooftop/models/media_item.rb
128
133
  - lib/rooftop/models/taxonomy.rb
129
134
  - lib/rooftop/models/taxonomy_term.rb
130
135
  - lib/rooftop/nested.rb
131
136
  - lib/rooftop/page.rb
137
+ - lib/rooftop/pagination.rb
132
138
  - lib/rooftop/post.rb
133
139
  - lib/rooftop/queries/queries.rb
134
140
  - lib/rooftop/resource_links/collection.rb
135
141
  - lib/rooftop/resource_links/link.rb
136
142
  - lib/rooftop/resource_links/resource_links.rb
143
+ - lib/rooftop/scopes.rb
137
144
  - lib/rooftop/version.rb
138
145
  - rooftop.gemspec
139
146
  homepage: http://www.rooftopcms.com