jqr-versionable 0.0.3

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/CHANGELOG ADDED
@@ -0,0 +1,5 @@
1
+ v0.0.3. New standardized usage with per-model options.
2
+
3
+ v0.0.2. Initial gem release.
4
+
5
+ v0.0.1. Initial release.
data/Manifest ADDED
@@ -0,0 +1,10 @@
1
+ CHANGELOG
2
+ init.rb
3
+ lib/versionable.rb
4
+ Manifest
5
+ Rakefile
6
+ README.rdoc
7
+ spec/fake_cache.rb
8
+ spec/spec_helper.rb
9
+ spec/versionable.spec
10
+ versionable.gemspec
data/README.rdoc ADDED
@@ -0,0 +1,54 @@
1
+ = Versionable
2
+
3
+ Super fast and easy versioning for models and associations. Do cache lookups
4
+ using this fast version number instead of managing complex cache expiry.
5
+
6
+ Versionable is intended for versioning of a model or an association and doesn't
7
+ currently provide any means of caching version numbers for normal model
8
+ instances.
9
+
10
+ == Examples
11
+
12
+ # A simple User model with a User.cached_all method that returns all users
13
+ # in the application. This method is cached and will automatically "expire"
14
+ # whenever a record is saved or you call User.new_version.
15
+
16
+ class User < ActiveRecord::Base
17
+ versionable :cache => Rails.cache
18
+
19
+ after_save { new_version }
20
+
21
+ def self.cached_all
22
+ # reference version in cache keys
23
+ Rails.cache.fetch("User.cached_all:#{version}") { all }
24
+ end
25
+ end
26
+
27
+ >> User.cached_all # initial cache miss and cache fill
28
+ >> User.cached_all # cache hit
29
+
30
+ >> User.new_version # => 1224985243
31
+ >> User.cached_all # new cache miss and cache fill
32
+
33
+ >> User.first.save # causing new_version to be called from after_save
34
+ >> User.cached_all # new cache miss and cache fill
35
+
36
+
37
+ You can also use it on associations...
38
+
39
+
40
+ == Install
41
+
42
+ As a Rails plugin.
43
+
44
+ ./script/plugin install git://github.com/jqr/versionable.git
45
+
46
+ Prefer gems? Add this to your environment.rb and run the following command.
47
+
48
+ config.gem 'jqr-versionable', :lib => 'versionable', :source => 'http://gems.github.com'
49
+
50
+ $ rake gems:install
51
+
52
+
53
+ Homepage:: http://github.com/jqr/versionable/tree/master
54
+ License:: Copyright (c) 2008 Elijah Miller <mailto:elijah.miller@gmail.com>, released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'spec/rake/spectask'
2
+
3
+ require 'echoe'
4
+ Echoe.new 'versionable' do |p|
5
+ p.description = "Simple versioning for Ruby objects using only the Rails cache."
6
+ # p.url = "http://versionable.rubyforge.org"
7
+ p.author = "Elijah Miller"
8
+ p.email = "elijah.miller@gmail.com"
9
+ p.retain_gemspec = true
10
+ p.need_tar_gz = false
11
+ p.extra_deps = [
12
+ ]
13
+ end
14
+
15
+ desc 'Default: run specs'
16
+ task :default => :spec
17
+ Spec::Rake::SpecTask.new do |t|
18
+ t.spec_files = FileList["spec/**/*_spec.rb"]
19
+ end
20
+
21
+ task :test => :spec
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'versionable'
@@ -0,0 +1,81 @@
1
+ class Object
2
+ # For extending models and assocations to provide an time based version number
3
+ # for cache expiration by changing part of the key we lookup the cache by.
4
+ def versionable(options = {})
5
+ default_options = {
6
+ # :cache => Rails.cache,
7
+ :ttl => 0
8
+ }
9
+ instance_eval "
10
+ class << self
11
+ attr_accessor :versionable_options
12
+ end
13
+ "
14
+ self.versionable_options = default_options.merge(options)
15
+
16
+ include Versionable
17
+ end
18
+ end
19
+
20
+ module Versionable
21
+
22
+ def self.included(base)
23
+ base.extend ClassMethods
24
+ end
25
+
26
+ module ClassMethods
27
+ # Returns the current version of the assocation. Likely to be nil.
28
+ def version
29
+ versionable_options[:cache].read(version_cache_key)
30
+ end
31
+
32
+ # Sets the version of this assocation to the current time in integer form.
33
+ def new_version
34
+ # the expiry needs to be longer than any page that might use this as a
35
+ # cache key.
36
+ time = Time.now.to_i
37
+ versionable_options[:cache].write(version_cache_key, time, :expires_in => versionable_options[:ttl])
38
+ time
39
+ end
40
+
41
+ # Returns the association version key.
42
+ def version_cache_key
43
+ parts =
44
+ if object.class == Class
45
+ [object]
46
+ else
47
+ [object.class, object.id]
48
+ end
49
+
50
+ parts << version_name
51
+ parts * ':'
52
+ end
53
+
54
+ # Returns the object that version applies to.
55
+ def object
56
+ if inside_association?
57
+ proxy_owner
58
+ else
59
+ self
60
+ end
61
+ end
62
+
63
+ # Returns the name portion of the version cache key
64
+ def version_name
65
+ if inside_association?
66
+ "#{proxy_reflection.name}_version"
67
+ else
68
+ "version"
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ # Returns true if this is called with the context available to an
75
+ # assocation.
76
+ def inside_association?
77
+ # TODO: see if we can memoize
78
+ self.respond_to?(:proxy_owner)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,16 @@
1
+ class FakeCache
2
+ attr_accessor :store
3
+
4
+ def initialize
5
+ self.store = {}
6
+ end
7
+
8
+ def read(key)
9
+ store[key]
10
+ end
11
+
12
+ def write(key, value, options = {})
13
+ store[key] = value
14
+ end
15
+
16
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ $: << File.dirname(__FILE__)
5
+ require 'fake_cache'
6
+
7
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'versionable')
8
+
@@ -0,0 +1,48 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Versionable do
4
+ describe "initializing" do
5
+ it "should allow initializing without any options" do
6
+ lambda {
7
+ class DefaultListy
8
+ versionable
9
+ end
10
+ }.should_not raise_error
11
+ end
12
+
13
+ it "should store options in Class.versionable_options" do
14
+ class TTLListy
15
+ versionable :ttl => 60
16
+ end
17
+
18
+ TTLListy.versionable_options.should == { :ttl => 60 }
19
+ end
20
+
21
+ end
22
+
23
+ describe "versioning" do
24
+ before(:each) do
25
+ class Listy
26
+ versionable :cache => FakeCache.new, :ttl => 30
27
+ end
28
+ @cache = Listy.versionable_options[:cache]
29
+ @ttl = Listy.versionable_options[:ttl]
30
+ end
31
+
32
+ it "should start off with a nil version" do
33
+ Listy.version.should == nil
34
+ end
35
+
36
+ it "should have a version after calling new_version" do
37
+ Listy.new_version
38
+ Listy.new_version.should_not be_nil
39
+ end
40
+
41
+ it "should set the version with the TTL from the options" do
42
+ @cache.should_receive(:write).with(anything, anything, hash_including(:expires_in => 30))
43
+ Listy.new_version
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{versionable}
5
+ s.version = "0.0.3"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Elijah Miller"]
9
+ s.date = %q{2009-02-09}
10
+ s.description = %q{Simple versioning for Ruby objects using only the Rails cache.}
11
+ s.email = %q{elijah.miller@gmail.com}
12
+ s.extra_rdoc_files = ["CHANGELOG", "lib/versionable.rb", "README.rdoc"]
13
+ s.files = ["CHANGELOG", "init.rb", "lib/versionable.rb", "Manifest", "Rakefile", "README.rdoc", "spec/fake_cache.rb", "spec/spec_helper.rb", "spec/versionable.spec", "versionable.gemspec"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Versionable", "--main", "README.rdoc"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{versionable}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{Simple versioning for Ruby objects using only the Rails cache.}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_development_dependency(%q<echoe>, [">= 0"])
28
+ else
29
+ s.add_dependency(%q<echoe>, [">= 0"])
30
+ end
31
+ else
32
+ s.add_dependency(%q<echoe>, [">= 0"])
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jqr-versionable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Elijah Miller
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-09 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: echoe
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ description: Simple versioning for Ruby objects using only the Rails cache.
25
+ email: elijah.miller@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - CHANGELOG
32
+ - lib/versionable.rb
33
+ - README.rdoc
34
+ files:
35
+ - CHANGELOG
36
+ - init.rb
37
+ - lib/versionable.rb
38
+ - Manifest
39
+ - Rakefile
40
+ - README.rdoc
41
+ - spec/fake_cache.rb
42
+ - spec/spec_helper.rb
43
+ - spec/versionable.spec
44
+ - versionable.gemspec
45
+ has_rdoc: true
46
+ homepage: ""
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --line-numbers
50
+ - --inline-source
51
+ - --title
52
+ - Versionable
53
+ - --main
54
+ - README.rdoc
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "1.2"
68
+ version:
69
+ requirements: []
70
+
71
+ rubyforge_project: versionable
72
+ rubygems_version: 1.2.0
73
+ signing_key:
74
+ specification_version: 2
75
+ summary: Simple versioning for Ruby objects using only the Rails cache.
76
+ test_files: []
77
+