flagpole_sitta 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +38 -0
- data/README.rdoc +121 -0
- data/Rakefile +29 -0
- data/app/helpers/flagpole_sitta_helper.rb +169 -0
- data/flagpole_sitta.gemspec +23 -0
- data/lib/flagpole_sitta/bracket_retrieval.rb +83 -0
- data/lib/flagpole_sitta/cache_sitta.rb +198 -0
- data/lib/flagpole_sitta/config_sitta.rb +35 -0
- data/lib/flagpole_sitta/engine.rb +12 -0
- data/lib/flagpole_sitta/existance_hash.rb +164 -0
- data/lib/flagpole_sitta/version.rb +3 -0
- data/lib/flagpole_sitta.rb +20 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +45 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +22 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +26 -0
- data/test/dummy/config/environments/production.rb +49 -0
- data/test/dummy/config/environments/test.rb +35 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +2 -0
- data/test/dummy/public/javascripts/controls.js +965 -0
- data/test/dummy/public/javascripts/dragdrop.js +974 -0
- data/test/dummy/public/javascripts/effects.js +1123 -0
- data/test/dummy/public/javascripts/prototype.js +6001 -0
- data/test/dummy/public/javascripts/rails.js +202 -0
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/flagpole_sitta_test.rb +7 -0
- data/test/integration/navigation_test.rb +7 -0
- data/test/support/integration_case.rb +5 -0
- data/test/test_helper.rb +22 -0
- metadata +116 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.2.8)
|
5
|
+
capybara (1.1.2)
|
6
|
+
mime-types (>= 1.16)
|
7
|
+
nokogiri (>= 1.3.3)
|
8
|
+
rack (>= 1.0.0)
|
9
|
+
rack-test (>= 0.5.4)
|
10
|
+
selenium-webdriver (~> 2.0)
|
11
|
+
xpath (~> 0.1.4)
|
12
|
+
childprocess (0.3.3)
|
13
|
+
ffi (~> 1.0.6)
|
14
|
+
ffi (1.0.11)
|
15
|
+
libwebsocket (0.1.3)
|
16
|
+
addressable
|
17
|
+
mime-types (1.19)
|
18
|
+
multi_json (1.3.6)
|
19
|
+
nokogiri (1.5.5)
|
20
|
+
rack (1.4.1)
|
21
|
+
rack-test (0.6.1)
|
22
|
+
rack (>= 1.0)
|
23
|
+
rubyzip (0.9.9)
|
24
|
+
selenium-webdriver (2.24.0)
|
25
|
+
childprocess (>= 0.2.5)
|
26
|
+
libwebsocket (~> 0.1.3)
|
27
|
+
multi_json (~> 1.0)
|
28
|
+
rubyzip
|
29
|
+
sqlite3 (1.3.6)
|
30
|
+
xpath (0.1.4)
|
31
|
+
nokogiri (~> 1.3)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
capybara (>= 0.4.0)
|
38
|
+
sqlite3
|
data/README.rdoc
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
= FlagpoleSitta
|
2
|
+
|
3
|
+
I had visions, I was in them,
|
4
|
+
|
5
|
+
I was looking into the mirror
|
6
|
+
|
7
|
+
To see a little bit clearer
|
8
|
+
|
9
|
+
The rottenness and evil in me.
|
10
|
+
|
11
|
+
♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫~♫
|
12
|
+
|
13
|
+
== Preface
|
14
|
+
|
15
|
+
===This gem should be considered in early beta and highly unstable. Do not use in a production app!
|
16
|
+
|
17
|
+
This gem was inspired in part by the song Flagpole Sitta by Harvey Danger. So if you like the gem and are wanting to help out please either donate your time and submits some patches, or donate to the band who wrote the song. They put there last two albums out there all open source like, only asking that those that could donate after downloading. While your donating, if you choose to do so, don't be afraid to download there albums, its good stuff!
|
18
|
+
|
19
|
+
http://www.harveydanger.com/contribute.php
|
20
|
+
|
21
|
+
Another note on a personal level, I have some learning disabilities that make it so I frequently make grammar mistake, and once and a while just drop random articles out of my sentences. All this only happens when writing. So if something doesn't make sense in the docs don't be afraid to point it out or submit a patch. Because to me it all looks right.....
|
22
|
+
|
23
|
+
This gem has also only so far been tested with memcache using the Dalli adapter. File System caching and memory caching are not recommended.Redis/Memcache are defiantly suggested and strongly encouraged for this gem.
|
24
|
+
|
25
|
+
== Installation
|
26
|
+
|
27
|
+
For now add the following line to your gemfile
|
28
|
+
|
29
|
+
gem 'flagpole_sitta', :git => "git://github.com/rovermicrover/FlagpoleSitta.git"
|
30
|
+
|
31
|
+
Then bundle install.
|
32
|
+
|
33
|
+
== About
|
34
|
+
|
35
|
+
Flagpole Sitta is a gem that main purpose is to make it easier to effectively fragment cache in dynamic fashions in Rails.
|
36
|
+
|
37
|
+
When ever a cache is created it is associated with any model and/or record you tell it to be from the view helper method. When that model and/or record is updated all it's associated cache's are cleared.
|
38
|
+
|
39
|
+
Flagpole also expects you to put all your database calls into Procs/Lamdbas. This makes it so that your database calls wont have to happen unless your cache hasn't been created. Thus speeding up response time and reducing database traffic.
|
40
|
+
|
41
|
+
For a simple application you could do something like this.
|
42
|
+
|
43
|
+
=== PageModel
|
44
|
+
|
45
|
+
cache_sitta :route_id => "url"
|
46
|
+
|
47
|
+
=== PagesController
|
48
|
+
|
49
|
+
def show
|
50
|
+
|
51
|
+
@page_call = lambda {
|
52
|
+
if params[:url]
|
53
|
+
@page = Page.find_by_url params[:url]
|
54
|
+
else
|
55
|
+
@page = Page.find_by_url 'home'
|
56
|
+
end
|
57
|
+
}
|
58
|
+
|
59
|
+
@body_calls = [
|
60
|
+
['page', @page_call]
|
61
|
+
]
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
=== show.haml for Pages
|
66
|
+
|
67
|
+
- cache_sitta :model => Page, :route_id => params[:url], :section => "body" do
|
68
|
+
= @page.content.try(:html_safe)
|
69
|
+
|
70
|
+
First off lets look at @body_calls. The if you don't provide cache_sitta :calls, then it will look for @#{:section}_calls. So in this case it will look for @body_calls.
|
71
|
+
|
72
|
+
Second off @body_calls is an array of arrays. This is because you can have multiple calls, and each call must be given a target instance variable ('page') and a call object (@page_call). So in this instance the cache_sitta helper will do
|
73
|
+
|
74
|
+
@page = @page_call.call
|
75
|
+
|
76
|
+
You can also pass your calls options by providing :calls_args to the helper. You must though set up your calls to expect an options hash.
|
77
|
+
|
78
|
+
:route_id and :model must be provide so that the cache can be associated with the correct object, and the cache clear when its supposed to.
|
79
|
+
|
80
|
+
:route_id must also be a unquie field on the model, other wise the cache won't connect properly to the object.
|
81
|
+
|
82
|
+
For an index page you could do something like the following for a simple app.
|
83
|
+
|
84
|
+
=== BlogModel
|
85
|
+
|
86
|
+
cache_sitta :route_id => "url"
|
87
|
+
|
88
|
+
=== BlogsController
|
89
|
+
|
90
|
+
def index
|
91
|
+
|
92
|
+
@blogs_call = lambda {
|
93
|
+
@blogs = Blog.all
|
94
|
+
}
|
95
|
+
|
96
|
+
@body_calls = [
|
97
|
+
['blogs', @blogs_call]
|
98
|
+
]
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
=== index.haml for Blogs
|
103
|
+
|
104
|
+
- cache_sitta :models_in_index => Blog, :index_only => true, :section => "body" do
|
105
|
+
- @blogs.each do |blog|
|
106
|
+
= blog.title
|
107
|
+
etc
|
108
|
+
etc
|
109
|
+
|
110
|
+
First notice you don't have to pass :route_id if you pass :index_only => true. And you don't have to pass :model if you pass :models_in_index.
|
111
|
+
|
112
|
+
:models_in_index tells the helper which Model to associated with the index, so if any objects in that model update the index cache gets nuked.
|
113
|
+
|
114
|
+
:index_only => true, tells the helper to not bother trying to associated this cache with anyone item in particular.
|
115
|
+
|
116
|
+
== Footer
|
117
|
+
|
118
|
+
More examples to come in the wiki, and in the coming example app.
|
119
|
+
|
120
|
+
===This gem should be considered in early beta and highly unstable. Do not use in a production app!
|
121
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake'
|
10
|
+
require 'rake/rdoctask'
|
11
|
+
|
12
|
+
require 'rake/testtask'
|
13
|
+
|
14
|
+
Rake::TestTask.new(:test) do |t|
|
15
|
+
t.libs << 'lib'
|
16
|
+
t.libs << 'test'
|
17
|
+
t.pattern = 'test/**/*_test.rb'
|
18
|
+
t.verbose = false
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => :test
|
22
|
+
|
23
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
24
|
+
rdoc.rdoc_dir = 'rdoc'
|
25
|
+
rdoc.title = 'FlagpoleSitta'
|
26
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
27
|
+
rdoc.rdoc_files.include('README.rdoc')
|
28
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
29
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module FlagpoleSittaHelper
|
2
|
+
|
3
|
+
def update_index_array_cache model, key
|
4
|
+
model.try(:update_index_array_cache, key)
|
5
|
+
end
|
6
|
+
|
7
|
+
def update_show_array_cache model, key, route_id
|
8
|
+
model.try(:update_show_array_cache, key, route_id)
|
9
|
+
end
|
10
|
+
|
11
|
+
#AR - cache_sitta helper
|
12
|
+
|
13
|
+
#NOTE This is not safe for .builder xml files.
|
14
|
+
|
15
|
+
#Options are for cache_sitta
|
16
|
+
|
17
|
+
#key_args, passed to route_id if its a proc or lamdba
|
18
|
+
#all args must be passed in this manner if proc or lamdba
|
19
|
+
#so start your proc or lamdba with |options = {}|
|
20
|
+
|
21
|
+
#call_args, passed to call_path if its a proc of lamdba
|
22
|
+
#all args must be passed in this manner if proc or lamdba
|
23
|
+
#so start your proc or lamdba with |options = {}|
|
24
|
+
|
25
|
+
#models_in_index
|
26
|
+
#Use this if the fragment you are rendering is an index
|
27
|
+
#pass it all the different types of models/classes could be
|
28
|
+
#included in the index. All the include classes must have cache
|
29
|
+
#sitta enabled. The cache for the used index pages will then be
|
30
|
+
#wiped clear when anyone of these models/classes has an object
|
31
|
+
#created or updated.
|
32
|
+
def cache_sitta options={}, &block
|
33
|
+
|
34
|
+
#AR - If its a string, then just use that value, other wise it
|
35
|
+
#assumes that the route_id is a proc or lamdba and call its
|
36
|
+
#with the provide args.
|
37
|
+
|
38
|
+
if options[:route_id_args]
|
39
|
+
options[:route_id] = options[:route_id].call(options[:route_id_args])
|
40
|
+
elsif options[:route_id].class.eql?(Proc)
|
41
|
+
options[:route_id] = options[:route_id].call()
|
42
|
+
end
|
43
|
+
|
44
|
+
if options[:route_id].class.eql?(Array)
|
45
|
+
main_route_id = options[:route_id][0]
|
46
|
+
else
|
47
|
+
main_route_id = options[:route_id]
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
#Use subroute idea if the view can differ on things like current day, or any type of passed params that can effect how
|
52
|
+
#the page will look.
|
53
|
+
if options[:sub_route_id_args]
|
54
|
+
options[:sub_route_id] = options[:sub_route_id].call(options[:sub_route_id_args])
|
55
|
+
elsif options[:sub_route_id_args].class.eql?(Proc)
|
56
|
+
options[:sub_route_id] = options[:sub_route_id].call()
|
57
|
+
end
|
58
|
+
|
59
|
+
if options[:model]
|
60
|
+
if options[:model_args]
|
61
|
+
options[:model] = options[:model].call(options[:model_args])
|
62
|
+
elsif options[:model].class.eql?(Proc)
|
63
|
+
options[:model] = options[:model].call()
|
64
|
+
end
|
65
|
+
|
66
|
+
if options[:model].class.eql?(Array)
|
67
|
+
main_model = options[:model][0]
|
68
|
+
else
|
69
|
+
main_model = options[:model]
|
70
|
+
end
|
71
|
+
|
72
|
+
elsif params[:model]
|
73
|
+
|
74
|
+
main_model = params[:model]
|
75
|
+
|
76
|
+
elsif options[:models_in_index]
|
77
|
+
|
78
|
+
if options[:models_in_index].class.eql?(Array)
|
79
|
+
main_model = options[:models_in_index][0]
|
80
|
+
else
|
81
|
+
main_model = options[:models_in_index]
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
main_model = main_model.respond_to?(:constantize) ? main_model.constantize : main_model
|
87
|
+
|
88
|
+
action = options[:action] || params[:action]
|
89
|
+
|
90
|
+
key = "views/#{main_model}/#{action}"
|
91
|
+
|
92
|
+
key = key + (main_route_id ? ('/' + main_route_id) : '')
|
93
|
+
|
94
|
+
key = key + (options[:sub_route_id] ? ('/' + options[:sub_route_id]) : '')
|
95
|
+
|
96
|
+
key = key + (options[:section] ? ('/' + options[:section]) : '')
|
97
|
+
|
98
|
+
calls = instance_variable_get(
|
99
|
+
"@" + (options[:section] ? options[:section] : 'body') + "_calls"
|
100
|
+
)
|
101
|
+
|
102
|
+
if content = Rails.cache.read(key)
|
103
|
+
#Do nothing the content is ready to render
|
104
|
+
else
|
105
|
+
#NOTE This is not safe for .builder xml files, and using capture here is why.
|
106
|
+
#Its either this or a really complicated hack, from the rails source code, which
|
107
|
+
#at the moment I don't feel comfortable using. Waiting for an official solution for
|
108
|
+
#the ability to use capture with .builders.
|
109
|
+
content = capture do
|
110
|
+
#AR - If call_path is an array render each one, other
|
111
|
+
#wise just render call_path, because it must
|
112
|
+
#be a string or array by this point or something went
|
113
|
+
#terribly terribly wrong.
|
114
|
+
|
115
|
+
if calls
|
116
|
+
calls.each do |c|
|
117
|
+
if instance_variable_get("@#{c[0]}").nil?
|
118
|
+
if options[:calls_args]
|
119
|
+
instance_variable_set("@#{c[0]}", c[1].call(options[:calls_args]))
|
120
|
+
else
|
121
|
+
instance_variable_set("@#{c[0]}", c[1].call())
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
yield
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
#AR - If the cache is an index or includes an index
|
132
|
+
#then models_in_index should be passed with all the
|
133
|
+
#models that could show up in the index.
|
134
|
+
#Then on save of any model include here this index will be cleared.
|
135
|
+
#This can also be used for fragments where there are just so many objects,
|
136
|
+
#that while its not an index, there isn't a clear way expect to nuke it when
|
137
|
+
#any of the model types involved are updated.
|
138
|
+
|
139
|
+
if options[:models_in_index].class.eql?(Array)
|
140
|
+
options[:models_in_index].each do |m|
|
141
|
+
processed_m = m.respond_to?(:constantize) ? m.constantize : m
|
142
|
+
update_index_array_cache(processed_m, key)
|
143
|
+
end
|
144
|
+
elsif options[:models_in_index]
|
145
|
+
processed_model = options[:models_in_index].respond_to?(:constantize) ? options[:models_in_index].constantize : options[:models_in_index]
|
146
|
+
update_index_array_cache(options[:models_in_index], key)
|
147
|
+
end
|
148
|
+
|
149
|
+
#AR - Create a link between each declared object and the cache.
|
150
|
+
|
151
|
+
if !options[:index_only] && options[:route_id]
|
152
|
+
if options[:route_id].class.eql?(Array) && options[:model].class.eql?(Array)
|
153
|
+
options[:model].each_index do |i|
|
154
|
+
update_show_array_cache(options[:model][i], key, options[:route_id][i])
|
155
|
+
end
|
156
|
+
else
|
157
|
+
update_show_array_cache(main_model, key, main_route_id)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
Rails.cache.write(key, content)
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
safe_concat content
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path("../lib/flagpole_sitta/version", __FILE__)
|
2
|
+
|
3
|
+
# Provide a simple gemspec so you can easily use your enginex
|
4
|
+
# project in your rails apps through git.
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "flagpole_sitta"
|
7
|
+
s.version = FlagpoleSitta::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Andrew Rove (Rover)"]
|
10
|
+
s.email = ["rovermicrover@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/rovermicrover/FlagpoleSitta"
|
12
|
+
s.summary = "FlagpoleSitta a gem for effective dynamic caching"
|
13
|
+
s.description = "Flagpole Sitta is a gem that main purpose is to make it easier to effectively fragment cache in dynamic fashions.\n When ever a cache is created it is associated with any model and/or record you tell it to be from the helper method. When that model and/or record is updated all it's associated cache's are cleared.\n Flagpole also expects you to put all your database calls into Procs/Lamdbas. This makes it so that your database calls wont have to happen unless your cache hasn't been created. Thus speeding up response time and reducing database traffic."
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
17
|
+
s.require_path = 'lib'
|
18
|
+
|
19
|
+
#add dependecy here and in the engine file as a required.
|
20
|
+
|
21
|
+
s.add_dependency('dalli')
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module FlagpoleSitta
|
2
|
+
module BracketRetrieval
|
3
|
+
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
#When forcing a call back into a class from a module you must do it inside an include block
|
7
|
+
included do
|
8
|
+
validates_uniqueness_of (@_key_field || "name").to_sym
|
9
|
+
validates_presence_of (@_key_field || "name").to_sym
|
10
|
+
before_save :br_update_save
|
11
|
+
before_destroy :br_update_destroy
|
12
|
+
end
|
13
|
+
|
14
|
+
def br_update_save
|
15
|
+
self.br_update(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
def br_update_destroy
|
19
|
+
self.br_update(false)
|
20
|
+
end
|
21
|
+
|
22
|
+
def br_update alive
|
23
|
+
downcased = self.class.name.downcase
|
24
|
+
#Checks to make sure Controller Caching is on
|
25
|
+
if Rails.application.config.action_controller.perform_caching
|
26
|
+
Rails.cache.delete("#{downcased}/#{self.send(self.class.key_field + "_was")}")
|
27
|
+
Rails.cache.write("#{downcased}/#{self.send(self.class.key_field)}", self.send(self.class.value_field))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
|
33
|
+
#Will look up the object chain till it finds what it was set to, or not set too.
|
34
|
+
def safe_content?
|
35
|
+
result = @_safe_content || (self.superclass.respond_to?(:safe_content?) ? self.superclass.safe_content? : nil) || false
|
36
|
+
end
|
37
|
+
|
38
|
+
#Will look up the object chain till it finds what it was set to, or not set too.
|
39
|
+
def key_field
|
40
|
+
result = @_key_field || (self.superclass.respond_to?(:key_field) ? self.superclass.key_field : nil) || "name"
|
41
|
+
end
|
42
|
+
|
43
|
+
#Will look up the object chain till it finds what it was set to, or not set too.
|
44
|
+
def value_field
|
45
|
+
result = @_value_field || (self.superclass.respond_to?(:value_field) ? self.superclass.value_field : nil) || "content"
|
46
|
+
end
|
47
|
+
|
48
|
+
def default_value
|
49
|
+
result = @_default_value || (self.superclass.respond_to?(:default_value) ? self.superclass.default_value : nil) || ""
|
50
|
+
end
|
51
|
+
|
52
|
+
def [] key
|
53
|
+
downcased = self.to_s.downcase
|
54
|
+
#If its in cache return that, unless blank, then return nil
|
55
|
+
#elsif the object is in the database put it into the cache
|
56
|
+
#then return it.
|
57
|
+
#else create the corresponding object as blank, and return nil.
|
58
|
+
#The last line there is why this extension should never be used
|
59
|
+
#with user generated content.
|
60
|
+
if value = Rails.cache.read("#{downcased}/#{key}") || Rails.cache.exist?("#{downcased}/#{key}")
|
61
|
+
if value.present?
|
62
|
+
value = self.safe_content? ? value.html_safe : value
|
63
|
+
else
|
64
|
+
#Always return nil even if the return value is blank.
|
65
|
+
#Also if its blank we don't want to try to create it again
|
66
|
+
#thus the reason for this odd nested if statement.
|
67
|
+
value = nil
|
68
|
+
end
|
69
|
+
elsif obj = self.send("find_by_#{self.key_field}", key)
|
70
|
+
value = obj.send(self.value_field)
|
71
|
+
Rails.cache.write("#{downcased}/#{key}", value)
|
72
|
+
value = value && self.safe_content? ? value.html_safe : value
|
73
|
+
else
|
74
|
+
rec = self.create(self.key_field.to_sym => key, self.value_field.to_sym => self.default_value)
|
75
|
+
Rails.cache.write("#{downcased}/#{key}", rec.send(self.value_field))
|
76
|
+
value = nil
|
77
|
+
end
|
78
|
+
value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
module FlagpoleSitta
|
2
|
+
module CacheSitta
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
#When forcing a call back into a class from a module you must do it inside an include block
|
6
|
+
included do
|
7
|
+
before_save :cache_sitta_save
|
8
|
+
before_destroy :cache_sitta_destory
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
def initialize_index_array_cache
|
14
|
+
|
15
|
+
clazz = self
|
16
|
+
|
17
|
+
flag = {:space => - 1}
|
18
|
+
|
19
|
+
Rails.cache.write("#{clazz}/IndexArray/Flag", flag)
|
20
|
+
|
21
|
+
flag
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_index_array_cache key
|
26
|
+
|
27
|
+
clazz = self
|
28
|
+
|
29
|
+
flag = Rails.cache.read("#{clazz}/IndexArray/Flag")
|
30
|
+
|
31
|
+
#AR - If it doesn't exist start the process of creating it
|
32
|
+
if flag.nil?
|
33
|
+
flag = initialize_index_array_cache
|
34
|
+
end
|
35
|
+
|
36
|
+
#AR - update the array's end point
|
37
|
+
flag[:space] = flag[:space] + 1
|
38
|
+
|
39
|
+
#AR - write out the new index at the end of the array
|
40
|
+
Rails.cache.write("#{clazz}/IndexArray/#{flag[:space]}", {:key => key})
|
41
|
+
|
42
|
+
#AR - update flag in the cache
|
43
|
+
Rails.cache.write("#{clazz}/IndexArray/Flag", flag)
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def each_index_cache &block
|
48
|
+
|
49
|
+
clazz = self
|
50
|
+
|
51
|
+
flag = Rails.cache.read("#{clazz}/IndexArray/Flag")
|
52
|
+
|
53
|
+
#AR - If it doesn't exist start the process of creating it
|
54
|
+
if flag.nil?
|
55
|
+
flag = initialize_index_array_cache
|
56
|
+
end
|
57
|
+
|
58
|
+
#AR - If there aren't any index do nothing.
|
59
|
+
#Else wise loop through every index.
|
60
|
+
#If it actually does exist then yield.
|
61
|
+
for i in 0..flag[:space] do
|
62
|
+
hash = Rails.cache.read("#{clazz}/IndexArray/#{i}")
|
63
|
+
if hash
|
64
|
+
yield hash[:key]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
nil
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def destroy_index_array_cache
|
73
|
+
|
74
|
+
clazz = self
|
75
|
+
|
76
|
+
each_index_cache do |key|
|
77
|
+
Rails.cache.delete(key)
|
78
|
+
end
|
79
|
+
|
80
|
+
Rails.cache.delete("#{clazz}/IndexArray/Flag")
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize_show_array_cache route_id
|
84
|
+
|
85
|
+
clazz = self
|
86
|
+
|
87
|
+
#AR - Its negative one to stop the for loops in the each method if its empty
|
88
|
+
flag = {:space => - 1}
|
89
|
+
|
90
|
+
Rails.cache.write("#{clazz}/#{route_id}/ShowArray/Flag", flag)
|
91
|
+
|
92
|
+
flag
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
def update_show_array_cache key, route_id
|
97
|
+
|
98
|
+
clazz = self
|
99
|
+
|
100
|
+
flag = Rails.cache.read("#{clazz}/#{route_id}/ShowArray/Flag")
|
101
|
+
|
102
|
+
#AR - If it doesn't exist start the process of creating it
|
103
|
+
if flag.nil?
|
104
|
+
flag = initialize_show_array_cache(route_id)
|
105
|
+
end
|
106
|
+
|
107
|
+
#AR - Update the array's end point
|
108
|
+
flag[:space] = flag[:space] + 1
|
109
|
+
|
110
|
+
#AR - Write out the new index at the end of the array
|
111
|
+
Rails.cache.write("#{clazz}/#{route_id}/ShowArray/#{flag[:space]}", {:key => key})
|
112
|
+
|
113
|
+
#AR - Update flag in the cache
|
114
|
+
Rails.cache.write("#{clazz}/#{route_id}/ShowArray/Flag", flag)
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
def each_show_cache route_id, &block
|
119
|
+
|
120
|
+
clazz = self
|
121
|
+
|
122
|
+
flag = Rails.cache.read("#{clazz}/#{route_id}/ShowArray/Flag")
|
123
|
+
|
124
|
+
#AR - If it doesn't exist start the process of creating it
|
125
|
+
if flag.nil?
|
126
|
+
flag = initialize_show_array_cache(route_id)
|
127
|
+
end
|
128
|
+
|
129
|
+
#AR - If there aren't any shows caches do nothing, this happens when space is -1.
|
130
|
+
#Else wise loop through every caches.
|
131
|
+
#If it actually does exist then yield.
|
132
|
+
for i in 0..flag[:space] do
|
133
|
+
hash = Rails.cache.read("#{clazz}/#{route_id}/ShowArray/#{i}")
|
134
|
+
if hash
|
135
|
+
yield hash[:key]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
nil
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
def destroy_show_array_cache route_id
|
144
|
+
|
145
|
+
clazz = self
|
146
|
+
|
147
|
+
each_show_cache route_id do |k|
|
148
|
+
Rails.cache.delete(k)
|
149
|
+
end
|
150
|
+
|
151
|
+
Rails.cache.delete("#{clazz}/#{route_id}/ShowArray/Flag")
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
def cache_sitta_save
|
157
|
+
self.cache_work(true)
|
158
|
+
end
|
159
|
+
|
160
|
+
def cache_sitta_destory
|
161
|
+
self.cache_work(false)
|
162
|
+
end
|
163
|
+
|
164
|
+
def cache_work(alive)
|
165
|
+
original_clazz = self.class
|
166
|
+
# Also have to go through all its super objects till the super objects aren't cache sittaed
|
167
|
+
# this is because the new updated object for a sub class, could have also been in a cache for
|
168
|
+
# said sub class, but also in a cache for its super.
|
169
|
+
clazz = original_clazz
|
170
|
+
while(clazz.respond_to? :destroy_show_array_cache)
|
171
|
+
|
172
|
+
#AR - Clear all caches related to the old route_id
|
173
|
+
clazz.destroy_show_array_cache(self.try(:send, ("#{clazz.route_id}_was")).to_s)
|
174
|
+
#AR - Clear all caches related to the new route_id just in case
|
175
|
+
clazz.destroy_show_array_cache(self.try(:send, ("#{clazz.route_id}")).to_s)
|
176
|
+
#AR - If the new and old are the same All that will happen on the second call is that
|
177
|
+
#it will write the flag out and then destroy it. A very tiny bit of work
|
178
|
+
#for a great amount of extra protection.
|
179
|
+
|
180
|
+
# AR - Remember to include models_in_index in your helper call in the corresponding index cache.
|
181
|
+
clazz.destroy_index_array_cache
|
182
|
+
|
183
|
+
clazz = clazz.superclass
|
184
|
+
end
|
185
|
+
|
186
|
+
#AR - For Safety this will not recurse upwards for the extra cache maintenance
|
187
|
+
extra_cache_maintenance(alive)
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
#AR - For Safety this will not recurse upwards for the extra cache maintenance
|
192
|
+
def extra_cache_maintenance alive
|
193
|
+
method = (@_cache_extra_maintance || Proc.new{})
|
194
|
+
method.call
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
end
|