fsevent 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.
@@ -0,0 +1,34 @@
1
+ # periodicschedule.rb --- periodic schedule
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::PeriodicSchedule
19
+ def initialize(initial_time, interval)
20
+ @initial_time = initial_time
21
+ @interval = interval
22
+ @n = 0
23
+ end
24
+
25
+ def first
26
+ @initial_time + @n * @interval
27
+ end
28
+
29
+ def shift
30
+ t = first
31
+ @n += 1
32
+ t
33
+ end
34
+ end
@@ -0,0 +1,93 @@
1
+ # processdevice.rb --- run device on another process
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::ProcessDevice < FSEvent::AbstractDevice
19
+ def self.spawner(defs)
20
+ Spawner.new(defs)
21
+ end
22
+
23
+ class Spawner
24
+ def initialize(defs)
25
+ @defs = defs
26
+ end
27
+
28
+ def assemble_code(device_name, *args)
29
+ code = ''
30
+
31
+ libpath = File.dirname(File.dirname(__FILE__))
32
+ code << "$:.unshift #{libpath.dump}\n"
33
+
34
+ code << <<-'End'
35
+ require 'fsevent'
36
+ def ep(arg) STDERR.puts arg.inspect end
37
+ End
38
+
39
+ code << "class FSEvent::ProcessDevice_#{device_name} < FSEvent::ProcessDeviceC\n#{@defs}\nend\n"
40
+ marshaled_args = Marshal.dump(args)
41
+ code << "FSEvent::ProcessDeviceC.main(FSEvent::ProcessDevice_#{device_name}.new(#{device_name.dump}, *(Marshal.load(#{marshaled_args.dump}))))\n"
42
+ end
43
+
44
+ def new(device_name, *args)
45
+ code = assemble_code(device_name, *args)
46
+ io = IO.popen([RbConfig.ruby], "r+")
47
+ io.sync = true
48
+ io.write code
49
+ io.write "__END__\n"
50
+ FSEvent::ProcessDevice.send(:new, device_name, io)
51
+ end
52
+ end
53
+
54
+ class << self
55
+ private :new
56
+ end
57
+
58
+ def initialize(device_name, io)
59
+ super device_name
60
+ @io = io
61
+ end
62
+
63
+ def call_subprocess(method, *args)
64
+ Marshal.dump([:call_child, method, *args], @io)
65
+ msgtype, *rest = Marshal.load(@io)
66
+ while msgtype == :call_parent
67
+ method, *args = rest
68
+ ret = @framework.send(method, *args)
69
+ Marshal.dump([:return_to_child, ret], @io)
70
+ msgtype, *rest = Marshal.load(@io)
71
+ end
72
+ if msgtype != :return_to_parent
73
+ raise RuntimeError, "unexpected message type: #{msgtype.inspect}"
74
+ end
75
+ rest[0]
76
+ end
77
+ #private :call_subprocess
78
+
79
+ def finish
80
+ @io.close_write
81
+ @io.close
82
+ nil
83
+ end
84
+
85
+ def framework=(framework)
86
+ @framework = framework
87
+ call_subprocess(:processdevice_framework_set)
88
+ end
89
+
90
+ def registered
91
+ call_subprocess(:processdevice_registered)
92
+ end
93
+ end
@@ -0,0 +1,79 @@
1
+ # processdevicec.rb --- child process code for processdevice.rb
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::ProcessDeviceC < FSEvent::AbstractDevice
19
+ class StubFramework
20
+ def initialize(obj)
21
+ @obj = obj
22
+ end
23
+
24
+ def add_watch(watchee_device_name, status_name)
25
+ @obj.call_parent(:add_watch, watchee_device_name, status_name)
26
+ end
27
+
28
+ def status_changed(status_name, value)
29
+ @obj.call_parent(:status_changed, status_name, value)
30
+ end
31
+ end
32
+
33
+ def initialize(device_name)
34
+ super(device_name)
35
+ end
36
+
37
+ def processdevice_framework_set
38
+ framework = StubFramework.new(self)
39
+ self.framework = framework
40
+ nil
41
+ end
42
+
43
+ def processdevice_registered
44
+ registered
45
+ nil
46
+ end
47
+
48
+ def call_parent(method, *args)
49
+ Marshal.dump([:call_parent, method, *args], STDOUT)
50
+ msgtype, *rest = Marshal.load(STDIN)
51
+ while msgtype == :call_child
52
+ method, *args = rest
53
+ ret = self.send(method, *args)
54
+ Marshal.dump([:return_to_parent, ret], STDOUT)
55
+ msgtype, *rest = Marshal.load(STDIN)
56
+ end
57
+ if msgtype != :return_to_child
58
+ raise RuntimeError, "unexpected message type: #{msgtype.inspect}"
59
+ end
60
+ rest[0]
61
+ end
62
+
63
+ def self.main(this_device)
64
+ STDOUT.sync = true
65
+ while true
66
+ begin
67
+ msgtype, *rest = Marshal.load(STDIN)
68
+ rescue EOFError
69
+ exit true
70
+ end
71
+ if msgtype != :call_child
72
+ raise RuntimeError, "unexpected message type: #{msgtype.inspect}"
73
+ end
74
+ method, *args = rest
75
+ ret = this_device.send(method, *args)
76
+ Marshal.dump([:return_to_parent, ret], STDOUT)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,48 @@
1
+ # schedulemerger.rb --- merge multiple schedules
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::ScheduleMerger
19
+ def initialize(*schedules)
20
+ @q = Depq.new
21
+ schedules.each {|s|
22
+ merge_schedule(s)
23
+ }
24
+ end
25
+
26
+ def merge_schedule(s)
27
+ t = s.shift
28
+ if t
29
+ @q.insert s, t
30
+ end
31
+ end
32
+
33
+ def first
34
+ return nil if @q.empty?
35
+ @q.find_min_priority[1]
36
+ end
37
+
38
+ def shift
39
+ return nil if @q.empty?
40
+ s, t = @q.delete_min_priority
41
+ t2 = s.shift
42
+ if t2
43
+ @q.insert s, t2
44
+ end
45
+ t
46
+ end
47
+ end
48
+
@@ -0,0 +1,46 @@
1
+ # simpledevice.rb --- simple device definition using constructor arguments
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ class FSEvent::SimpleDevice < FSEvent::AbstractDevice
19
+ def initialize(device_name, initial_status, watches, registered_elapsed_time, schedule=nil, &run_block)
20
+ super device_name
21
+ @name = device_name
22
+ @initial_status = initial_status
23
+ @watches = watches
24
+ @registered_elapsed_time = registered_elapsed_time
25
+ @schedule.merge_schedule schedule if schedule
26
+ @run_block = run_block
27
+ end
28
+ attr_writer :registered_elapsed_time
29
+
30
+ def registered
31
+ @initial_status.each {|status_name, value|
32
+ define_status status_name, value
33
+ }
34
+ @watches.each {|watchee_device_name, status_name, reaction|
35
+ reaction ||= :immediate
36
+ add_watch watchee_device_name, status_name, reaction
37
+ }
38
+ if @registered_elapsed_time
39
+ set_elapsed_time @registered_elapsed_time
40
+ end
41
+ end
42
+
43
+ def run(watched_status_change)
44
+ @run_block.call watched_status_change
45
+ end
46
+ end
@@ -0,0 +1,77 @@
1
+ # util.rb --- various utilities
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module FSEvent::Util
19
+ module_function
20
+
21
+ def nested_hash(n)
22
+ if n == 1
23
+ {}
24
+ else
25
+ Hash.new {|h, k|
26
+ h[k] = nested_hash(n-1)
27
+ }
28
+ end
29
+ end
30
+
31
+ def nonempty_hash(h, level)
32
+ return nil if h.nil?
33
+ result = {}
34
+ h.each {|k, v|
35
+ if level == 1
36
+ result[k] = v
37
+ else
38
+ h2 = nonempty_hash(v, level-1)
39
+ if h2
40
+ result[k] = h2
41
+ end
42
+ end
43
+ }
44
+ if result.empty?
45
+ nil
46
+ else
47
+ result
48
+ end
49
+ end
50
+
51
+ def reaction_immediate_at_beginning?(reaction)
52
+ case reaction
53
+ when :immediate
54
+ true
55
+ when :immediate_only_at_beginning
56
+ true
57
+ when :schedule
58
+ false
59
+ else
60
+ raise "unexpected reaction: #{reaction.inspect}"
61
+ end
62
+ end
63
+
64
+ def reaction_immediate_at_subsequent?(reaction)
65
+ case reaction
66
+ when :immediate
67
+ true
68
+ when :immediate_only_at_beginning
69
+ false
70
+ when :schedule
71
+ false
72
+ else
73
+ raise "unexpected reaction: #{reaction.inspect}"
74
+ end
75
+ end
76
+
77
+ end
data/sample/repeat.rb ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'time'
4
+ require 'fsevent'
5
+
6
+ class Repeater < FSEvent::AbstractDevice
7
+ def initialize(name, schedule)
8
+ super name
9
+ @schedule = schedule
10
+ end
11
+
12
+ def run(watched_status_change)
13
+ @framework.set_elapsed_time(1)
14
+ p @framework.current_time.iso8601(3)
15
+ end
16
+ end
17
+
18
+ schedule = FSEvent::PeriodicSchedule.new(Time.utc(2000), 10)
19
+ repeat = Repeater.new("repeat", schedule)
20
+
21
+ fsevent = FSEvent.new(Time.utc(2000))
22
+ fsevent.register_device(repeat)
23
+ fsevent.start
24
+
data/sample/repeat2.rb ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'fsevent'
4
+
5
+ class Repeater < FSEvent::AbstractDevice
6
+ def initialize(name, schedule)
7
+ super name
8
+ @schedule = schedule
9
+ end
10
+
11
+ def run(watched_status_change)
12
+ p [self.name, @framework.current_time]
13
+ end
14
+ end
15
+
16
+ repeat1 = Repeater.new("repeat1", FSEvent::PeriodicSchedule.new(Time.now, 3))
17
+ repeat2 = Repeater.new("repeat2", FSEvent::PeriodicSchedule.new(Time.now, 10))
18
+
19
+ fsevent = FSEvent.new
20
+ fsevent.register_device(repeat1)
21
+ fsevent.register_device(repeat2)
22
+ fsevent.start
23
+
@@ -0,0 +1,159 @@
1
+ # test_failsafedevice.rb --- tests for fail safe device
2
+ #
3
+ # Copyright (C) 2014 National Institute of Advanced Industrial Science and Technology (AIST)
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'test/unit'
19
+ require 'fsevent'
20
+
21
+ class TestFSEventFailSafeDevice < Test::Unit::TestCase
22
+ class SrcDevice < FSEvent::AbstractDevice
23
+ def initialize(device_name, init, pairs)
24
+ super device_name
25
+ @init = init
26
+ @elapsed = 1
27
+ @schedule = pairs.map {|t, v| t-@elapsed }
28
+ @values = pairs.map {|t, v| v }
29
+ @test_result = []
30
+ end
31
+ attr_reader :test_result
32
+
33
+ def registered
34
+ set_elapsed_time(1)
35
+ define_status("s", @init)
36
+ end
37
+
38
+ def run(watched_status_change)
39
+ @test_result << [@framework.current_time, @values.first]
40
+ set_elapsed_time(@elapsed)
41
+ status_changed "s", @values.shift
42
+ end
43
+ end
44
+
45
+ def test_srcdevice
46
+ t = Time.utc(2000)
47
+ fsevent = FSEvent.new(t)
48
+ srcdevice = SrcDevice.new("src", 0,
49
+ [[t+10,8],
50
+ [t+20,2],
51
+ [t+30,3],
52
+ [t+40,5],
53
+ [t+50,1]])
54
+ fsevent.register_device(srcdevice)
55
+ fsevent.start
56
+ assert_equal(
57
+ [[t+9,8],
58
+ [t+19,2],
59
+ [t+29,3],
60
+ [t+39,5],
61
+ [t+49,1]],
62
+ srcdevice.test_result)
63
+ end
64
+
65
+ class FailSafeDeviceT < FSEvent::FailSafeDevice
66
+ def registered
67
+ super
68
+ set_elapsed_time(1)
69
+ end
70
+
71
+ def run(watched_status_change)
72
+ set_elapsed_time(1)
73
+ super(watched_status_change)
74
+ end
75
+ end
76
+
77
+ class DstDevice < FSEvent::AbstractDevice
78
+ def initialize(device_name, watchee_device_name, watchee_status)
79
+ super device_name
80
+ @watchee_device_name = watchee_device_name
81
+ @watchee_status = watchee_status
82
+ @test_result = []
83
+ end
84
+ attr_reader :test_result
85
+
86
+ def registered
87
+ super
88
+ set_elapsed_time(1)
89
+ add_watch(@watchee_device_name, @watchee_status)
90
+ end
91
+
92
+ def run(watched_status_change)
93
+ set_elapsed_time(1)
94
+ @test_result << [@framework.current_time, watched_status_change]
95
+ end
96
+ end
97
+
98
+ def test_failsafe1
99
+ t = Time.utc(2000)
100
+ fsevent = FSEvent.new(t)
101
+ src1 = SrcDevice.new("src1", 0,
102
+ [[t+10,9],
103
+ [t+20,2],
104
+ [t+30,3],
105
+ [t+40,5],
106
+ [t+50,1]])
107
+ fsd = FailSafeDeviceT.new("fs",
108
+ [["s", 0, lambda {|cur, val| val }]],
109
+ "src1")
110
+ dst = DstDevice.new("dst", "fs", "s")
111
+ fsevent.register_device(src1)
112
+ fsevent.register_device(fsd)
113
+ fsevent.register_device(dst)
114
+ fsevent.start
115
+ assert_equal(
116
+ [[t + 1, {"fs"=>{"s"=>0}}],
117
+ [t + 11, {"fs"=>{"s"=>9}}],
118
+ [t + 21, {"fs"=>{"s"=>2}}],
119
+ [t + 31, {"fs"=>{"s"=>3}}],
120
+ [t + 41, {"fs"=>{"s"=>5}}],
121
+ [t + 51, {"fs"=>{"s"=>1}}]],
122
+ dst.test_result)
123
+ end
124
+
125
+ def test_failsafe2_max
126
+ t = Time.utc(2000)
127
+ fsevent = FSEvent.new(t)
128
+ src1 = SrcDevice.new("src1", 0,
129
+ [[t+10,9],
130
+ [t+20,2],
131
+ [t+30,3],
132
+ [t+40,5],
133
+ [t+50,1]])
134
+ src2 = SrcDevice.new("src2", 0,
135
+ [[t+11,9],
136
+ [t+19,2],
137
+ [t+31,3],
138
+ [t+39,5],
139
+ [t+51,1]])
140
+ fsd = FailSafeDeviceT.new("fs",
141
+ [["s", 0, :max]],
142
+ "src1", "src2")
143
+ dst = DstDevice.new("dst", "fs", "s")
144
+ fsevent.register_device(src1)
145
+ fsevent.register_device(src2)
146
+ fsevent.register_device(fsd)
147
+ fsevent.register_device(dst)
148
+ fsevent.start
149
+ assert_equal(
150
+ [[t + 1, {"fs"=>{"s"=>0}}],
151
+ [t + 10+1, {"fs"=>{"s"=>9}}],
152
+ [t + 20+1, {"fs"=>{"s"=>2}}],
153
+ [t + 30+1, {"fs"=>{"s"=>3}}],
154
+ [t + 39+1, {"fs"=>{"s"=>5}}],
155
+ [t + 51+1, {"fs"=>{"s"=>1}}]],
156
+ dst.test_result)
157
+ end
158
+
159
+ end