firebolt 0.9.0

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 firebolt.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Adam Hutchison
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,58 @@
1
+ # Firebolt
2
+
3
+ Firebolt is a simple cache warmer. It warms the cache using a specially defined warmer class. It also has an optional file-based warmer.
4
+
5
+ It's not quite ready for Prime Time™ and needs specs (YIKES!). Feel free add some, if you like...
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'firebolt'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install firebolt
20
+
21
+ ## Usage
22
+
23
+ To use Firebolt, you first need to configure it:
24
+
25
+ ```Ruby
26
+ ::Firebolt.initialize! do |firebolt_config|
27
+ firebolt_config.cache = ::Rails.cache
28
+ firebolt_config.frequency = 12.hours
29
+ firebolt_config.warmer = ::YourAwesomeCacheWarmer
30
+ end
31
+ ```
32
+
33
+ Firebolt uses a cache warmer that you create. Valid cache warmers must:
34
+
35
+ 1. Include `Firebolt::Warmer`
36
+ 2. Define a `perform` method that returns a hash
37
+
38
+ Here's an example:
39
+
40
+ ```Ruby
41
+ class YourAwesomeCacheWarmer
42
+ include ::Firebolt::Warmer
43
+
44
+ def perform
45
+ # Returns a hash. The keys become the cache keys and the values become cache values.
46
+ end
47
+ end
48
+ ```
49
+
50
+ More documentation coming soon...
51
+
52
+ ## Contributing
53
+
54
+ 1. Fork it
55
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
56
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
57
+ 4. Push to the branch (`git push origin my-new-feature`)
58
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/firebolt.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'firebolt/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "firebolt"
8
+ spec.version = Firebolt::VERSION
9
+ spec.authors = ["Adam Hutchison","BJ Neilsen"]
10
+ spec.email = ["liveh2o@gmail.com","bj.neilsen@gmail.com"]
11
+ spec.description = %q{Simple little cache warmer.}
12
+ spec.summary = %q{Firebolt is a simple cache warmer. It warms the cache using a specially defined warmer class.}
13
+ spec.homepage = "https://github.com/moneydesktop/firebolt"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ ##
22
+ # Dependencies
23
+ #
24
+ spec.add_dependency "json"
25
+ spec.add_dependency "rufus-scheduler"
26
+ spec.add_dependency "sucker_punch"
27
+
28
+ ##
29
+ # Development Dependencies
30
+ #
31
+ spec.add_development_dependency "rake"
32
+ end
@@ -0,0 +1,27 @@
1
+ module Firebolt
2
+ module Cache
3
+ include ::Firebolt::Keys
4
+
5
+ def fetch(suffix, options = nil, &block)
6
+ salted_key = self.cache_key_with_salt(key_suffix, salt)
7
+ return nil if salted_key.nil?
8
+
9
+ ::Firebolt.config.cache.fetch(salted_key, options, &block)
10
+ end
11
+
12
+ def read(key_suffix, options = nil)
13
+ salted_key = self.cache_key_with_salt(key_suffix, salt)
14
+ return nil if salted_key.nil?
15
+
16
+ ::Firebolt.config.cache.read(salted_key, options)
17
+ end
18
+
19
+ def reset_salt!(new_salt)
20
+ ::Firebolt.config.cache.write(self.salt_key, new_salt)
21
+ end
22
+
23
+ def salt
24
+ ::Firebolt.config.cache.read(self.salt_key)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ module Firebolt
2
+ class CacheWorker
3
+ include ::SuckerPunch::Worker
4
+
5
+ def perform(warmer_class)
6
+ cache_warmer = warmer_class.new
7
+ results = cache_warmer.warm
8
+ return unless write_results_to_cache_file?
9
+
10
+ write_results_to_cache_file(results)
11
+ end
12
+
13
+ private
14
+
15
+ def cache_file
16
+ ::Firebolt.config.cache_file
17
+ end
18
+
19
+ def write_results_to_cache_file(results)
20
+ ::File.open(cache_file, 'w') do |file|
21
+ json_results = ::JSON.dump(results)
22
+ file.write(json_results)
23
+ end
24
+ end
25
+
26
+ def write_results_to_cache_file?
27
+ ::Firebolt.config.use_file_warmer?
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,86 @@
1
+ module Firebolt
2
+ class Config < Hash
3
+ CACHE_FILENAME = "firebolt.cache.json".freeze
4
+
5
+ ##
6
+ # Constructor!
7
+ #
8
+ def initialize(options = {})
9
+ merge!(options)
10
+
11
+ self[:cache_file_path] ||= '/tmp'
12
+ self[:namespace] ||= ::SecureRandom.hex
13
+ end
14
+
15
+ ##
16
+ # Class methods
17
+ #
18
+
19
+ # Creates an accessor that simply sets and reads a key in the hash:
20
+ #
21
+ # class Config < Hash
22
+ # hash_accessor :app
23
+ # end
24
+ #
25
+ # config = Config.new
26
+ # config.app = Foo
27
+ # config[:app] #=> Foo
28
+ #
29
+ # config[:app] = Bar
30
+ # config.app #=> Bar
31
+ #
32
+ def self.hash_accessor(*names) #:nodoc:
33
+ names.each do |name|
34
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
35
+ def #{name}
36
+ self[:#{name}]
37
+ end
38
+
39
+ def #{name}=(value)
40
+ self[:#{name}] = value
41
+ end
42
+ METHOD
43
+ end
44
+ end
45
+
46
+ hash_accessor :cache, :cache_file_enabled, :cache_file_path, :namespace, :warmer, :warming_frequency
47
+
48
+ ##
49
+ # Public instance methods
50
+ #
51
+
52
+ def cache_file
53
+ ::File.join(self[:cache_file_path], CACHE_FILENAME)
54
+ end
55
+
56
+ def cache_file_enabled?
57
+ !! ::Firebolt.config.cache_file_enabled
58
+ end
59
+
60
+ def cache_file_path=(path)
61
+ raise ArgumentError, "Directory '#{path}' does not exist or is not writable." unless ::File.writable?(path)
62
+
63
+ self[:cache_file_enabled] = true
64
+ self[:cache_file_path] = path
65
+ end
66
+
67
+ def cache_file_readable?
68
+ ::File.readable?(cache_file)
69
+ end
70
+
71
+ def namespace
72
+ @namespace ||= "firebolt.#{self[:namespace]}"
73
+ end
74
+
75
+ def use_file_warmer?
76
+ cache_file_enabled? && cache_file_readable?
77
+ end
78
+
79
+ def warmer=(value)
80
+ raise ArgumentError, "Warmer must include the ::Firebolt::Warmer module." unless value.ancestors.include?(::Firebolt::Warmer)
81
+ raise ArgumentError, "Warmer must respond to `perform`." unless value.instance_methods.include?(:perform)
82
+
83
+ self[:warmer] = value
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,34 @@
1
+ module Firebolt
2
+ class FileWarmer
3
+ include ::Firebolt::Warmer
4
+
5
+ def perform
6
+ return nil unless cache_file_exists?
7
+
8
+ return parsed_contents
9
+ end
10
+
11
+ private
12
+
13
+ def cache_file
14
+ ::Firebolt.config.cache_file
15
+ end
16
+
17
+ def cache_file_exists?
18
+ ::File.exists?(cache_file)
19
+ end
20
+
21
+ def file_contents
22
+ ::File.open(cache_file) do |file|
23
+ file.read
24
+ end
25
+ end
26
+
27
+ def parsed_contents
28
+ ::JSON.parse(file_contents)
29
+ rescue => e
30
+ warn "Could not parse #{cache_file}, falling back to default warmer."
31
+ return nil
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ module Firebolt
2
+ module Keys
3
+ def cache_key(key_suffix)
4
+ "#{namespace}.#{key_suffix}"
5
+ end
6
+
7
+ def cache_key_with_salt(key_suffix, salt)
8
+ "#{namespace}.#{salt}.#{key_suffix}"
9
+ end
10
+
11
+ def namespace
12
+ ::Firebolt.config.namespace
13
+ end
14
+
15
+ def salt_key
16
+ cache_key(:salt)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ require 'rake'
2
+ require 'fileutils'
3
+
4
+ namespace :firebolt do
5
+ namespace :cache do
6
+
7
+ desc 'Rebuild the cache by purging and re-warming'
8
+ task :rebuild do
9
+ ::Rake::Task['firebolt:cache:purge'].invoke
10
+ ::Rake::Task['firebolt:cache:warm'].invoke
11
+ puts 'Cache rebuilt'
12
+ end
13
+
14
+ desc 'Purge the cache and startup file'
15
+ task :purge do
16
+ pattern = ::Firebolt.cache_key_with_salt('*', ::Firebolt.salt)
17
+ puts "Purging keys matching pattern '#{pattern}'"
18
+ ::Firebolt.config.cache.delete_matched(pattern)
19
+
20
+ if ::Firebolt.config.cache_file_readable?
21
+ cache_file = ::Firebolt.config.cache_file
22
+ puts "Removing cache file at '#{cache_file}'"
23
+ ::FileUtils.rm(cache_file)
24
+ end
25
+ end
26
+
27
+ desc 'Warm the cache with a new salt'
28
+ task :warm do
29
+ ::Firebolt.initialize!
30
+ warmer = ::Firebolt.config.warmer
31
+ puts "Warming the cache with #{warmer}"
32
+ ::SuckerPunch::Queue[:firebolt_queue].perform(warmer)
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module Firebolt
2
+ VERSION = "0.9.0"
3
+ end
@@ -0,0 +1,51 @@
1
+ module Firebolt
2
+ module Warmer
3
+ include ::Firebolt::Keys
4
+
5
+ ##
6
+ # Public instance methods
7
+ #
8
+ def warm
9
+ results = perform
10
+
11
+ _warmer_write_results_to_cache(results)
12
+ _warmer_reset_salt!
13
+
14
+ results
15
+ end
16
+
17
+ private
18
+
19
+ ##
20
+ # Private instance methods
21
+ #
22
+ def _warmer_expires_in
23
+ @_warmer_expires_in ||= ::Firebolt.config.frequency + 1.hour
24
+ end
25
+
26
+ def _warmer_raise_failed_result
27
+ raise "Warmer must return an object that responds to #each_pair."
28
+ end
29
+
30
+ def _warmer_reset_salt!
31
+ ::Firebolt.reset_salt!(_warmer_salt)
32
+ end
33
+
34
+ def _warmer_salt
35
+ @_warmer_salt ||= ::SecureRandom.hex
36
+ end
37
+
38
+ def _warmer_salted_cache_key(suffix)
39
+ cache_key_with_salt(suffix, _warmer_salt)
40
+ end
41
+
42
+ def _warmer_write_results_to_cache(results)
43
+ _warmer_raise_failed_result unless results.respond_to?(:each_pair)
44
+
45
+ results.each_pair do |key, value|
46
+ cache_key = _warmer_salted_cache_key(key)
47
+ ::Firebolt.config.cache.write(cache_key, value, :expires_in => _warmer_expires_in)
48
+ end
49
+ end
50
+ end
51
+ end
data/lib/firebolt.rb ADDED
@@ -0,0 +1,87 @@
1
+ require "json"
2
+ require "securerandom"
3
+ require "sucker_punch"
4
+ require "rufus/scheduler"
5
+
6
+ require "firebolt/keys"
7
+ require "firebolt/cache"
8
+ require "firebolt/cache_worker"
9
+ require "firebolt/config"
10
+ require "firebolt/warmer"
11
+ require "firebolt/file_warmer"
12
+
13
+ require "firebolt/version"
14
+
15
+ module Firebolt
16
+ extend ::Firebolt::Cache
17
+
18
+ # Using a mutex to control access while creating a ::Firebolt::Config
19
+ @firebolt_mutex = ::Mutex.new
20
+
21
+ def self.config
22
+ return @config unless @config.nil?
23
+
24
+ @firebolt_mutex.synchronize do
25
+ @config = ::Firebolt::Config.new if @config.nil?
26
+ end
27
+
28
+ return @config
29
+ end
30
+
31
+ def self.configure
32
+ ::Thread.exclusive do
33
+ yield(config)
34
+ end
35
+ end
36
+
37
+ def self.configure_sucker_punch
38
+ ::SuckerPunch.config do
39
+ queue :name => :firebolt_queue, :worker => ::Firebolt::CacheWorker, :workers => 2
40
+ end
41
+ end
42
+
43
+ def self.initialize_rufus_scheduler
44
+ frequency = ::Rufus.to_time_string(config.warming_frequency)
45
+
46
+ ::Rufus::Scheduler.start_new.every(frequency) do
47
+ ::SuckerPunch::Queue[:firebolt_queue].async.perform(config.warmer)
48
+ end
49
+ end
50
+
51
+ def self.initialize!(&block)
52
+ return if initialized? || skip_warming?
53
+
54
+ configure(&block) if block_given?
55
+
56
+ raise "Firebolt.config.cache has not been set" unless config.cache
57
+ raise "Firebolt.config.warmer has not been set" unless config.warmer
58
+ raise "Firebolt.config.warming_frequency has not been set" unless config.warming_frequency
59
+
60
+ configure_sucker_punch
61
+ initialize_rufus_scheduler
62
+
63
+ # Initial warming
64
+ warmer = config.use_file_warmer? ? ::Firebolt::FileWarmer : config.warmer
65
+ ::SuckerPunch::Queue[:firebolt_queue].async.perform(warmer)
66
+
67
+ initialized!
68
+ end
69
+
70
+ def self.initialized!
71
+ return @initialized unless @initialized.nil?
72
+
73
+ @firebolt_mutex.synchronize do
74
+ @initialized = true if @initialized.nil?
75
+ end
76
+
77
+ return @initialized
78
+ end
79
+
80
+ def self.initialized?
81
+ !! @initialized
82
+ end
83
+
84
+ def self.skip_warming?
85
+ ENV['FIREBOLT_SKIP_WARMING'] || ENV['RAILS_ENV'] == 'test'
86
+ end
87
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: firebolt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Adam Hutchison
9
+ - BJ Neilsen
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-05-10 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: rufus-scheduler
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: sucker_punch
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: rake
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ description: Simple little cache warmer.
80
+ email:
81
+ - liveh2o@gmail.com
82
+ - bj.neilsen@gmail.com
83
+ executables: []
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - .gitignore
88
+ - Gemfile
89
+ - LICENSE.txt
90
+ - README.md
91
+ - Rakefile
92
+ - firebolt.gemspec
93
+ - lib/firebolt.rb
94
+ - lib/firebolt/cache.rb
95
+ - lib/firebolt/cache_worker.rb
96
+ - lib/firebolt/config.rb
97
+ - lib/firebolt/file_warmer.rb
98
+ - lib/firebolt/keys.rb
99
+ - lib/firebolt/tasks/cache.rake
100
+ - lib/firebolt/version.rb
101
+ - lib/firebolt/warmer.rb
102
+ homepage: https://github.com/moneydesktop/firebolt
103
+ licenses:
104
+ - MIT
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ segments:
116
+ - 0
117
+ hash: -3262743682020511243
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ segments:
125
+ - 0
126
+ hash: -3262743682020511243
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 1.8.24
130
+ signing_key:
131
+ specification_version: 3
132
+ summary: Firebolt is a simple cache warmer. It warms the cache using a specially defined
133
+ warmer class.
134
+ test_files: []