reac 0.1.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/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
+