reac 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,2 @@
1
+ = reac - Reactive Programming in Ruby
2
+
@@ -0,0 +1,69 @@
1
+ #
2
+ # Reac::Reactivize
3
+ #
4
+ class Reac
5
+ module Reactivize
6
+ # reactivize - make methods to take reactive values
7
+ # NOTE: currently supports one-arg method only
8
+ #
9
+ # exmaple:
10
+ #class String
11
+ # alias __old_plus__ +
12
+ # def +(other)
13
+ # if other.kind_of?(Reac)
14
+ # Reac(self) + other
15
+ # else
16
+ # self.__old_plus__(other)
17
+ # end
18
+ # end
19
+ #end
20
+ OLD_NAME = {
21
+ :+ => "plus",
22
+ :- => "minus",
23
+ :* => "mul",
24
+ :/ => "div"
25
+ }
26
+ def reactivize(*methods)
27
+ methods.each do |m|
28
+ old_name = OLD_NAME[m] || m
29
+ module_eval <<-EOD
30
+ alias __old_#{old_name}__ #{m}
31
+ def #{m}(arg)
32
+ if arg.kind_of?(Reac)
33
+ Reac(self).#{m}(arg)
34
+ else
35
+ __old_#{old_name}__(arg)
36
+ end
37
+ end
38
+ EOD
39
+ end
40
+ end
41
+
42
+ # reactivize singleton methods.
43
+ #
44
+ # example:
45
+ # module Math
46
+ # reactivize_singleton :sin, :cos
47
+ # end
48
+ def reactivize_singleton(*methods)
49
+ method_list = methods.map{|m|m.inspect}.join(',')
50
+ instance_eval <<-EOD
51
+ class << self
52
+ reactivize #{method_list}
53
+ end
54
+ EOD
55
+ end
56
+ end
57
+ end
58
+
59
+ # now you can use 'reactivize' in all classes & modules
60
+ Module.__send__(:include, Reac::Reactivize)
61
+
62
+ class String
63
+ reactivize :+, :*
64
+ end
65
+
66
+ class Fixnum
67
+ reactivize :+, :-, :*, :/
68
+ end
69
+
@@ -0,0 +1,33 @@
1
+ require 'curses'
2
+
3
+ class Reac
4
+ class CursesView < View
5
+ def initialize(&block)
6
+ @top_window = Curses.init_screen
7
+ @x = @y = 0
8
+ #Curses.noecho
9
+ #Curses.nocbreak # disable buffering keyboard input
10
+ Curses.clear
11
+ Curses.refresh
12
+ @reacs = []
13
+ #@top_window = Curses::Window.new(Curses.cols-2, Curses.lines-1, 0, 0) #y, x = 0
14
+
15
+ instance_eval(&block) if block
16
+ end
17
+
18
+ def put(y, x, reac)
19
+ @reacs << [y, x, reac]
20
+ end
21
+
22
+ def main_loop(now)
23
+ @top_window.clear
24
+ @reacs.each do |y, x, reac|
25
+ yval = Reac.value(y, now)
26
+ xval = Reac.value(x, now)
27
+ @top_window.setpos(yval, xval)
28
+ @top_window << Reac.value(reac, now).to_s
29
+ @top_window.refresh
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,92 @@
1
+ require 'sdl'
2
+
3
+ class Reac
4
+
5
+ class SDLView < View
6
+
7
+ def self.init(w=640, h=480, options={})
8
+ @@width, @@height = w, h
9
+ bpp = options[:bpp] || 32
10
+ flags = options[:flags] || SDL::SWSURFACE
11
+
12
+ SDL.init(options[:init_flags] || SDL::INIT_EVERYTHING)
13
+ SDL::TTF.init unless options[:no_ttf]
14
+ @@screen = SDL.set_video_mode(w, h, bpp, flags)
15
+ end
16
+
17
+ def initialize(options={}, &block)
18
+ @reacs = {
19
+ :images => [],
20
+ :bmfonts => [],
21
+ :ttffonts => [],
22
+ }
23
+ @background_color = options[:background] || [0, 0, 0]
24
+
25
+ instance_eval(&block) if block
26
+ end
27
+
28
+ def put(_x, _y, _obj, *rest)
29
+ case _obj
30
+ when SDL::Surface
31
+ put_image(_x, _y, _obj)
32
+ else
33
+ put_string(_x, _y, _obj, *rest)
34
+ #raise ArgumentError, "unkown object type: #{_obj.class}"
35
+ end
36
+ end
37
+
38
+ def main_loop(now)
39
+ @@screen.fill_rect(0, 0, @@width, @@height, @background_color)
40
+ @reacs[:images].each do |_x, _y, _image|
41
+ x = Reac.value(_x, now)
42
+ y = Reac.value(_y, now)
43
+ image = Reac.value(_image, now)
44
+ @@screen.put(image, x, y)
45
+ end
46
+ @reacs[:bmfonts].each do |_x, _y, _str, font|
47
+ x = Reac.value(_x, now)
48
+ y = Reac.value(_y, now)
49
+ str = Reac.value(_str, now)
50
+ i = 0
51
+ str.each_line do |line|
52
+ font.textout(@@screen, line, x, y + font.height*i)
53
+ i += 1
54
+ end
55
+ end
56
+ col = [255,255,255]
57
+ @reacs[:ttffonts].each do |_x, _y, _str, font|
58
+ x = Reac.value(_x, now)
59
+ y = Reac.value(_y, now)
60
+ str = Reac.value(_str, now)
61
+ i = 0
62
+ str.each_line do |line|
63
+ font.draw_solid_utf8(@@screen, line, x, y + font.line_skip*i, *col)
64
+ i += 1
65
+ end
66
+ end
67
+ @@screen.flip
68
+ end
69
+
70
+ private
71
+
72
+ #TODO: rewrite in OOP style (use inheritance)
73
+ def put_image(_x, _y, _image)
74
+ @reacs[:images] << [_x, _y, _image]
75
+ end
76
+
77
+ def put_string(_x, _y, _str, font)
78
+ case font
79
+ # when SDL::BMFont
80
+ # @reacs[:bmfonts] << [_x, _y, _str, font]
81
+ when SDL::TTF
82
+ @reacs[:ttffonts] << [_x, _y, _str, font]
83
+ when SDL::Kanji
84
+ raise NotImplementedError, "this font type is not yet supported.."
85
+ else
86
+ raise ArgumentError, "unkown font type: #{font.class}"
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ end
data/lib/reac/view.rb ADDED
@@ -0,0 +1,26 @@
1
+ class Reac
2
+
3
+ class View
4
+
5
+ WAIT = 0.1
6
+
7
+ def start(secs=nil)
8
+ if secs
9
+ start = Time.now
10
+ loop{
11
+ now = Time.now
12
+ main_loop(now)
13
+ break if now - start > secs
14
+ sleep WAIT
15
+ }
16
+ else
17
+ loop{
18
+ main_loop(Time.now)
19
+ sleep WAIT
20
+ }
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ end
data/lib/reac.rb ADDED
@@ -0,0 +1,96 @@
1
+ #
2
+ # reacruby - Reactive Programming on Ruby
3
+ #
4
+ # (c) 2008, yhara@kmc.gr.jp
5
+ require 'reac/reactivize.rb'
6
+ require 'reac/view.rb'
7
+
8
+ class Reac
9
+ attr_accessor :last_update
10
+
11
+ # construct tree of Reac::*
12
+ # (instead of call the method)
13
+ # to call it later
14
+ def method_missing(method, *args)
15
+ Call.new(self, method, args)
16
+ end
17
+
18
+ # caluculate the value
19
+ # (short-hand for Reac.value(x))
20
+ def value
21
+ Reac.value(self)
22
+ end
23
+
24
+ # caluculate the value by traversing the tree
25
+ def self.value(reac, tick=nil)
26
+ return reac if not reac.is_a?(Reac)
27
+
28
+ # return cached data if already calculated
29
+ return reac.data if tick && (reac.last_update == tick)
30
+
31
+ reac.last_update = tick
32
+ case reac
33
+ when Value
34
+ reac.data
35
+ when Call
36
+ receiver = Reac.value(reac.receiver, tick)
37
+ args = reac.args.map{|item|
38
+ Reac.value(item, tick)
39
+ }
40
+ reac.data = receiver.__send__(reac.method, *args)
41
+ when Proc
42
+ reac.data = reac.proc.call
43
+ when Array
44
+ reac.data = reac.ary.map{|item| Reac.value(item, tick)}
45
+ else
46
+ raise "must not happen"
47
+ end
48
+ end
49
+ undef :to_s
50
+ undef :inspect
51
+ undef :==
52
+
53
+ class Value < Reac
54
+ def initialize(data)
55
+ @data = data
56
+ end
57
+ attr_reader :data
58
+ end
59
+
60
+ class Call < Reac
61
+ def initialize(receiver, method, args)
62
+ @receiver, @method, @args = receiver, method, args
63
+ end
64
+ attr_accessor :data
65
+ attr_reader :receiver, :method, :args
66
+ end
67
+
68
+ class Proc < Reac
69
+ def initialize(proc)
70
+ @proc = proc
71
+ end
72
+ attr_accessor :data
73
+ attr_reader :proc
74
+ end
75
+
76
+ class Array < Reac
77
+ def initialize(ary)
78
+ @ary = ary
79
+ end
80
+ attr_accessor :data
81
+ attr_reader :ary
82
+ end
83
+ end
84
+
85
+ # construct reactive value from normal value (or proc)
86
+ def Reac(obj=nil, &block)
87
+ if block
88
+ Reac::Proc.new(block)
89
+ else
90
+ if obj.is_a?(Array)
91
+ Reac::Array.new(obj)
92
+ else
93
+ Reac::Value.new(obj)
94
+ end
95
+ end
96
+ end
Binary file
data/sample/clock.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'reac'
2
+ require 'reac/view/curses'
3
+
4
+ module Math
5
+ reactivize_singleton :sin, :cos
6
+ end
7
+
8
+ def rad(phase)
9
+ phase * (2 * Math::PI)
10
+ end
11
+
12
+ SIZE = 20
13
+ t = Reac{Time.now}
14
+
15
+ deg = (t.sec.to_f - 15) / 60
16
+ phase = rad(deg)
17
+ y = Math.sin(phase) * SIZE/2 + SIZE/2
18
+ x = Math.cos(phase) * SIZE + SIZE
19
+
20
+ Reac::CursesView.new{
21
+ put SIZE/2, SIZE-2, t.strftime("%H:%M")
22
+ put y, x, t.sec.to_s
23
+ }.start(30)
24
+
data/sample/icon.bmp ADDED
Binary file
data/sample/reacirb.rb ADDED
@@ -0,0 +1,103 @@
1
+ require 'readline'
2
+ require 'reac'
3
+ require 'reac/view/sdl'
4
+
5
+ # ruby 1.8/1.9 compatible
6
+ # ord(?a) #=> returns 97
7
+ def ord(x)
8
+ if x.is_a? String
9
+ x.ord
10
+ else
11
+ x
12
+ end
13
+ end
14
+
15
+ # call this before loading images, fonts, etc.
16
+ Reac::SDLView.init
17
+ view = Reac::SDLView.new
18
+
19
+ #font = SDL::BMFont.open("font.bmp", SDL::BMFont::TRANSPARENT)
20
+ font = SDL::TTF.open("ttf.ttf", 16)
21
+ #font = SDL::TTF.open("/Library/Fonts/Osaka.dfont", 16)
22
+
23
+ h = font.line_skip
24
+ n_lines = 0
25
+
26
+ #def method_missing(method, *args)
27
+ # eval("@#{method}")
28
+ #end
29
+
30
+ KEYS = {
31
+ SDL::Key::K1 => [ "1" , "!"] ,
32
+ SDL::Key::K2 => [ "2" , "\""] ,
33
+ SDL::Key::K3 => [ "3" , "#"] ,
34
+ SDL::Key::K4 => [ "4" , "$"] ,
35
+ SDL::Key::K5 => [ "5" , "%"] ,
36
+ SDL::Key::K6 => [ "6" , "&"] ,
37
+ SDL::Key::K7 => [ "7" , "'"] ,
38
+ SDL::Key::K8 => [ "8" , "("] ,
39
+ SDL::Key::K9 => [ "9" , ")"] ,
40
+ SDL::Key::K0 => [ "0" , "0"] ,
41
+ SDL::Key::MINUS => [ "-" , "="] ,
42
+ SDL::Key::CARET => [ "^" , "~"] ,
43
+ SDL::Key::BACKSLASH => [ "\\" , "_"] ,
44
+
45
+ SDL::Key::AT => [ "@" , "`"] ,
46
+ SDL::Key::LEFTBRACKET => [ "[" , "{"] ,
47
+
48
+ SDL::Key::SEMICOLON => [ ";" , "+"] ,
49
+ SDL::Key::COLON => [ ":" , "*"] ,
50
+ SDL::Key::RIGHTBRACKET => [ "]" , "}"] ,
51
+
52
+ SDL::Key::COMMA => [ "," , "<"] ,
53
+ SDL::Key::PERIOD => [ "." , ">"] ,
54
+ SDL::Key::SLASH => [ "/" , "?"] ,
55
+ }
56
+ str = "(press ESCAPE to exit)\n" +
57
+ "example: > @t = Reac{ Time.now }\n" +
58
+ "> "
59
+ header_size = str.split(/\n/).size
60
+ res = Reac{
61
+ while event = SDL::Event2.poll
62
+ if event.is_a?(SDL::Event2::KeyDown)
63
+ if KEYS.key?(event.sym)
64
+ if (event.mod & SDL::Key::MOD_SHIFT) == 0
65
+ str << KEYS[event.sym].first
66
+ else
67
+ str << KEYS[event.sym].last
68
+ end
69
+ else
70
+ case event.sym
71
+ when SDL::Key::BACKSPACE
72
+ str.chop!
73
+ when SDL::Key::A .. SDL::Key::Z
74
+ c = ord(?a) + (event.sym - SDL::Key::A)
75
+ if (event.mod & SDL::Key::MOD_SHIFT) != 0
76
+ c += (ord(?A) - ord(?a))
77
+ end
78
+ str << c.chr
79
+ when SDL::Key::COMMA .. SDL::Key::SEMICOLON
80
+ str << (ord(?,) + (event.sym - SDL::Key::COMMA)).chr
81
+ when SDL::Key::RETURN
82
+ code = str.split(/\n/).last[/^> (.*)/, 1]
83
+ y = h * (n_lines * 2 + header_size)
84
+ begin
85
+ view.put 0, y, self.instance_eval(code).inspect, font
86
+ rescue StandardError, ScriptError => e
87
+ view.put 0, y, "#{e.class}!"
88
+ end
89
+ n_lines += 1
90
+ str << "\n\n> "
91
+ when SDL::Key::SPACE
92
+ str << " "
93
+ when SDL::Key::ESCAPE
94
+ exit
95
+ end
96
+ end
97
+ end
98
+ end
99
+ str
100
+ }
101
+
102
+ view.put 0, 0, res, font
103
+ view.start
data/sample/sample.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'reac'
2
+ require 'reac/view/curses.rb'
3
+
4
+ x = Reac{rand(100)}
5
+ y = x * 2
6
+ t = Reac{Time.now}
7
+
8
+ #### Make a reactive world (declarative style):
9
+ Reac::CursesView.new{
10
+ put 0, 0, "x : " + x.to_s
11
+ put 1, 0, "y : " + y.to_s
12
+ put 2, 0, "x and y : " + Reac([x, y]).inspect
13
+
14
+ put 4, 0, "now : " + t.strftime("%H:%M:%S")
15
+ put 5, 0, "second * 100 : " + (t.sec * 100).to_s
16
+
17
+ }.start(3)
18
+
19
+ #### Another way (imperative style):
20
+ #
21
+ #view = Reac::CursesView.new
22
+ #view.put(0, 0, "x : " + x.to_s)
23
+ #view.put(1, 0, "y : " + y.to_s)
24
+ #view.put(2, 0, "x and y : " + Reac([x, y]).inspect)
25
+ #
26
+ #view.put(4, 0, "now : " + t.strftime("%H:%M:%S"))
27
+ #view.put(5, 0, "second * 100 : " + (t.sec * 100).to_s)
28
+ #
29
+ #view.start(3)
data/sample/sdltest.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'reac'
2
+ require 'reac/view/sdl.rb'
3
+
4
+ Reac::SDLView.init
5
+ img = SDL::Surface.load_bmp("icon.bmp")
6
+
7
+ x = Reac{ (Time.now.usec/5000) % 640 }
8
+ y = (x * 3) % 480
9
+
10
+ Reac::SDLView.new{
11
+ put x, y, img
12
+ }.start(5)
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reac
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - yhara (Yutaka HARA)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-07-07 00:00:00 +09:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: yhara.at.kmc.gr.jp
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README.rdoc
26
+ - lib/reac/reactivize.rb
27
+ - lib/reac/view/curses.rb
28
+ - lib/reac/view/sdl.rb
29
+ - lib/reac/view.rb
30
+ - lib/reac.rb
31
+ - sample/boxfont2.ttf
32
+ - sample/clock.rb
33
+ - sample/icon.bmp
34
+ - sample/reacirb.rb
35
+ - sample/sample.rb
36
+ - sample/sdltest.rb
37
+ has_rdoc: false
38
+ homepage:
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project: reac
59
+ rubygems_version: 1.2.0
60
+ signing_key:
61
+ specification_version: 2
62
+ summary: Reactive Programming in Ruby
63
+ test_files: []
64
+