jqr-versionable 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +5 -0
- data/Manifest +10 -0
- data/README.rdoc +54 -0
- data/Rakefile +21 -0
- data/init.rb +1 -0
- data/lib/versionable.rb +81 -0
- data/spec/fake_cache.rb +16 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/versionable.spec +48 -0
- data/versionable.gemspec +34 -0
- metadata +77 -0
data/CHANGELOG
ADDED
data/Manifest
ADDED
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'
|
data/lib/versionable.rb
ADDED
@@ -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
|
data/spec/fake_cache.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -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
|
data/versionable.gemspec
ADDED
@@ -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
|
+
|