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 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 @currentfoobar vars everywhere. don't do
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 Current.user
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 'fattr'
1
+ require 'map'
2
2
 
3
3
  module Current
4
- def Current.version() '1.0.0' end
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
- attribute_names.push(name) unless attribute?(name)
9
- Fattr(name, *args, &block)
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
- attribute_names.include?(name.to_s)
76
+ calls.has_key?(name)
14
77
  end
15
78
 
16
79
  def Current.attribute_names
17
- @attribute_names ||= []
80
+ calls.keys
18
81
  end
19
82
 
20
83
  def Current.attributes
21
- attribute_names.inject({}){|hash, name| hash.update(name => send(name))}
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)
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification::new do |spec|
5
5
  spec.name = "rails_current"
6
- spec.version = "1.0.0"
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.0.0
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: 2011-12-17 00:00:00.000000000 Z
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: