rails_current 1.0.0 → 1.1.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/README.md +35 -6
- data/lib/rails_current.rb +70 -14
- data/rails_current.gemspec +5 -2
- data/test/rails_current_test.rb +130 -0
- data/test/testing.rb +197 -0
- metadata +4 -2
data/README.md
CHANGED
@@ -2,23 +2,52 @@
|
|
2
2
|
NAME
|
3
3
|
--------------------------------
|
4
4
|
rails_current
|
5
|
-
|
5
|
+
|
6
|
+
--------------------------------
|
7
|
+
DESCRIPTION
|
8
|
+
--------------------------------
|
9
|
+
|
10
|
+
track 'current_user' et all in a tidy, global, and thread-safe fashion.
|
6
11
|
|
7
12
|
|
8
13
|
--------------------------------
|
9
14
|
SYNOPSIS
|
10
15
|
--------------------------------
|
11
|
-
most rails apps scatter a bunch of @
|
12
|
-
that. do
|
16
|
+
most rails apps scatter a bunch of @current_foobar vars everywhere. don't do
|
17
|
+
that. it's fugly. instead, do this.
|
18
|
+
|
19
|
+
class ApplicationController
|
20
|
+
|
21
|
+
Current(:user){ User.find session[:current_user }
|
22
|
+
Current(:account)
|
23
|
+
|
24
|
+
include Current
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
...
|
13
29
|
|
14
|
-
Current.user = ...
|
15
30
|
|
16
|
-
if
|
31
|
+
if current_user
|
32
|
+
|
33
|
+
...
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
self.current_account = Account.find(id)
|
17
39
|
|
18
|
-
if Current.account
|
19
40
|
|
20
41
|
etc.
|
21
42
|
|
22
43
|
out of the box it's loaded with Current.controller
|
23
44
|
|
45
|
+
--------------------------------
|
46
|
+
INSTALL
|
47
|
+
--------------------------------
|
48
|
+
|
49
|
+
gem install rails-current
|
50
|
+
|
51
|
+
|
24
52
|
gem 'rails-current', :require => 'current'
|
53
|
+
bundle install
|
data/lib/rails_current.rb
CHANGED
@@ -1,31 +1,87 @@
|
|
1
|
-
require '
|
1
|
+
require 'map'
|
2
2
|
|
3
3
|
module Current
|
4
|
-
def Current.version() '1.
|
4
|
+
def Current.version() '1.1.0' end
|
5
|
+
|
6
|
+
def Current.data
|
7
|
+
Thread.current[:current] ||= Map.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def Current.clear
|
11
|
+
data.clear
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def Current.reset
|
16
|
+
calls.keys.each{|name, block| undefine_attribute_method(name) }
|
17
|
+
data.clear
|
18
|
+
calls.clear
|
19
|
+
self
|
20
|
+
end
|
5
21
|
|
6
22
|
def Current.attribute(name, *args, &block)
|
23
|
+
options = Map.options_for(args)
|
24
|
+
|
7
25
|
name = name.to_s
|
8
|
-
|
9
|
-
|
26
|
+
default = options.has_key?(:default) ? options[:default] : args.shift
|
27
|
+
|
28
|
+
block ||= proc{ default }
|
29
|
+
calls[name] = block
|
30
|
+
|
31
|
+
define_attribute_method(name)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def Current.calls
|
36
|
+
@calls ||= Map.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def Current.define_attribute_method(name)
|
40
|
+
unless respond_to?(name)
|
41
|
+
singleton_class.module_eval do
|
42
|
+
define_method(name) do
|
43
|
+
if data.has_key?(name)
|
44
|
+
data[name]
|
45
|
+
else
|
46
|
+
data[name] = calls[name].call
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
define_method(name + '=') do |value|
|
51
|
+
data[name] = value
|
52
|
+
end
|
53
|
+
|
54
|
+
define_method(name + '?') do |value|
|
55
|
+
data[name]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def Current.undefine_attribute_method(name)
|
62
|
+
if respond_to?(name)
|
63
|
+
singleton_class.module_eval do
|
64
|
+
remove_method(name)
|
65
|
+
remove_method(name + '=')
|
66
|
+
remove_method(name + '?')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def Current.singleton_class
|
72
|
+
@singleton_class ||= class << self; self; end
|
10
73
|
end
|
11
74
|
|
12
75
|
def Current.attribute?(name)
|
13
|
-
|
76
|
+
calls.has_key?(name)
|
14
77
|
end
|
15
78
|
|
16
79
|
def Current.attribute_names
|
17
|
-
|
80
|
+
calls.keys
|
18
81
|
end
|
19
82
|
|
20
83
|
def Current.attributes
|
21
|
-
attribute_names.inject(
|
22
|
-
end
|
23
|
-
|
24
|
-
def Current.clear
|
25
|
-
attribute_names.each do |name|
|
26
|
-
ivar = "@#{ name }"
|
27
|
-
remove_instance_variable(ivar) if instance_variable_defined?(ivar)
|
28
|
-
end
|
84
|
+
attribute_names.inject(Map.new){|map, name| map.update(name => send(name))}
|
29
85
|
end
|
30
86
|
|
31
87
|
def Current.method_missing(method, *args, &block)
|
data/rails_current.gemspec
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
Gem::Specification::new do |spec|
|
5
5
|
spec.name = "rails_current"
|
6
|
-
spec.version = "1.
|
6
|
+
spec.version = "1.1.0"
|
7
7
|
spec.platform = Gem::Platform::RUBY
|
8
8
|
spec.summary = "rails_current"
|
9
9
|
spec.description = "description: rails_current kicks the ass"
|
@@ -13,7 +13,10 @@ Gem::Specification::new do |spec|
|
|
13
13
|
"Rakefile",
|
14
14
|
"lib",
|
15
15
|
"lib/rails_current.rb",
|
16
|
-
"rails_current.gemspec"
|
16
|
+
"rails_current.gemspec",
|
17
|
+
"test",
|
18
|
+
"test/rails_current_test.rb",
|
19
|
+
"test/testing.rb"]
|
17
20
|
|
18
21
|
spec.executables = []
|
19
22
|
|
@@ -0,0 +1,130 @@
|
|
1
|
+
|
2
|
+
Testing Current do
|
3
|
+
|
4
|
+
##
|
5
|
+
#
|
6
|
+
test 'Current attributes can be declared by name' do
|
7
|
+
assert{ Current.attribute :foo }
|
8
|
+
assert{ Current.foo.nil? }
|
9
|
+
end
|
10
|
+
|
11
|
+
##
|
12
|
+
#
|
13
|
+
test 'Current attributes can be declared by name with a default' do
|
14
|
+
assert{ Current.attribute :foo, 42 }
|
15
|
+
assert{ Current.foo == 42 }
|
16
|
+
|
17
|
+
assert{ Current.attribute :bar, :default => 42.0 }
|
18
|
+
assert{ Current.bar == 42.0 }
|
19
|
+
|
20
|
+
assert{ Current.attribute(:foobar){ 'forty-two' } }
|
21
|
+
assert{ Current.foobar == 'forty-two' }
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
#
|
26
|
+
test 'Current attributes can be retrieved en mass' do
|
27
|
+
assert{ Current.attributes =~ {} }
|
28
|
+
assert{ Current.attribute :foo, 42 }
|
29
|
+
assert{ Current.attribute :bar, 42.0 }
|
30
|
+
assert{ Current.attributes =~ {:foo => 42, :bar => 42.0} }
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
#
|
35
|
+
test 'that Current attributes are thread safe' do
|
36
|
+
assert{ Current.attribute :foo }
|
37
|
+
assert{ Current.attribute :bar }
|
38
|
+
|
39
|
+
require 'thread'
|
40
|
+
ra = Queue.new
|
41
|
+
rb = Queue.new
|
42
|
+
wa = Queue.new
|
43
|
+
wb = Queue.new
|
44
|
+
|
45
|
+
a = Thread.new do
|
46
|
+
Thread.current.abort_on_exception = true
|
47
|
+
id = Thread.current.object_id
|
48
|
+
Current.foo = 40
|
49
|
+
Current.bar = 2
|
50
|
+
ra.push(id)
|
51
|
+
wa.pop()
|
52
|
+
ra.push( Current.attributes )
|
53
|
+
end
|
54
|
+
|
55
|
+
b = Thread.new do
|
56
|
+
Thread.current.abort_on_exception = true
|
57
|
+
id = Thread.current.object_id
|
58
|
+
Current.foo = 'forty'
|
59
|
+
Current.bar = 'two'
|
60
|
+
rb.push(id)
|
61
|
+
wb.pop()
|
62
|
+
rb.push( Current.attributes )
|
63
|
+
end
|
64
|
+
|
65
|
+
ra.pop
|
66
|
+
rb.pop
|
67
|
+
|
68
|
+
assert{ Current.attributes =~ {:foo => nil, :bar => nil} }
|
69
|
+
|
70
|
+
id = Thread.current.object_id
|
71
|
+
|
72
|
+
wa.push(id)
|
73
|
+
wb.push(id)
|
74
|
+
|
75
|
+
assert{ ra.pop =~ {:foo => 40, :bar => 2 } }
|
76
|
+
assert{ rb.pop =~ {:foo => 'forty', :bar => 'two' } }
|
77
|
+
|
78
|
+
assert{ Current.attributes =~ {:foo => nil, :bar => nil} }
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
#
|
83
|
+
test 'that Current methods can be mixed into a class or object' do
|
84
|
+
assert{ Current.attribute :foo, 42 }
|
85
|
+
assert{ Current.attribute :bar, 42.0 }
|
86
|
+
|
87
|
+
c = assert do
|
88
|
+
Class.new do
|
89
|
+
include Current
|
90
|
+
end.new
|
91
|
+
end
|
92
|
+
|
93
|
+
assert{ c.current_foo == 42 }
|
94
|
+
assert{ c.current_bar == 42.0 }
|
95
|
+
|
96
|
+
o = assert do
|
97
|
+
Object.new.tap{|o| o.extend Current }
|
98
|
+
end
|
99
|
+
|
100
|
+
assert{ o.current_foo == 42 }
|
101
|
+
assert{ o.current_bar == 42.0 }
|
102
|
+
|
103
|
+
assert{ c.current_foo = 'forty-two' }
|
104
|
+
assert{ o.current_foo == 'forty-two' }
|
105
|
+
|
106
|
+
assert{ o.current_bar = 0b101010 }
|
107
|
+
assert{ c.current_bar == 0b101010 }
|
108
|
+
|
109
|
+
assert{ c.current_foo = :bar }
|
110
|
+
assert{ o.current_bar = :foo }
|
111
|
+
|
112
|
+
assert{ Current.attributes =~ {:foo => :bar, :bar => :foo} }
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
#
|
117
|
+
teardown do
|
118
|
+
assert{ Current.reset }
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
BEGIN {
|
125
|
+
this = File.expand_path(__FILE__)
|
126
|
+
root = File.dirname(File.dirname(this))
|
127
|
+
|
128
|
+
require(File.join(root, 'lib/rails_current.rb'))
|
129
|
+
require(File.join(root, 'test/testing.rb'))
|
130
|
+
}
|
data/test/testing.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
#
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
testdir = File.expand_path(File.dirname(__FILE__))
|
6
|
+
rootdir = File.dirname(testdir)
|
7
|
+
libdir = File.join(rootdir, 'lib')
|
8
|
+
|
9
|
+
STDOUT.sync = true
|
10
|
+
|
11
|
+
$:.unshift(testdir) unless $:.include?(testdir)
|
12
|
+
$:.unshift(libdir) unless $:.include?(libdir)
|
13
|
+
$:.unshift(rootdir) unless $:.include?(rootdir)
|
14
|
+
|
15
|
+
class Testing
|
16
|
+
class Slug < ::String
|
17
|
+
def Slug.for(*args)
|
18
|
+
string = args.flatten.compact.join('-')
|
19
|
+
words = string.to_s.scan(%r/\w+/)
|
20
|
+
words.map!{|word| word.gsub %r/[^0-9a-zA-Z_-]/, ''}
|
21
|
+
words.delete_if{|word| word.nil? or word.strip.empty?}
|
22
|
+
new(words.join('-').downcase)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Context
|
27
|
+
attr_accessor :name
|
28
|
+
|
29
|
+
def initialize(name, *args)
|
30
|
+
@name = name
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
Slug.for(name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def Testing(*args, &block)
|
40
|
+
Class.new(::Test::Unit::TestCase) do
|
41
|
+
|
42
|
+
## class methods
|
43
|
+
#
|
44
|
+
class << self
|
45
|
+
def contexts
|
46
|
+
@contexts ||= []
|
47
|
+
end
|
48
|
+
|
49
|
+
def context(*args, &block)
|
50
|
+
return contexts.last if(args.empty? and block.nil?)
|
51
|
+
|
52
|
+
context = Testing::Context.new(*args)
|
53
|
+
contexts.push(context)
|
54
|
+
|
55
|
+
begin
|
56
|
+
block.call(context)
|
57
|
+
ensure
|
58
|
+
contexts.pop
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def slug_for(*args)
|
63
|
+
string = [context, args].flatten.compact.join('-')
|
64
|
+
words = string.to_s.scan(%r/\w+/)
|
65
|
+
words.map!{|word| word.gsub %r/[^0-9a-zA-Z_-]/, ''}
|
66
|
+
words.delete_if{|word| word.nil? or word.strip.empty?}
|
67
|
+
words.join('-').downcase.sub(/_$/, '')
|
68
|
+
end
|
69
|
+
|
70
|
+
def name() const_get(:Name) end
|
71
|
+
|
72
|
+
def testno()
|
73
|
+
'%05d' % (@testno ||= 0)
|
74
|
+
ensure
|
75
|
+
@testno += 1
|
76
|
+
end
|
77
|
+
|
78
|
+
def testing(*args, &block)
|
79
|
+
method = ["test", testno, slug_for(*args)].delete_if{|part| part.empty?}.join('_')
|
80
|
+
define_method(method, &block)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test(*args, &block)
|
84
|
+
testing(*args, &block)
|
85
|
+
end
|
86
|
+
|
87
|
+
def setup(&block)
|
88
|
+
define_method(:setup, &block) if block
|
89
|
+
end
|
90
|
+
|
91
|
+
def teardown(&block)
|
92
|
+
define_method(:teardown, &block) if block
|
93
|
+
end
|
94
|
+
|
95
|
+
def prepare(&block)
|
96
|
+
@prepare ||= []
|
97
|
+
@prepare.push(block) if block
|
98
|
+
@prepare
|
99
|
+
end
|
100
|
+
|
101
|
+
def cleanup(&block)
|
102
|
+
@cleanup ||= []
|
103
|
+
@cleanup.push(block) if block
|
104
|
+
@cleanup
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
## configure the subclass!
|
109
|
+
#
|
110
|
+
const_set(:Testno, '0')
|
111
|
+
slug = slug_for(*args).gsub(%r/-/,'_')
|
112
|
+
name = ['TESTING', '%03d' % const_get(:Testno), slug].delete_if{|part| part.empty?}.join('_')
|
113
|
+
name = name.upcase!
|
114
|
+
const_set(:Name, name)
|
115
|
+
const_set(:Missing, Object.new.freeze)
|
116
|
+
|
117
|
+
## instance methods
|
118
|
+
#
|
119
|
+
alias_method('__assert__', 'assert')
|
120
|
+
|
121
|
+
def assert(*args, &block)
|
122
|
+
if args.size == 1 and args.first.is_a?(Hash)
|
123
|
+
options = args.first
|
124
|
+
expected = getopt(:expected, options){ missing }
|
125
|
+
actual = getopt(:actual, options){ missing }
|
126
|
+
if expected == missing and actual == missing
|
127
|
+
actual, expected, *ignored = options.to_a.flatten
|
128
|
+
end
|
129
|
+
expected = expected.call() if expected.respond_to?(:call)
|
130
|
+
actual = actual.call() if actual.respond_to?(:call)
|
131
|
+
assert_equal(expected, actual)
|
132
|
+
end
|
133
|
+
|
134
|
+
if block
|
135
|
+
label = "assert(#{ args.join(' ') })"
|
136
|
+
result = nil
|
137
|
+
assert_nothing_raised{ result = block.call }
|
138
|
+
__assert__(result, label)
|
139
|
+
result
|
140
|
+
else
|
141
|
+
result = args.shift
|
142
|
+
label = "assert(#{ args.join(' ') })"
|
143
|
+
__assert__(result, label)
|
144
|
+
result
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def missing
|
149
|
+
self.class.const_get(:Missing)
|
150
|
+
end
|
151
|
+
|
152
|
+
def getopt(opt, hash, options = nil, &block)
|
153
|
+
[opt.to_s, opt.to_s.to_sym].each do |key|
|
154
|
+
return hash[key] if hash.has_key?(key)
|
155
|
+
end
|
156
|
+
default =
|
157
|
+
if block
|
158
|
+
block.call
|
159
|
+
else
|
160
|
+
options.is_a?(Hash) ? options[:default] : nil
|
161
|
+
end
|
162
|
+
return default
|
163
|
+
end
|
164
|
+
|
165
|
+
def subclass_of exception
|
166
|
+
class << exception
|
167
|
+
def ==(other) super or self > other end
|
168
|
+
end
|
169
|
+
exception
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
#
|
174
|
+
module_eval(&block)
|
175
|
+
|
176
|
+
self.setup()
|
177
|
+
self.prepare.each{|b| b.call()}
|
178
|
+
|
179
|
+
at_exit{
|
180
|
+
self.teardown()
|
181
|
+
self.cleanup.each{|b| b.call()}
|
182
|
+
}
|
183
|
+
|
184
|
+
self
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
if $0 == __FILE__
|
190
|
+
|
191
|
+
Testing 'Testing' do
|
192
|
+
testing('foo'){ assert true }
|
193
|
+
test{ assert true }
|
194
|
+
p instance_methods.grep(/test/)
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_current
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-05 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'description: rails_current kicks the ass'
|
15
15
|
email: ara.t.howard@gmail.com
|
@@ -21,6 +21,8 @@ files:
|
|
21
21
|
- Rakefile
|
22
22
|
- lib/rails_current.rb
|
23
23
|
- rails_current.gemspec
|
24
|
+
- test/rails_current_test.rb
|
25
|
+
- test/testing.rb
|
24
26
|
homepage: https://github.com/ahoward/rails_current
|
25
27
|
licenses: []
|
26
28
|
post_install_message:
|