the_force 0.1.0 → 0.2.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/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
|