the_force 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -2
- data/lib/the_force/memoize.rb +27 -0
- data/lib/the_force/object_support.rb +67 -31
- data/lib/the_force/remote_includes.rb +63 -0
- data/lib/the_force.rb +1 -0
- data/test/test_helper.rb +6 -0
- data/test/unit/test_memoize.rb +65 -0
- data/test/unit/test_object_support.rb +1 -5
- metadata +11 -5
data/Rakefile
CHANGED
@@ -7,13 +7,13 @@ exclude_file_globs = []
|
|
7
7
|
|
8
8
|
spec = Gem::Specification.new do |s|
|
9
9
|
s.name = "the_force"
|
10
|
-
s.version = '0.
|
10
|
+
s.version = '0.2.0'
|
11
11
|
s.author = "Ryan Ziegler"
|
12
12
|
s.email = "info@symbolforce.com"
|
13
13
|
s.homepage = "http://www.symbolforce.com"
|
14
14
|
s.platform = Gem::Platform::RUBY
|
15
15
|
s.summary = "Common code for Symbolforce"
|
16
|
-
s.description = "Common code for Symbolforce
|
16
|
+
s.description = "Common code for Symbolforce"
|
17
17
|
s.files = FileList[include_file_globs].to_a - FileList[exclude_file_globs].to_a
|
18
18
|
s.require_path = "lib"
|
19
19
|
s.test_files = FileList["test/**/test_*.rb"].to_a
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#CRZ - any reason to support parameters in the block? would need instance_exec, and maybe some compat instance_exec for pre-1.8.7 rubies...
|
2
|
+
|
3
|
+
module TheForce
|
4
|
+
module ObjectSupport
|
5
|
+
module Memoization
|
6
|
+
def attr_memoize(attribute, &b)
|
7
|
+
raise ArgumentError, "attr_memoize requires a block" unless block_given?
|
8
|
+
|
9
|
+
ivar = "@#{attribute}"
|
10
|
+
self.class_eval do
|
11
|
+
define_method attribute.to_sym do |*args|
|
12
|
+
refresh = !!args[0]
|
13
|
+
if not instance_variable_defined?(ivar) or refresh
|
14
|
+
instance_variable_set(ivar, instance_eval(&b))
|
15
|
+
else
|
16
|
+
instance_variable_get(ivar)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Object
|
26
|
+
extend TheForce::ObjectSupport::Memoization
|
27
|
+
end
|
@@ -1,41 +1,21 @@
|
|
1
|
+
#--
|
1
2
|
#CRZ - imitate andand to a great degree.
|
2
3
|
# Could have something more complex than if or unless, but they're enough, and didnt want to introduce proc overhead.
|
3
4
|
# - logic duplication, three ""inverted^ return ? caller : nil""s
|
4
5
|
### - better behavior for args WITH a block?
|
5
6
|
|
6
|
-
if Module.constants.include?('BasicObject')
|
7
|
-
class ConditionalReturningMe < BasicObject
|
8
|
-
end
|
9
|
-
else
|
10
|
-
class ConditionalReturningMe
|
11
|
-
instance_methods.reject { |m| m =~ /^__/ }.each { |m| undef_method m }
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class ConditionalReturningMe
|
16
|
-
def initialize(me, inverted)
|
17
|
-
super()
|
18
|
-
@me = me
|
19
|
-
@inverted = inverted
|
20
|
-
end
|
21
|
-
|
22
|
-
def method_missing(sym, *args, &b)
|
23
|
-
(@inverted ^ @me.send(sym, *args, &b)) ? @me : nil
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
7
|
module TheForce
|
28
8
|
module ObjectSupport
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
9
|
+
if Module.constants.include?('BasicObject')
|
10
|
+
class ConditionalReturningMe < BasicObject
|
11
|
+
end
|
12
|
+
else
|
13
|
+
class ConditionalReturningMe
|
14
|
+
instance_methods.reject { |m| m =~ /^__/ }.each { |m| undef_method m }
|
15
|
+
end
|
35
16
|
end
|
36
|
-
|
37
|
-
|
38
|
-
def self.conditional(who, inverted, *args, &b)
|
17
|
+
|
18
|
+
def self.conditional(who, inverted, *args, &b) # :doc:
|
39
19
|
if block_given?
|
40
20
|
(inverted ^ yield(who)) ? who : nil
|
41
21
|
elsif args.length > 0
|
@@ -44,9 +24,65 @@ module TheForce
|
|
44
24
|
ConditionalReturningMe.new(who, inverted)
|
45
25
|
end
|
46
26
|
end
|
27
|
+
|
28
|
+
# This is the blankslate object returned by the Object#if and Object#unless methods when called with no arguments or block.
|
29
|
+
class ConditionalReturningMe
|
30
|
+
def initialize(me, inverted)
|
31
|
+
super()
|
32
|
+
@me = me
|
33
|
+
@inverted = inverted
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(sym, *args, &b) # :doc:
|
37
|
+
(@inverted ^ @me.send(sym, *args, &b)) ? @me : nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module InstanceMethods
|
42
|
+
# Will return a nil if the condition is false, otherwise it will return the receiver. The condition
|
43
|
+
# can be specified in 3 ways, as a list of arguments which are sent to Object#send, a block, or via essentially a
|
44
|
+
# delegate object, ala the andand gem.
|
45
|
+
#
|
46
|
+
# == Examples
|
47
|
+
# - 10.if(:>, 5) => 10
|
48
|
+
# - 10.if {|x| x == 5) => nil
|
49
|
+
# - 10.if.nonzero? => 10
|
50
|
+
#
|
51
|
+
# == Usage
|
52
|
+
# This becomes useful to simplify logic when you want to specify a default value,
|
53
|
+
# (particularly in Rails views) or you have a receiver which is the result of a lengthy expression.
|
54
|
+
#
|
55
|
+
# * number_of_products.if.nonzero? || "Don't you want to buy something?"
|
56
|
+
# * lovers.sort.reverse.unless.empty? || "Where's the fever? :("
|
57
|
+
#
|
58
|
+
# == Warning
|
59
|
+
# Do not try to do:
|
60
|
+
#
|
61
|
+
# * title.if.length > 20 || title.truncate[0,17] + "..."
|
62
|
+
#
|
63
|
+
# This would evaluate to:
|
64
|
+
#
|
65
|
+
# * title > 20 || title.truncate(17) + "..."
|
66
|
+
def if(*args, &b)
|
67
|
+
TheForce::ObjectSupport.conditional(self, false, *args, &b)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Same as +if+, only with the predictably inverse logic.
|
71
|
+
#
|
72
|
+
# == Usage
|
73
|
+
# * title.unless.blank? || suggestion_html "please set your title on the edit page"
|
74
|
+
def unless(*args, &b)
|
75
|
+
TheForce::ObjectSupport.conditional(self, true, *args, &b)
|
76
|
+
end
|
77
|
+
|
78
|
+
def eigenclass #this could also be labeled 'self'?
|
79
|
+
class << self; self; end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
47
83
|
end
|
48
84
|
end
|
49
85
|
|
50
86
|
class Object
|
51
|
-
include TheForce::ObjectSupport
|
87
|
+
include TheForce::ObjectSupport::InstanceMethods
|
52
88
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module TheForce
|
2
|
+
module RemoteIncludes
|
3
|
+
@@remotes = []
|
4
|
+
|
5
|
+
def self.[](name)
|
6
|
+
@@remotes.detect {|remote| remote.name == name }
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.remotes
|
10
|
+
@@remotes
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.add(attrs)
|
14
|
+
attrs[:name] ||= attrs[:url]
|
15
|
+
@@remotes << RemoteInclude.new(attrs) unless attrs[:url].blank?
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.cache_url_with_partial_fallback(url, key, partial = nil)
|
19
|
+
begin
|
20
|
+
response = Net::HTTP.get_response(URI.parse(url))
|
21
|
+
Rails.cache.write(key, response.body) if response.class == Net::HTTPOK
|
22
|
+
rescue Exception => e
|
23
|
+
ensure
|
24
|
+
cache_from_partial(key, partial) if partial and not Rails.cache.read(key)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.cache_from_partial(key, partial)
|
29
|
+
av = ActionView::Base.new(Rails::Configuration.new.view_path)
|
30
|
+
Rails.cache.write(key, av.render(:partial => partial))
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.recache!
|
34
|
+
Array(@@remotes).each do |remote|
|
35
|
+
remote.recache!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class RemoteInclude < OpenStruct
|
41
|
+
def [](key)
|
42
|
+
self.send(key.to_sym)
|
43
|
+
end
|
44
|
+
|
45
|
+
def key
|
46
|
+
name
|
47
|
+
end
|
48
|
+
|
49
|
+
def value
|
50
|
+
Rails.cache.read(key)
|
51
|
+
end
|
52
|
+
|
53
|
+
def recache!
|
54
|
+
RemoteIncludes.cache_url_with_partial_fallback(self.url, self.name, self.backup_partial)
|
55
|
+
end
|
56
|
+
|
57
|
+
def last_cached_at
|
58
|
+
return nil unless Rails.cache.instance_of? ActiveSupport::Cache::FileStore
|
59
|
+
|
60
|
+
File.mtime(Rails.cache.instance_eval("real_file_path('#{self.key}')")) rescue nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/the_force.rb
CHANGED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'memoize'
|
3
|
+
|
4
|
+
class Memoizer
|
5
|
+
def initialize
|
6
|
+
@value = 1
|
7
|
+
@other = 2
|
8
|
+
end
|
9
|
+
|
10
|
+
def inc
|
11
|
+
@value +=1
|
12
|
+
end
|
13
|
+
|
14
|
+
def meth
|
15
|
+
3
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_memoize :a do
|
19
|
+
@value
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_memoize :test_instance_variable_access do
|
23
|
+
@other
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_memoize :test_instance_method_access do
|
27
|
+
meth
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class TestMemoize < Test::Unit::TestCase
|
32
|
+
def setup
|
33
|
+
@m = Memoizer.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_raises_error_with_no_block
|
37
|
+
assert_raise ArgumentError do
|
38
|
+
Memoizer.class_eval('attr_memoize :b')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_memoizes_first_time
|
43
|
+
assert_equal 1, @m.a
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_can_refer_to_instance_variables_inside_block
|
47
|
+
assert_equal 2, @m.test_instance_variable_access
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_can_call_instance_method_inside_block
|
51
|
+
assert_equal 3, @m.test_instance_method_access
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_does_not_rememoize_second_time
|
55
|
+
@m.a
|
56
|
+
@m.inc
|
57
|
+
assert_equal 1, @m.a
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_memoizes_again_after_refresh
|
61
|
+
@m.a
|
62
|
+
@m.inc
|
63
|
+
assert_equal 2, @m.a(true)
|
64
|
+
end
|
65
|
+
end
|
@@ -1,9 +1,5 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
3
|
-
require File.join(File.dirname(__FILE__), '../../lib/the_force/object_support.rb')
|
4
|
-
rescue LoadError
|
5
|
-
require File.expand_path('../../lib/the_force/object_support.rb', __FILE__)
|
6
|
-
end
|
2
|
+
require 'object_support'
|
7
3
|
|
8
4
|
begin
|
9
5
|
require 'active_support'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: the_force
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ryan Ziegler
|
@@ -15,11 +15,11 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-10-03 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
22
|
-
description: Common code for Symbolforce
|
22
|
+
description: Common code for Symbolforce
|
23
23
|
email: info@symbolforce.com
|
24
24
|
executables: []
|
25
25
|
|
@@ -33,11 +33,15 @@ files:
|
|
33
33
|
- Rakefile
|
34
34
|
- init.rb
|
35
35
|
- lib/the_force/keep_trying.rb
|
36
|
+
- lib/the_force/memoize.rb
|
36
37
|
- lib/the_force/object_support.rb
|
38
|
+
- lib/the_force/remote_includes.rb
|
37
39
|
- lib/the_force/thread_pool.rb
|
38
40
|
- lib/the_force/timer.rb
|
39
41
|
- lib/the_force.rb
|
42
|
+
- test/test_helper.rb
|
40
43
|
- test/unit/test_keep_trying.rb
|
44
|
+
- test/unit/test_memoize.rb
|
41
45
|
- test/unit/test_object_support.rb
|
42
46
|
has_rdoc: true
|
43
47
|
homepage: http://www.symbolforce.com
|
@@ -75,5 +79,7 @@ signing_key:
|
|
75
79
|
specification_version: 3
|
76
80
|
summary: Common code for Symbolforce
|
77
81
|
test_files:
|
82
|
+
- test/test_helper.rb
|
78
83
|
- test/unit/test_keep_trying.rb
|
84
|
+
- test/unit/test_memoize.rb
|
79
85
|
- test/unit/test_object_support.rb
|