falls_back_on 0.0.3 → 0.1.1
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/.document +11 -0
- data/.gitignore +11 -0
- data/Gemfile +4 -0
- data/LICENSE +1 -1
- data/Rakefile +25 -0
- data/falls_back_on.gemspec +34 -0
- data/lib/falls_back_on.rb +30 -48
- data/lib/falls_back_on/definition.rb +50 -0
- data/lib/falls_back_on/definition/storage.rb +8 -0
- data/lib/falls_back_on/version.rb +3 -0
- data/test/helper.rb +40 -0
- data/test/test_falls_back_on.rb +108 -0
- metadata +78 -68
- data/lib/falls_back_on/active_record_ext.rb +0 -5
- data/lib/falls_back_on/app/models/fallback.rb +0 -35
- data/spec/lib/falls_back_on/app/models/fallback_spec.rb +0 -19
- data/spec/lib/falls_back_on_spec.rb +0 -24
- data/spec/spec_helper.rb +0 -33
data/.document
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# .document is used by rdoc and yard to know how to generate documentation
|
2
|
+
# for example, it can be used to control how rdoc gets built when you do `gem install foo`
|
3
|
+
|
4
|
+
README.rdoc
|
5
|
+
lib/**/*.rb
|
6
|
+
bin/*
|
7
|
+
|
8
|
+
# Files below this - are treated as 'extra files', and aren't parsed for ruby code
|
9
|
+
-
|
10
|
+
features/**/*.feature
|
11
|
+
LICENSE
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
CHANGED
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/testtask'
|
6
|
+
Rake::TestTask.new(:test) do |test|
|
7
|
+
test.libs << 'lib' << 'test'
|
8
|
+
test.pattern = 'test/**/test_*.rb'
|
9
|
+
test.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'rake/rdoctask'
|
14
|
+
Rake::RDocTask.new do |rdoc|
|
15
|
+
rdoc.rdoc_dir = 'rdoc'
|
16
|
+
rdoc.title = 'falls_back_on'
|
17
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
18
|
+
rdoc.rdoc_files.include('README*')
|
19
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
puts "Rdoc is not available"
|
23
|
+
end
|
24
|
+
|
25
|
+
task :default => :test
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "falls_back_on/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "falls_back_on"
|
7
|
+
s.version = FallsBackOn::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['Seamus Abshere', 'Derek Kastner']
|
10
|
+
s.email = ["seamus@abshere.net"]
|
11
|
+
s.homepage = 'http://github.com/dkastner/falls_back_on'
|
12
|
+
s.summary = 'Adds ActiveRecord::Base.falls_back_on and ActiveRecord::Base.fallback'
|
13
|
+
s.description = 'ActiveRecord extension to intelligently fall back on another column when a given column is unavailable'
|
14
|
+
|
15
|
+
s.rubyforge_project = "falls_back_on"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency 'cache_method'
|
23
|
+
s.add_dependency 'lock_method'
|
24
|
+
s.add_development_dependency 'test-unit'
|
25
|
+
s.add_development_dependency 'memcached'
|
26
|
+
s.add_development_dependency 'activerecord'
|
27
|
+
s.add_development_dependency 'sqlite3-ruby'
|
28
|
+
s.add_development_dependency 'weighted_average'
|
29
|
+
if RUBY_VERSION >= '1.9'
|
30
|
+
s.add_development_dependency 'ruby-debug19'
|
31
|
+
else
|
32
|
+
s.add_development_dependency 'ruby-debug'
|
33
|
+
end
|
34
|
+
end
|
data/lib/falls_back_on.rb
CHANGED
@@ -1,54 +1,36 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class_eval do
|
4
|
-
class_inheritable_accessor :fallback_options
|
5
|
-
end
|
6
|
-
self.fallback_options = options
|
7
|
-
extend FallsBackOn::InstanceMethods
|
8
|
-
end
|
1
|
+
require 'cache_method'
|
2
|
+
require 'lock_method'
|
9
3
|
|
10
|
-
|
11
|
-
def class_name
|
12
|
-
self.to_s
|
13
|
-
end
|
4
|
+
require 'falls_back_on/version'
|
14
5
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
def fallback_value_for(k)
|
20
|
-
v = fallback_options[k]
|
21
|
-
case v
|
22
|
-
when :random
|
23
|
-
fallback_value_for_random(k)
|
24
|
-
when Numeric
|
25
|
-
v
|
26
|
-
when String
|
27
|
-
v.upcase.starts_with?('SELECT') ? ActiveRecord::Base.connection.select_value(v) : v
|
28
|
-
when Proc
|
29
|
-
v.call
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def fallback_value_for_name
|
34
|
-
{ :name => "Fallback #{class_name.underscore.gsub('_', ' ')}" }
|
35
|
-
end
|
36
|
-
|
37
|
-
def fallback_value_for_random(k)
|
38
|
-
random = rand(1e8)
|
39
|
-
return fallback_value_for_random(k) unless send("find_by_#{k}", random).nil?
|
40
|
-
random
|
41
|
-
end
|
42
|
-
|
43
|
-
def destroy_fallback
|
44
|
-
Fallback.destroy_for(class_name)
|
45
|
-
end
|
6
|
+
module FallsBackOn
|
7
|
+
autoload :Definition, 'falls_back_on/definition'
|
46
8
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
9
|
+
def self.clear
|
10
|
+
::FallsBackOn::Definition.all.each { |definition| definition.clear }
|
11
|
+
end
|
12
|
+
|
13
|
+
def falls_back_on(attrs)
|
14
|
+
definition = ::FallsBackOn::Definition.new self
|
15
|
+
definition.attrs = attrs
|
16
|
+
end
|
17
|
+
|
18
|
+
def fallback
|
19
|
+
obj = new
|
20
|
+
definition = ::FallsBackOn::Definition.new self
|
21
|
+
begin
|
22
|
+
definition.attrs.each do |k, v|
|
23
|
+
obj.send "#{k}=", v
|
24
|
+
end
|
25
|
+
rescue ::LockMethod::Locked
|
26
|
+
$stderr.puts "#{self.to_s} was locked, retrying in 0.5 seconds..."
|
27
|
+
sleep 0.5
|
28
|
+
retry
|
52
29
|
end
|
30
|
+
obj
|
53
31
|
end
|
54
32
|
end
|
33
|
+
|
34
|
+
unless ::Class.method_defined?(:falls_back_on)
|
35
|
+
::Class.send :include, ::FallsBackOn
|
36
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module FallsBackOn
|
2
|
+
class Definition
|
3
|
+
autoload :Storage, 'falls_back_on/definition/storage'
|
4
|
+
|
5
|
+
def self.all
|
6
|
+
Storage.instance.keys.map { |parent| new parent }
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :parent
|
10
|
+
|
11
|
+
def initialize(parent)
|
12
|
+
@parent = parent.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear
|
16
|
+
clear_method_cache :calculate
|
17
|
+
clear_method_cache :attrs
|
18
|
+
clear_method_lock :attrs
|
19
|
+
end
|
20
|
+
|
21
|
+
def attrs=(attrs)
|
22
|
+
Storage.instance[parent] = attrs
|
23
|
+
end
|
24
|
+
|
25
|
+
def attrs
|
26
|
+
Storage.instance[parent].inject({}) do |memo, (k, v)|
|
27
|
+
memo[k] = calculate k
|
28
|
+
memo
|
29
|
+
end
|
30
|
+
end
|
31
|
+
lock_method :attrs
|
32
|
+
cache_method :attrs
|
33
|
+
|
34
|
+
def calculate(k)
|
35
|
+
v = Storage.instance[parent][k]
|
36
|
+
case v
|
37
|
+
when Proc
|
38
|
+
v.call
|
39
|
+
else
|
40
|
+
v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
cache_method :calculate
|
44
|
+
|
45
|
+
# for cache_method and lock_method
|
46
|
+
def hash
|
47
|
+
parent.hash
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup
|
4
|
+
require 'test/unit'
|
5
|
+
require 'active_record'
|
6
|
+
require 'active_support/all'
|
7
|
+
require 'weighted_average'
|
8
|
+
require 'memcached'
|
9
|
+
require 'timeout'
|
10
|
+
require 'ruby-debug'
|
11
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
12
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
13
|
+
require 'falls_back_on'
|
14
|
+
|
15
|
+
# thanks authlogic!
|
16
|
+
ActiveRecord::Schema.verbose = false
|
17
|
+
begin
|
18
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
19
|
+
rescue ArgumentError
|
20
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
|
21
|
+
end
|
22
|
+
|
23
|
+
my_client = Memcached.new
|
24
|
+
CacheMethod.config.storage = my_client
|
25
|
+
LockMethod.config.storage = my_client
|
26
|
+
|
27
|
+
class Test::Unit::TestCase
|
28
|
+
def setup
|
29
|
+
CacheMethod.config.storage.flush
|
30
|
+
LockMethod.config.storage.flush
|
31
|
+
@old_abort_on_exception = Thread.abort_on_exception
|
32
|
+
Thread.abort_on_exception = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def teardown
|
36
|
+
Thread.abort_on_exception = @old_abort_on_exception
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
ENV['FALLS_BACK_ON_DEBUG'] = 'true'
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class Car1
|
4
|
+
attr_accessor :fuel
|
5
|
+
attr_accessor :random_number
|
6
|
+
attr_accessor :some_place_in_the_mall
|
7
|
+
falls_back_on :fuel => 'gasoline',
|
8
|
+
:random_number => lambda { rand },
|
9
|
+
:some_place_in_the_mall => lambda { some_place_in_the_mall }
|
10
|
+
def self.some_place_in_the_mall
|
11
|
+
"Orange Julius at #{Time.now}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Car2 < ActiveRecord::Base
|
16
|
+
set_table_name :cars2
|
17
|
+
def self.create_table
|
18
|
+
connection.create_table :cars2, :force => true do |t|
|
19
|
+
t.string :fuel
|
20
|
+
t.float :efficiency
|
21
|
+
t.integer :popularity
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :efficiency2
|
26
|
+
|
27
|
+
falls_back_on :fuel => 'diesel',
|
28
|
+
:efficiency => lambda { calculate(:average, :efficiency) },
|
29
|
+
:efficiency2 => lambda { weighted_average(:efficiency, :weighted_by => :popularity ) }
|
30
|
+
end
|
31
|
+
|
32
|
+
Car2.create_table
|
33
|
+
Car2.create! :efficiency => 10.0, :popularity => 1_000_000
|
34
|
+
Car2.create! :efficiency => 1.0, :popularity => 1
|
35
|
+
|
36
|
+
class Car3
|
37
|
+
attr_accessor :long_running_calculation
|
38
|
+
falls_back_on :long_running_calculation => lambda { sleep 3 }
|
39
|
+
end
|
40
|
+
|
41
|
+
class TestFallsBackOn < Test::Unit::TestCase
|
42
|
+
def test_fallback
|
43
|
+
assert_equal 'gasoline', Car1.fallback.fuel
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_active_record
|
47
|
+
assert_equal 'diesel', Car2.fallback.fuel
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_proc
|
51
|
+
assert Car1.fallback.random_number.is_a?(Numeric)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_proc_calling_class_method
|
55
|
+
assert(Car1.fallback.some_place_in_the_mall =~ /Orange Julius/)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_active_record_calculations
|
59
|
+
assert_equal 5.5, Car2.fallback.efficiency
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_weighted_average
|
63
|
+
assert_equal 10, Car2.fallback.efficiency2.round
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_cache
|
67
|
+
random_number = Car1.fallback.random_number
|
68
|
+
assert_equal random_number, Car1.fallback.random_number
|
69
|
+
assert_equal random_number, Car1.fallback.random_number
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_retries_if_lock
|
73
|
+
blocker = Thread.new { Car3.fallback.long_running_calculation }
|
74
|
+
|
75
|
+
assert_raises(::Timeout::Error) do
|
76
|
+
Timeout.timeout(2) do
|
77
|
+
Car3.fallback.long_running_calculation
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
blocker.join
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_caches_calculations_separately
|
85
|
+
definition = ::FallsBackOn::Definition.new Car3
|
86
|
+
definition.calculate :long_running_calculation
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_clear
|
90
|
+
# sanity check
|
91
|
+
assert_nothing_raised do
|
92
|
+
Timeout.timeout(4) do
|
93
|
+
blocker = Thread.new { Car3.fallback.long_running_calculation }
|
94
|
+
sleep 1
|
95
|
+
Car3.fallback.long_running_calculation
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
assert_raises(Timeout::Error) do
|
100
|
+
Timeout.timeout(4) do
|
101
|
+
blocker = Thread.new { Car3.fallback.long_running_calculation }
|
102
|
+
sleep 1
|
103
|
+
clearer = Thread.new { ::FallsBackOn.clear }
|
104
|
+
Car3.fallback.long_running_calculation
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
metadata
CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Seamus Abshere
|
@@ -16,14 +16,13 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date:
|
19
|
+
date: 2011-02-24 00:00:00 -06:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
23
|
-
|
23
|
+
name: cache_method
|
24
24
|
prerelease: false
|
25
|
-
|
26
|
-
version_requirements: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
26
|
none: false
|
28
27
|
requirements:
|
29
28
|
- - ">="
|
@@ -32,12 +31,12 @@ dependencies:
|
|
32
31
|
segments:
|
33
32
|
- 0
|
34
33
|
version: "0"
|
35
|
-
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
|
-
|
37
|
+
name: lock_method
|
38
38
|
prerelease: false
|
39
|
-
|
40
|
-
version_requirements: &id002 !ruby/object:Gem::Requirement
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
40
|
none: false
|
42
41
|
requirements:
|
43
42
|
- - ">="
|
@@ -46,12 +45,12 @@ dependencies:
|
|
46
45
|
segments:
|
47
46
|
- 0
|
48
47
|
version: "0"
|
49
|
-
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
50
|
- !ruby/object:Gem::Dependency
|
51
|
-
|
51
|
+
name: test-unit
|
52
52
|
prerelease: false
|
53
|
-
|
54
|
-
version_requirements: &id003 !ruby/object:Gem::Requirement
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
54
|
none: false
|
56
55
|
requirements:
|
57
56
|
- - ">="
|
@@ -60,12 +59,12 @@ dependencies:
|
|
60
59
|
segments:
|
61
60
|
- 0
|
62
61
|
version: "0"
|
63
|
-
requirement: *id003
|
64
|
-
- !ruby/object:Gem::Dependency
|
65
62
|
type: :development
|
63
|
+
version_requirements: *id003
|
64
|
+
- !ruby/object:Gem::Dependency
|
65
|
+
name: memcached
|
66
66
|
prerelease: false
|
67
|
-
|
68
|
-
version_requirements: &id004 !ruby/object:Gem::Requirement
|
67
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
68
|
none: false
|
70
69
|
requirements:
|
71
70
|
- - ">="
|
@@ -74,12 +73,12 @@ dependencies:
|
|
74
73
|
segments:
|
75
74
|
- 0
|
76
75
|
version: "0"
|
77
|
-
requirement: *id004
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
76
|
type: :development
|
77
|
+
version_requirements: *id004
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: activerecord
|
80
80
|
prerelease: false
|
81
|
-
|
82
|
-
version_requirements: &id005 !ruby/object:Gem::Requirement
|
81
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
83
82
|
none: false
|
84
83
|
requirements:
|
85
84
|
- - ">="
|
@@ -88,69 +87,81 @@ dependencies:
|
|
88
87
|
segments:
|
89
88
|
- 0
|
90
89
|
version: "0"
|
91
|
-
|
90
|
+
type: :development
|
91
|
+
version_requirements: *id005
|
92
92
|
- !ruby/object:Gem::Dependency
|
93
|
+
name: sqlite3-ruby
|
94
|
+
prerelease: false
|
95
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
93
104
|
type: :development
|
105
|
+
version_requirements: *id006
|
106
|
+
- !ruby/object:Gem::Dependency
|
107
|
+
name: weighted_average
|
94
108
|
prerelease: false
|
95
|
-
|
96
|
-
version_requirements: &id006 !ruby/object:Gem::Requirement
|
109
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
97
110
|
none: false
|
98
111
|
requirements:
|
99
112
|
- - ">="
|
100
113
|
- !ruby/object:Gem::Version
|
101
|
-
hash:
|
114
|
+
hash: 3
|
102
115
|
segments:
|
103
|
-
- 2
|
104
116
|
- 0
|
117
|
+
version: "0"
|
118
|
+
type: :development
|
119
|
+
version_requirements: *id007
|
120
|
+
- !ruby/object:Gem::Dependency
|
121
|
+
name: ruby-debug
|
122
|
+
prerelease: false
|
123
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
hash: 3
|
129
|
+
segments:
|
105
130
|
- 0
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
Description goes here.
|
114
|
-
|
115
|
-
== Note on Patches/Pull Requests
|
116
|
-
|
117
|
-
* Fork the project.
|
118
|
-
* Make your feature addition or bug fix.
|
119
|
-
* Add tests for it. This is important so I don't break it in a
|
120
|
-
future version unintentionally.
|
121
|
-
* Commit, do not mess with rakefile, version, or history.
|
122
|
-
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
123
|
-
* Send me a pull request. Bonus points for topic branches.
|
124
|
-
|
125
|
-
== Copyright
|
126
|
-
|
127
|
-
Copyright (c) 2010 Derek Kastner. See LICENSE for details.
|
128
|
-
|
129
|
-
email: seamus@brighterplanet.com
|
131
|
+
version: "0"
|
132
|
+
type: :development
|
133
|
+
version_requirements: *id008
|
134
|
+
description: ActiveRecord extension to intelligently fall back on another column when a given column is unavailable
|
135
|
+
email:
|
136
|
+
- seamus@abshere.net
|
130
137
|
executables: []
|
131
138
|
|
132
139
|
extensions: []
|
133
140
|
|
134
|
-
extra_rdoc_files:
|
141
|
+
extra_rdoc_files: []
|
142
|
+
|
143
|
+
files:
|
144
|
+
- .document
|
145
|
+
- .gitignore
|
146
|
+
- Gemfile
|
135
147
|
- LICENSE
|
136
148
|
- README.rdoc
|
137
|
-
|
149
|
+
- Rakefile
|
150
|
+
- falls_back_on.gemspec
|
138
151
|
- lib/falls_back_on.rb
|
139
|
-
- lib/falls_back_on/
|
140
|
-
- lib/falls_back_on/
|
152
|
+
- lib/falls_back_on/definition.rb
|
153
|
+
- lib/falls_back_on/definition/storage.rb
|
154
|
+
- lib/falls_back_on/version.rb
|
141
155
|
- rails/init.rb
|
142
|
-
-
|
143
|
-
-
|
144
|
-
- spec/lib/falls_back_on/app/models/fallback_spec.rb
|
145
|
-
- spec/lib/falls_back_on_spec.rb
|
146
|
-
- spec/spec_helper.rb
|
156
|
+
- test/helper.rb
|
157
|
+
- test/test_falls_back_on.rb
|
147
158
|
has_rdoc: true
|
148
159
|
homepage: http://github.com/dkastner/falls_back_on
|
149
160
|
licenses: []
|
150
161
|
|
151
162
|
post_install_message:
|
152
|
-
rdoc_options:
|
153
|
-
|
163
|
+
rdoc_options: []
|
164
|
+
|
154
165
|
require_paths:
|
155
166
|
- lib
|
156
167
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -173,12 +184,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
173
184
|
version: "0"
|
174
185
|
requirements: []
|
175
186
|
|
176
|
-
rubyforge_project:
|
187
|
+
rubyforge_project: falls_back_on
|
177
188
|
rubygems_version: 1.3.7
|
178
189
|
signing_key:
|
179
190
|
specification_version: 3
|
180
|
-
summary: ActiveRecord
|
191
|
+
summary: Adds ActiveRecord::Base.falls_back_on and ActiveRecord::Base.fallback
|
181
192
|
test_files:
|
182
|
-
-
|
183
|
-
-
|
184
|
-
- spec/spec_helper.rb
|
193
|
+
- test/helper.rb
|
194
|
+
- test/test_falls_back_on.rb
|
@@ -1,35 +0,0 @@
|
|
1
|
-
class Fallback < ActiveRecord::Base
|
2
|
-
index :name if respond_to? :index
|
3
|
-
|
4
|
-
serialize :values
|
5
|
-
|
6
|
-
class << self
|
7
|
-
def get_for(name)
|
8
|
-
klass = name.constantize
|
9
|
-
if fallback = find_by_name(name)
|
10
|
-
begin
|
11
|
-
f = klass.new(fallback.values)
|
12
|
-
rescue ActiveRecord::UnknownAttributeError
|
13
|
-
raise $!, "while trying to use #{fallback.values.inspect} with #{name} #{$!.inspect}"
|
14
|
-
end
|
15
|
-
f.readonly!
|
16
|
-
f
|
17
|
-
elsif klass.set_fallback
|
18
|
-
get_for(name)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def set_for(name, values = {})
|
23
|
-
raise "fallback race condition" if Fallback.count > 100
|
24
|
-
fallback = find_or_create_by_name(name)
|
25
|
-
fallback.update_attributes(:values => values)
|
26
|
-
true
|
27
|
-
end
|
28
|
-
|
29
|
-
def destroy_for(name)
|
30
|
-
fallback = find_by_name(name.to_s)
|
31
|
-
fallback.destroy unless fallback.nil?
|
32
|
-
true
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
class Arcade < ActiveRecord::Base
|
4
|
-
attr_accessor :pinball_count, :video_game_count
|
5
|
-
|
6
|
-
falls_back_on :pinball_count => 73
|
7
|
-
end
|
8
|
-
|
9
|
-
describe Fallback do
|
10
|
-
describe '.destroy_for' do
|
11
|
-
it 'should destroy a fallback' do
|
12
|
-
Fallback.create :name => 'Arcade'
|
13
|
-
expect do
|
14
|
-
Fallback.destroy_for Arcade
|
15
|
-
end.should change(Fallback, :count).by(-1)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
class State < ActiveRecord::Base
|
4
|
-
falls_back_on :area => 10000
|
5
|
-
end
|
6
|
-
|
7
|
-
describe FallsBackOn do
|
8
|
-
describe '.class_name' do
|
9
|
-
it 'should return the class name' do
|
10
|
-
State.class_name.should == 'State'
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '.set_fallback' do
|
15
|
-
it 'should create a Fallback for a class' do
|
16
|
-
State.set_fallback
|
17
|
-
|
18
|
-
fallback = Fallback.find_by_name 'State'
|
19
|
-
fallback.should be_an_instance_of(Fallback)
|
20
|
-
fallback.values.should == {:area => 10000 }
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
data/spec/spec_helper.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler'
|
3
|
-
begin
|
4
|
-
Bundler.setup
|
5
|
-
rescue Bundler::BundlerError => e
|
6
|
-
$stderr.puts e.message
|
7
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
-
exit e.status_code
|
9
|
-
end
|
10
|
-
|
11
|
-
require 'active_record'
|
12
|
-
require 'logger'
|
13
|
-
|
14
|
-
$:.unshift File.expand_path('../lib', __FILE__)
|
15
|
-
require 'falls_back_on'
|
16
|
-
require 'falls_back_on/active_record_ext'
|
17
|
-
require 'falls_back_on/app/models/fallback'
|
18
|
-
|
19
|
-
ActiveRecord::Base.logger = Logger.new nil
|
20
|
-
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
21
|
-
|
22
|
-
ActiveRecord::Schema.define(:version => 1) do
|
23
|
-
create_table "fallbacks", :force => true do |t|
|
24
|
-
t.string "name"
|
25
|
-
t.text "values"
|
26
|
-
t.datetime "created_at"
|
27
|
-
t.datetime "updated_at"
|
28
|
-
end
|
29
|
-
|
30
|
-
create_table 'states' do |t|
|
31
|
-
t.integer 'area'
|
32
|
-
end
|
33
|
-
end
|