rails_current 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: