rooftop-rails 0.0.3 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/helpers/rooftop/rails/menu_helper.rb +4 -0
- data/app/models/concerns/rooftop/rails/nested_model.rb +1 -4
- data/app/models/concerns/rooftop/rails/object_cache.rb +99 -36
- data/lib/generators/rooftop/rooftop_generator.rb +13 -0
- data/lib/generators/rooftop/templates/initializer.rb.erb +26 -0
- data/lib/rooftop/rails.rb +4 -1
- data/lib/rooftop/rails/cache_expirer.rb +20 -0
- data/lib/rooftop/rails/engine.rb +17 -19
- data/lib/rooftop/rails/errors.rb +1 -0
- data/lib/rooftop/rails/preview.rb +5 -5
- data/lib/rooftop/rails/route_resolver.rb +9 -4
- data/lib/rooftop/rails/version.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d686ac1326055010ea5a1026d27d6d1b5fa7cfda
|
4
|
+
data.tar.gz: 62ddec40b4f47c843bc01976efa2dc28a0fa782c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 767379cba619e6a39b40c158043328f057119fe595ff215268ef92aace444541be2621197493f5946734d8c4508c5d6a75991ed95b3f06c47dd6eb5731cf7f23
|
7
|
+
data.tar.gz: 03298dae9b28386035440e077b6a07ed222b3b42a0eadcdee48eff2865d0b1a2eecb8997062fbc320a4505e1250d765dac5f47d7dd0efc6519121c6904211082
|
@@ -22,7 +22,7 @@ module Rooftop
|
|
22
22
|
path = CGI::unescape(path) if options[:unescape]
|
23
23
|
delimiter = options[:delimiter]
|
24
24
|
slug = "#{options[:prefix]}#{path}".gsub(/^\//, '').split(delimiter).last
|
25
|
-
entity = where(slug: slug).first
|
25
|
+
entity = where(slug: slug, per_page: 1).first
|
26
26
|
if entity.nil?
|
27
27
|
raise Rooftop::RecordNotFoundError, "Couldn't find #{self} with slug #{slug}"
|
28
28
|
else
|
@@ -47,6 +47,3 @@ module Rooftop
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
@@ -31,6 +31,7 @@ module Rooftop
|
|
31
31
|
::Rails.cache.read("#{cache_key_base}/#{id}")
|
32
32
|
end
|
33
33
|
|
34
|
+
all_objects.each {|o| o.run_callbacks(:find)}
|
34
35
|
all_objects.length == 1 ? all_objects.first : all_objects
|
35
36
|
else
|
36
37
|
super
|
@@ -38,59 +39,121 @@ module Rooftop
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def where(args)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
42
|
+
if Rooftop::Rails.configuration.perform_object_caching
|
43
|
+
# Sort the arguments - reduces the number of different argument hashes.
|
44
|
+
# Note that 2 different arg hashes might actually be the same, with child arrays
|
45
|
+
# in a different order. But we won't mess with the order of the child arrays
|
46
|
+
# because sometimes that's important.
|
47
|
+
args = Hash[args.sort]
|
48
|
+
# Generate a hash for keying the cache of the results
|
49
|
+
args_hash = Digest::MD5.hexdigest(args.to_s)
|
50
|
+
cache_key = "#{cache_key_base}/collection_query/#{args_hash}"
|
51
|
+
# first see if we have a collection which matches the args
|
52
|
+
cached_collection = ::Rails.cache.read(cache_key)
|
53
|
+
# if it's present, then we can return it directly.
|
54
|
+
if cached_collection.present?
|
55
|
+
::Rails.logger.debug("Returning cached collection for #{cache_key}")
|
56
|
+
cached_collection.each {|o| o.run_callbacks :find}
|
57
|
+
return cached_collection
|
58
|
+
else
|
59
|
+
# If not, then we need to call super() to get it from the API
|
60
|
+
collection = super(args)
|
61
|
+
# and write it into the cache
|
62
|
+
::Rails.cache.write(cache_key,collection)
|
63
|
+
# We also iterate over the collection and cache each object, and cache the argument hash against each object
|
64
|
+
collection.each do |object|
|
65
|
+
# This is a bit funky and circular. We store an array of hashes for queries to which this object belongs.
|
66
|
+
# If the object cache needs to be removed, we can iterate through those hashes and clear the collection caches too.
|
67
|
+
collection_query_hash_key = "#{object.cache_key}/collection_hashes"
|
68
|
+
collection_hashes = ::Rails.cache.read(collection_query_hash_key) || []
|
69
|
+
collection_hashes << args_hash
|
70
|
+
::Rails.cache.write(collection_query_hash_key, collection_hashes)
|
71
|
+
# this is the object cache - i.e. it'll respond with a cache lookup for Page.find(14) or whatever
|
72
|
+
::Rails.cache.write(object.cache_key,object)
|
73
|
+
::Rails.logger.debug("Written cached collection for #{object.cache_key}")
|
74
|
+
end
|
75
|
+
collection
|
76
|
+
end
|
51
77
|
else
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
alias_method :find_by, :where
|
83
|
+
|
84
|
+
def all(args = {})
|
85
|
+
#TODO this is identical to where, above, but the super() is different. Refactor out to DRY
|
86
|
+
if Rooftop::Rails.configuration.perform_object_caching
|
87
|
+
#Sort the arguments, and any keys which are arrays
|
88
|
+
args = Hash[args.sort]
|
89
|
+
# Generate a hash for keying the cache of the results
|
90
|
+
args_hash = Digest::MD5.hexdigest(args.to_s)
|
91
|
+
cache_key = "#{cache_key_base}/collection_query/#{args_hash}"
|
92
|
+
# first see if we have a collection which matches the args
|
93
|
+
cached_collection = ::Rails.cache.read(cache_key)
|
94
|
+
# if it's present, then we can return it directly.
|
95
|
+
if cached_collection.present?
|
96
|
+
::Rails.logger.debug("Returning cached collection for #{cache_key}")
|
97
|
+
cached_collection.each {|o| o.run_callbacks :find}
|
98
|
+
return cached_collection
|
99
|
+
else
|
100
|
+
# If not, then we need to call super() to get it from the API
|
101
|
+
collection = super
|
102
|
+
# and write it into the cache
|
103
|
+
::Rails.cache.write(cache_key,collection)
|
104
|
+
# We also iterate over the collection and cache each object, and cache the argument hash against each object
|
105
|
+
collection.each do |object|
|
106
|
+
# This is a bit funky and circular. We store an array of hashes for queries to which this object belongs.
|
107
|
+
# If the object cache needs to be removed, we can iterate through those hashes and clear the collection caches too.
|
108
|
+
collection_query_hash_key = "#{object.cache_key}/collection_hashes"
|
109
|
+
collection_hashes = ::Rails.cache.read(collection_query_hash_key) || []
|
110
|
+
collection_hashes << args_hash
|
111
|
+
::Rails.cache.write(collection_query_hash_key, collection_hashes)
|
112
|
+
# this is the object cache - i.e. it'll respond with a cache lookup for Page.find(14) or whatever
|
113
|
+
::Rails.cache.write(object.cache_key,object)
|
114
|
+
::Rails.logger.debug("Written cached collection for #{object.cache_key}")
|
115
|
+
end
|
116
|
+
collection
|
66
117
|
end
|
67
|
-
|
118
|
+
else
|
119
|
+
super
|
68
120
|
end
|
69
121
|
end
|
70
122
|
|
71
123
|
# A method to expire the relevant caches for a collection of objects or ids
|
72
124
|
# @param args [Array] of either objects which respond to `.id`, or ids themselves
|
73
125
|
def expire_cache_for(*args)
|
74
|
-
args = args.collect {|a| a.respond_to?(:id) ? a.id : a}
|
126
|
+
args = args.collect {|a| a.respond_to?(:id) ? a.id : a}.flatten
|
75
127
|
# the caches we need to clear are:
|
76
128
|
# - the object cache
|
77
129
|
# - any collection caches which included this object
|
78
130
|
args.each do |id|
|
79
131
|
object_cache_key = "#{cache_key_base}/#{id}"
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
132
|
+
object_collections = ::Rails.cache.read("#{object_cache_key}/collection_hashes")
|
133
|
+
if object_collections.present?
|
134
|
+
# this object has a list of collection hashes, each of which we need to remove
|
135
|
+
object_collections.each do |hash|
|
136
|
+
::Rails.cache.delete("#{cache_key_base}/collection_query/#{hash}")
|
137
|
+
end
|
138
|
+
# by implication, the cached object should also be present; clear that too
|
139
|
+
::Rails.cache.delete(object_cache_key)
|
140
|
+
else
|
141
|
+
# the object isn't in any collections; unfortunately we can't be sure whether this is because the object is old
|
142
|
+
# and uncached, or new and therefore needs to be in collections which currently exist for this class.
|
143
|
+
# Because of that, we'll aggressively remove caches for collections
|
144
|
+
begin
|
145
|
+
::Rails.cache.delete_matched("#{cache_key_base}/collection_query*")
|
146
|
+
rescue NotImplementedError
|
147
|
+
::Rails.cache.clear # rescue to Rails.cache.clear for caching methods where delete_matched isn't supported
|
148
|
+
end
|
86
149
|
end
|
87
|
-
|
88
|
-
|
150
|
+
# Always remove the object's cache. There's no risk of doing this for nonexistent things.
|
151
|
+
::Rails.cache.delete(object_cache_key)
|
89
152
|
|
90
153
|
|
154
|
+
end
|
91
155
|
end
|
92
|
-
|
93
156
|
end
|
94
157
|
end
|
95
158
|
end
|
96
|
-
end
|
159
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class RooftopGenerator < ::Rails::Generators::Base
|
2
|
+
source_root File.expand_path("../templates", __FILE__)
|
3
|
+
|
4
|
+
class_option :api_token, :type => :string, :aliases => "-t", :desc => "Your Rooftop API token", :default => "YOUR_TOKEN_HERE"
|
5
|
+
class_option :site_name, :type => :string, :aliases => "-s", :desc => "Your Rooftop subdomain / site name", :default => "YOUR_SITE_NAME_HERE"
|
6
|
+
|
7
|
+
def create_initializer
|
8
|
+
@api_token = options[:api_token]
|
9
|
+
@site_name = options[:site_name]
|
10
|
+
|
11
|
+
template "initializer.rb.erb", "config/initializers/rooftop.rb"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
if defined?(Rooftop)
|
2
|
+
Rooftop::Rails.configure do |config|
|
3
|
+
config.api_token = '<%= @api_token %>'
|
4
|
+
config.site_name = '<%= @site_name %>'
|
5
|
+
config.perform_http_response_caching = false
|
6
|
+
config.perform_object_caching = Rails.configuration.action_controller.perform_caching
|
7
|
+
config.resource_route_map = {
|
8
|
+
page: ->(path, params) {Rails.application.routes.url_helpers.page_path(path, params)}
|
9
|
+
}
|
10
|
+
<% if defined?(Rooftop::Events) %>
|
11
|
+
config.post_type_mapping = {
|
12
|
+
menu: Rooftop::Menus::Menu,
|
13
|
+
event: Rooftop::Events::Event,
|
14
|
+
event_instance: Rooftop::Events::Instance,
|
15
|
+
event_price: Rooftop::Events::Price,
|
16
|
+
event_price_list: Rooftop::Events::PriceList,
|
17
|
+
event_ticket_type: Rooftop::Events::TicketType,
|
18
|
+
event_price_band: Rooftop::Events::PriceBand
|
19
|
+
}
|
20
|
+
<% else %>
|
21
|
+
config.post_type_mapping = {
|
22
|
+
menu: Rooftop::Menus::Menu
|
23
|
+
}
|
24
|
+
<% end %>
|
25
|
+
end
|
26
|
+
end
|
data/lib/rooftop/rails.rb
CHANGED
@@ -31,9 +31,11 @@ module Rooftop
|
|
31
31
|
:perform_object_caching,
|
32
32
|
:cache_store,
|
33
33
|
:cache_logger,
|
34
|
+
:logger,
|
34
35
|
:ssl_options,
|
35
36
|
:proxy,
|
36
|
-
:resource_route_map
|
37
|
+
:resource_route_map,
|
38
|
+
:post_type_mapping
|
37
39
|
|
38
40
|
def initialize
|
39
41
|
@authenticate_webhooks = true
|
@@ -43,6 +45,7 @@ module Rooftop
|
|
43
45
|
@cache_logger = ::Rails.logger
|
44
46
|
@ssl_options = {}
|
45
47
|
@resource_route_map = {}
|
48
|
+
@logger = nil
|
46
49
|
end
|
47
50
|
end
|
48
51
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rooftop
|
2
|
+
module Rails
|
3
|
+
class CacheExpirer
|
4
|
+
def self.expire(payload)
|
5
|
+
begin
|
6
|
+
content_type = payload[:type].to_sym
|
7
|
+
if Rooftop.configuration.post_type_mapping[content_type].present?
|
8
|
+
klass = Rooftop.configuration.post_type_mapping[content_type]
|
9
|
+
else
|
10
|
+
klass = content_type.to_s.classify.constantize
|
11
|
+
end
|
12
|
+
klass.send(:expire_cache_for, payload[:id])
|
13
|
+
rescue => e
|
14
|
+
raise Rooftop::Rails::UnknownObjectForExpiry, e
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/rooftop/rails/engine.rb
CHANGED
@@ -11,18 +11,22 @@ module Rooftop
|
|
11
11
|
end
|
12
12
|
|
13
13
|
initializer "configure_rooftop", before: :add_entry_mappings do
|
14
|
-
Rooftop.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
14
|
+
if Rooftop::Rails.configuration.api_token.present?
|
15
|
+
Rooftop.configure do |config|
|
16
|
+
config.api_token = Rooftop::Rails.configuration.api_token
|
17
|
+
config.site_name = Rooftop::Rails.configuration.site_name
|
18
|
+
config.extra_headers = Rooftop::Rails.configuration.extra_headers
|
19
|
+
config.advanced_options = Rooftop::Rails.configuration.advanced_options
|
20
|
+
config.api_path = Rooftop::Rails.configuration.api_path
|
21
|
+
config.url = Rooftop::Rails.configuration.url || "https://#{config.site_name}.rooftopcms.io"
|
22
|
+
config.perform_caching = Rooftop::Rails.configuration.perform_http_response_caching
|
23
|
+
config.cache_store = Rooftop::Rails.configuration.cache_store
|
24
|
+
config.cache_logger = Rooftop::Rails.configuration.cache_logger
|
25
|
+
config.ssl_options = Rooftop::Rails.configuration.ssl_options
|
26
|
+
config.proxy = Rooftop::Rails.configuration.proxy
|
27
|
+
config.logger = Rooftop::Rails.configuration.logger
|
28
|
+
config.post_type_mapping = Rooftop::Rails.configuration.post_type_mapping
|
29
|
+
end
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
@@ -48,13 +52,7 @@ module Rooftop
|
|
48
52
|
|
49
53
|
initializer "clear_caches_on_webhook_notification" do
|
50
54
|
ActiveSupport::Notifications.subscribe(/rooftop.*/) do |name, start, finish, id, payload|
|
51
|
-
|
52
|
-
if Rooftop.configuration.post_type_mapping[content_type].present?
|
53
|
-
klass = Rooftop.configuration.post_type_mapping[content_type]
|
54
|
-
else
|
55
|
-
klass = content_type.classify.constantize
|
56
|
-
end
|
57
|
-
klass.send(:expire_cache_for, payload[:id])
|
55
|
+
Rooftop::Rails::CacheExpirer.expire(payload)
|
58
56
|
end
|
59
57
|
end
|
60
58
|
end
|
data/lib/rooftop/rails/errors.rb
CHANGED
@@ -8,11 +8,11 @@ module Rooftop::Rails
|
|
8
8
|
helper_method :preview?
|
9
9
|
end
|
10
10
|
# Check whether the subdomain being presented is the preview domain.
|
11
|
-
# If so, set Rooftop to
|
11
|
+
# If so, set Rooftop to add the preview header
|
12
12
|
def check_preview_domain
|
13
13
|
# If enable_preview_domain is not enabled, explicitly set use_preview_api false and return
|
14
14
|
unless Rooftop::Rails.configuration.enable_preview_domain
|
15
|
-
Rooftop.
|
15
|
+
Rooftop.preview = false
|
16
16
|
return
|
17
17
|
end
|
18
18
|
|
@@ -24,14 +24,14 @@ module Rooftop::Rails
|
|
24
24
|
end
|
25
25
|
# If user is authenticated, we're good to switch to the preview api
|
26
26
|
if authenticated
|
27
|
-
Rooftop.
|
27
|
+
Rooftop.preview = true
|
28
28
|
else
|
29
29
|
#otherwise ask for user / pass
|
30
30
|
request_http_basic_authentication
|
31
31
|
end
|
32
32
|
else
|
33
33
|
#if the subdomain doesn't match the configured one, explicitly set to false
|
34
|
-
Rooftop.
|
34
|
+
Rooftop.preview = false
|
35
35
|
return
|
36
36
|
end
|
37
37
|
|
@@ -47,7 +47,7 @@ module Rooftop::Rails
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def preview?
|
50
|
-
Rooftop.
|
50
|
+
Rooftop.preview == true
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -12,16 +12,21 @@ module Rooftop
|
|
12
12
|
@id = id
|
13
13
|
end
|
14
14
|
|
15
|
-
def resolve
|
15
|
+
def resolve(params={})
|
16
16
|
route_config = Rooftop::Rails.configuration.resource_route_map
|
17
17
|
resource_key = @id.nil? ? @type.to_s.pluralize.to_sym : @type
|
18
18
|
if route_config[resource_key]
|
19
|
-
|
19
|
+
if route_config[resource_key].arity == 1
|
20
|
+
return route_config[resource_key].try(:call,@id)
|
21
|
+
elsif route_config[resource_key].arity == 2
|
22
|
+
return route_config[resource_key].try(:call,@id, params)
|
23
|
+
end
|
20
24
|
else
|
21
25
|
begin
|
22
26
|
route_info = ::Rails.application.routes.named_routes[resource_key].defaults
|
23
|
-
|
24
|
-
|
27
|
+
# once you've called the routes once, you'll have an id. We don't want the previously called one, and in the case of not passing an ID, we want the index method instead of the show method
|
28
|
+
route_info.merge!(id: @id) unless @id.nil?
|
29
|
+
::Rails.application.routes.url_helpers.url_for(route_info.merge(only_path: true, params: params))
|
25
30
|
rescue
|
26
31
|
nil
|
27
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rooftop-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Error Studio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: require_all
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.0.
|
61
|
+
version: 0.0.7.4
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.0.
|
68
|
+
version: 0.0.7.4
|
69
69
|
description: This gem provides utility methods and a mountable engine for Rails applications
|
70
70
|
using the Rooftop gem
|
71
71
|
email:
|
@@ -82,7 +82,10 @@ files:
|
|
82
82
|
- app/models/concerns/rooftop/rails/nested_model.rb
|
83
83
|
- app/models/concerns/rooftop/rails/object_cache.rb
|
84
84
|
- config/routes.rb
|
85
|
+
- lib/generators/rooftop/rooftop_generator.rb
|
86
|
+
- lib/generators/rooftop/templates/initializer.rb.erb
|
85
87
|
- lib/rooftop/rails.rb
|
88
|
+
- lib/rooftop/rails/cache_expirer.rb
|
86
89
|
- lib/rooftop/rails/development_constraint.rb
|
87
90
|
- lib/rooftop/rails/engine.rb
|
88
91
|
- lib/rooftop/rails/errors.rb
|