contextr 0.0.1
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/COPYING.txt +340 -0
- data/History.txt +4 -0
- data/LICENSE.txt +57 -0
- data/Manifest.txt +32 -0
- data/README.txt +17 -0
- data/Rakefile +162 -0
- data/examples/education.rb +67 -0
- data/examples/fibonacci.rb +123 -0
- data/ext/dynamic.rb +73 -0
- data/ext/method_nature.rb +17 -0
- data/lib/contextr.rb +13 -0
- data/lib/contextr/contextr.rb +417 -0
- data/lib/contextr/version.rb +9 -0
- data/lib/core_ext/class.rb +24 -0
- data/lib/core_ext/module.rb +26 -0
- data/lib/core_ext/proc.rb +27 -0
- data/rake/group_spec_task.rb +7 -0
- data/rake/specific_group_spec_task.rb +7 -0
- data/scripts/txt2html +67 -0
- data/setup.rb +1585 -0
- data/spec/contextr/contextr_api_spec.rb +117 -0
- data/spec/core_ext/module_spec.rb +99 -0
- data/spec/core_ext/proc_spec.rb +61 -0
- data/spec/spec_helper.rb +1 -0
- data/tasks/annotations.rb +107 -0
- data/test/contextr/test_contextr.rb +165 -0
- data/test/test_helper.rb +2 -0
- data/website/index.html +180 -0
- data/website/index.txt +119 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +129 -0
- data/website/template.rhtml +48 -0
- metadata +79 -0
data/Rakefile
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'hoe'
|
11
|
+
include FileUtils
|
12
|
+
require File.join(File.dirname(__FILE__), 'lib', 'contextr', 'version')
|
13
|
+
|
14
|
+
AUTHOR = 'schmidt' # can also be an array of Authors
|
15
|
+
EMAIL = "ruby@schmidtwisser.de"
|
16
|
+
DESCRIPTION = "The goal is to equip Ruby with an API to allow context-oriented programming."
|
17
|
+
GEM_NAME = 'contextr' # what ppl will type to install your gem
|
18
|
+
RUBYFORGE_PROJECT = 'contextr' # The unix name for your project
|
19
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
20
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
21
|
+
|
22
|
+
NAME = "contextr"
|
23
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
24
|
+
VERS = ContextR::VERSION::STRING + (REV ? ".#{REV}" : "")
|
25
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
|
26
|
+
RDOC_OPTS = ['--quiet', '--title', 'contextr documentation',
|
27
|
+
"--opname", "index.html",
|
28
|
+
"--line-numbers",
|
29
|
+
"--main", "README",
|
30
|
+
"--inline-source"]
|
31
|
+
|
32
|
+
class Hoe
|
33
|
+
def extra_deps
|
34
|
+
@extra_deps.reject { |x| Array(x).first == 'hoe' }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generate all the Rake tasks
|
39
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
40
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
41
|
+
p.author = AUTHOR
|
42
|
+
p.description = DESCRIPTION
|
43
|
+
p.email = EMAIL
|
44
|
+
p.summary = DESCRIPTION
|
45
|
+
p.url = HOMEPATH
|
46
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
47
|
+
p.test_globs = ["test/**/test_*.rb"]
|
48
|
+
p.clean_globs = CLEAN #An array of file patterns to delete on clean.
|
49
|
+
|
50
|
+
# == Optional
|
51
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
52
|
+
#p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
53
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
desc 'Generate website files'
|
58
|
+
task :website_generate do
|
59
|
+
Dir['website/**/*.txt'].each do |txt|
|
60
|
+
sh %{ ruby scripts/txt2html #{txt} > #{txt.gsub(/txt$/,'html')} }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'Upload website files to rubyforge'
|
65
|
+
task :website_upload do
|
66
|
+
config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
|
67
|
+
host = "#{config["username"]}@rubyforge.org"
|
68
|
+
remote_dir = "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}/"
|
69
|
+
# remote_dir = "/var/www/gforge-projects/#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
70
|
+
local_dir = 'website'
|
71
|
+
sh %{rsync -av --delete-excluded --exclude=".*/" #{local_dir}/ #{host}:#{remote_dir}}
|
72
|
+
end
|
73
|
+
|
74
|
+
desc 'Generate and upload website files'
|
75
|
+
task :website => [:website_generate, :website_upload]
|
76
|
+
|
77
|
+
desc 'Release the website and new gem version'
|
78
|
+
task :deploy => [:check_version, :website, :release]
|
79
|
+
|
80
|
+
task :check_version do
|
81
|
+
unless ENV['VERSION']
|
82
|
+
puts 'Must pass a VERSION=x.y.z release version'
|
83
|
+
exit
|
84
|
+
end
|
85
|
+
unless ENV['VERSION'] == VERS
|
86
|
+
puts "Please update your version.rb to match the release version, currently #{VERS}"
|
87
|
+
exit
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# Most of this code was taken from the Rubinius Rakefile.
|
93
|
+
# Thanks for the help.
|
94
|
+
|
95
|
+
ROOT = File.expand_path(File.dirname(__FILE__))
|
96
|
+
|
97
|
+
def load_files(files)
|
98
|
+
files.each do |path|
|
99
|
+
begin
|
100
|
+
require(path)
|
101
|
+
rescue Object => e
|
102
|
+
STDERR.puts "Unable to load #{path}. #{e.message} (#{e.class})"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
def require_files(files)
|
107
|
+
files.each do |path|
|
108
|
+
begin
|
109
|
+
require(path)
|
110
|
+
rescue Object => e
|
111
|
+
STDERR.puts "Unable to load #{path}. #{e.message} (#{e.class})"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
begin
|
117
|
+
require 'spec/rake/spectask'
|
118
|
+
rescue LoadError
|
119
|
+
raise <<-EOM
|
120
|
+
Unable to load spec/rake/spectask. RSpec is a requirement to build Rubinius.
|
121
|
+
Please install RSpec before building (http://rspec.rubyforge.org).
|
122
|
+
EOM
|
123
|
+
end
|
124
|
+
|
125
|
+
# Task class extensions
|
126
|
+
paths = Dir[ File.join(File.dirname(__FILE__), 'rake/*') ]
|
127
|
+
require_files(paths)
|
128
|
+
#
|
129
|
+
# Other tasks
|
130
|
+
paths = Dir[ File.join(File.dirname(__FILE__), 'tasks/*') ]
|
131
|
+
load_files(paths)
|
132
|
+
|
133
|
+
desc "Run all specs and tests"
|
134
|
+
task :default => [ :spec, :test ]
|
135
|
+
|
136
|
+
task :spec => 'spec:all'
|
137
|
+
|
138
|
+
# Generate all the spec tasks
|
139
|
+
namespace :spec do
|
140
|
+
|
141
|
+
spec_targets = %w(contextr core_ext)
|
142
|
+
|
143
|
+
spec_targets.each do | group |
|
144
|
+
spec_files = Dir[ File.join( File.dirname(__FILE__),
|
145
|
+
"spec/#{group}/*_spec.rb") ]
|
146
|
+
|
147
|
+
GroupSpecTask.new( group )
|
148
|
+
|
149
|
+
namespace group do
|
150
|
+
spec_files.each do | file |
|
151
|
+
SpecificGroupSpecTask.new( File.basename( file, '_spec.rb'), group )
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
desc "Run all specs."
|
157
|
+
task :all => spec_targets.collect! { | group | 'spec:' << group }
|
158
|
+
end
|
159
|
+
|
160
|
+
desc "Run all benchmarks - currently none"
|
161
|
+
task :benchmark
|
162
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "contextr"
|
3
|
+
|
4
|
+
class Person
|
5
|
+
layer :address, :education
|
6
|
+
|
7
|
+
attr_accessor :name, :address, :university
|
8
|
+
|
9
|
+
def initialize name, address, university
|
10
|
+
self.name = name
|
11
|
+
self.address = address
|
12
|
+
self.university = university
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"Name: #{name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
address.post :to_s do | n |
|
20
|
+
n.return_value += "; Address: #{address}"
|
21
|
+
end
|
22
|
+
|
23
|
+
education.post :to_s do | n |
|
24
|
+
n.return_value += ";\n[Education] #{university}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class University
|
29
|
+
layer :address
|
30
|
+
|
31
|
+
attr_accessor :name, :address
|
32
|
+
|
33
|
+
def initialize name, address
|
34
|
+
self.name = name
|
35
|
+
self.address = address
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
"Name: #{name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
address.post :to_s do | n |
|
43
|
+
n.return_value += "; Address: #{address}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
hpi = University.new( "Hasso-Plattner-Institut", "Potsdam" )
|
49
|
+
somePerson = Person.new( "Gregor Schmidt", "Berlin", hpi )
|
50
|
+
|
51
|
+
puts
|
52
|
+
puts somePerson
|
53
|
+
ContextR::with_layers :address do
|
54
|
+
puts
|
55
|
+
puts somePerson
|
56
|
+
|
57
|
+
ContextR::with_layers :education do
|
58
|
+
puts
|
59
|
+
puts somePerson
|
60
|
+
|
61
|
+
ContextR::without_layers :address do
|
62
|
+
puts
|
63
|
+
puts somePerson
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
puts
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "contextr"
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
require 'benchmark'
|
6
|
+
|
7
|
+
class FibonacciCalculator
|
8
|
+
def compute fixnum
|
9
|
+
if fixnum == 1 or fixnum == 0
|
10
|
+
fixnum
|
11
|
+
elsif fixnum < 0
|
12
|
+
raise ArgumentError, "Fibonacci not defined for negative numbers"
|
13
|
+
else
|
14
|
+
compute(fixnum - 1) + compute(fixnum - 2)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
layer :fib_pre_post, :fib_wrap
|
19
|
+
attr_accessor :cache
|
20
|
+
|
21
|
+
fib_pre_post.pre :compute do | nature |
|
22
|
+
self.cache ||= {}
|
23
|
+
if self.cache.key? nature.arguments.first
|
24
|
+
nature.break! self.cache[nature.arguments.first]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
fib_pre_post.post :compute do | nature |
|
29
|
+
self.cache[nature.arguments.first] = nature.return_value
|
30
|
+
end
|
31
|
+
|
32
|
+
fib_wrap.wrap :compute do | nature |
|
33
|
+
self.cache ||= {}
|
34
|
+
if self.cache.key? nature.arguments.first
|
35
|
+
nature.return_value = self.cache[nature.arguments.first]
|
36
|
+
else
|
37
|
+
nature.call_next
|
38
|
+
self.cache[nature.arguments.first] = nature.return_value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Fixnum
|
44
|
+
def fibonacci
|
45
|
+
if self == 1 or self == 0
|
46
|
+
self
|
47
|
+
elsif self < 0
|
48
|
+
raise ArgumentError, "Fibonacci not defined for negative numbers"
|
49
|
+
else
|
50
|
+
old_fib, fib = 0, 1
|
51
|
+
for i in 2..self
|
52
|
+
fib, old_fib = old_fib + fib, fib
|
53
|
+
end
|
54
|
+
fib
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class FibonacciTest < Test::Unit::TestCase
|
60
|
+
def setup
|
61
|
+
@fibonacci = FibonacciCalculator.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_basic_function
|
65
|
+
Benchmark.bm(20) do |x|
|
66
|
+
x.report("Recursive:") {
|
67
|
+
assert_equal 0, @fibonacci.compute( 0 )
|
68
|
+
assert_equal 1, @fibonacci.compute( 1 )
|
69
|
+
assert_equal 1, @fibonacci.compute( 2 )
|
70
|
+
assert_equal 55, @fibonacci.compute( 10 )
|
71
|
+
assert_equal 6765, @fibonacci.compute( 20 )
|
72
|
+
# The following are too hard for the simple solution
|
73
|
+
assert_equal 75_025, 25.fibonacci
|
74
|
+
assert_equal 9227465, 35.fibonacci
|
75
|
+
assert_equal 280571172992510140037611932413038677189525,
|
76
|
+
200.fibonacci
|
77
|
+
assert_equal 176023680645013966468226945392411250770384383304492191886725992896575345044216019675,
|
78
|
+
400.fibonacci
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_layered_function_with_pre_post
|
84
|
+
Benchmark.bm(20) do |x|
|
85
|
+
x.report("Layered Pre/Post:") {
|
86
|
+
ContextR.with_layers :fib_pre_post do
|
87
|
+
assert_equal 0, @fibonacci.compute( 0 )
|
88
|
+
assert_equal 1, @fibonacci.compute( 1 )
|
89
|
+
assert_equal 1, @fibonacci.compute( 2 )
|
90
|
+
assert_equal 2, @fibonacci.compute( 3 )
|
91
|
+
assert_equal 55, @fibonacci.compute( 10 )
|
92
|
+
assert_equal 6765, @fibonacci.compute( 20 )
|
93
|
+
assert_equal 75_025, @fibonacci.compute( 25 )
|
94
|
+
assert_equal 9227465, @fibonacci.compute( 35 )
|
95
|
+
assert_equal 280571172992510140037611932413038677189525,
|
96
|
+
@fibonacci.compute( 200 )
|
97
|
+
assert_equal 176023680645013966468226945392411250770384383304492191886725992896575345044216019675,
|
98
|
+
@fibonacci.compute( 400 )
|
99
|
+
end
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_layered_function_with_wrap
|
105
|
+
Benchmark.bm(20) do |x|
|
106
|
+
x.report("Layered Wrap:") {
|
107
|
+
ContextR.with_layers :fib_wrap do
|
108
|
+
assert_equal 0, @fibonacci.compute( 0 )
|
109
|
+
assert_equal 1, @fibonacci.compute( 1 )
|
110
|
+
assert_equal 1, @fibonacci.compute( 2 )
|
111
|
+
assert_equal 55, @fibonacci.compute( 10 )
|
112
|
+
assert_equal 6765, @fibonacci.compute( 20 )
|
113
|
+
assert_equal 75_025, @fibonacci.compute( 25 )
|
114
|
+
assert_equal 9227465, @fibonacci.compute( 35 )
|
115
|
+
assert_equal 280571172992510140037611932413038677189525,
|
116
|
+
@fibonacci.compute( 200 )
|
117
|
+
assert_equal 176023680645013966468226945392411250770384383304492191886725992896575345044216019675,
|
118
|
+
@fibonacci.compute( 400 )
|
119
|
+
end
|
120
|
+
}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/ext/dynamic.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module Dynamic
|
2
|
+
class << self
|
3
|
+
Thread.main[:DYNAMIC] = Hash.new { |hash, key|
|
4
|
+
raise NameError, "no such dynamic variable: #{key}"
|
5
|
+
}
|
6
|
+
|
7
|
+
def here!
|
8
|
+
Thread.current[:DYNAMIC] = Hash.new { |hash, key|
|
9
|
+
raise NameError, "no such dynamic variable: #{key}"
|
10
|
+
}.update Thread.main[:DYNAMIC]
|
11
|
+
end
|
12
|
+
|
13
|
+
def variables
|
14
|
+
Thread.current[:DYNAMIC] or here!
|
15
|
+
end
|
16
|
+
|
17
|
+
def variable(definition)
|
18
|
+
case definition
|
19
|
+
when Symbol
|
20
|
+
if variables.has_key? definition
|
21
|
+
raise NameError, "dynamic variable `#{definition}' already exists"
|
22
|
+
end
|
23
|
+
variables[definition] = nil
|
24
|
+
when Hash
|
25
|
+
definition.each { |key, value|
|
26
|
+
if variables.has_key? key
|
27
|
+
raise NameError, "dynamic variable `#{key}' already exists"
|
28
|
+
end
|
29
|
+
variables[key] = value
|
30
|
+
}
|
31
|
+
else
|
32
|
+
raise ArgumentError,
|
33
|
+
"can't create a new dynamic variable from #{definition.class}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](key)
|
38
|
+
variables[key]
|
39
|
+
end
|
40
|
+
|
41
|
+
def []=(key, value)
|
42
|
+
variables[key] = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def undefine(*keys)
|
46
|
+
keys.each { |key|
|
47
|
+
self[key]
|
48
|
+
variables.delete key
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def let(bindings, &block)
|
53
|
+
save = {}
|
54
|
+
bindings.to_hash.collect { |key, value|
|
55
|
+
save[key] = self[key]
|
56
|
+
self[key] = value
|
57
|
+
}
|
58
|
+
return_value = block.call
|
59
|
+
variables.update save
|
60
|
+
return_value
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_missing(name, *args)
|
64
|
+
if match = /=\Z/.match(name.to_s) # setter?
|
65
|
+
raise ArgumentError, "invalid setter call" unless args.size == 1
|
66
|
+
self[match.pre_match.intern] = args.first
|
67
|
+
else
|
68
|
+
raise ArgumentError, "invalid getter call" unless args.empty?
|
69
|
+
self[name]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
unless Object.const_defined? "MethodNatureSuperClass"
|
2
|
+
MethodNatureSuperClass = Struct.new(:arguments, :return_value, :break, :block)
|
3
|
+
end
|
4
|
+
class MethodNature < MethodNatureSuperClass
|
5
|
+
def break!( *value )
|
6
|
+
self.break = true
|
7
|
+
self.return_value = value.first unless value.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
def call_next
|
11
|
+
block.call( *arguments )
|
12
|
+
end
|
13
|
+
|
14
|
+
def call_next_with( *args )
|
15
|
+
block.call( *args )
|
16
|
+
end
|
17
|
+
end
|
data/lib/contextr.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# TODO: get rid of this workaround to avoid double loading on `rake test`
|
2
|
+
unless Object.const_defined? "ContextR"
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_support'
|
5
|
+
|
6
|
+
Dir[File.join(File.dirname(__FILE__), '../ext/**/*.rb')].sort.each { |lib| require lib }
|
7
|
+
Dir[File.join(File.dirname(__FILE__), 'core_ext/**/*.rb')].sort.each { |lib| require lib }
|
8
|
+
Dir[File.join(File.dirname(__FILE__), 'contextr/**/*.rb')].sort.each { |lib| require lib }
|
9
|
+
|
10
|
+
end
|
11
|
+
unless Dynamic.variables.include?( :layers )
|
12
|
+
Dynamic.variable( :layers => [ ContextR::DefaultLayer ] )
|
13
|
+
end
|