reight 0.1.6 → 0.1.8

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
  3. data/CONTRIBUTING.md +7 -0
  4. data/ChangeLog.md +76 -0
  5. data/README.md +6 -5
  6. data/Rakefile +5 -2
  7. data/VERSION +1 -1
  8. data/bin/r8 +18 -8
  9. data/lib/reight/all.rb +7 -2
  10. data/lib/reight/app/chips.rb +124 -0
  11. data/lib/reight/app/map/brush.rb +1 -1
  12. data/lib/reight/app/map/brush_base.rb +3 -3
  13. data/lib/reight/app/map/canvas.rb +10 -15
  14. data/lib/reight/app/map/editor.rb +44 -17
  15. data/lib/reight/app/map/line.rb +5 -5
  16. data/lib/reight/app/map/rect.rb +2 -2
  17. data/lib/reight/app/map.rb +0 -1
  18. data/lib/reight/app/navigator.rb +33 -47
  19. data/lib/reight/app/runner.rb +123 -86
  20. data/lib/reight/app/sound/brush.rb +32 -0
  21. data/lib/reight/app/sound/canvas.rb +190 -0
  22. data/lib/reight/app/sound/editor.rb +170 -10
  23. data/lib/reight/app/sound/eraser.rb +28 -0
  24. data/lib/reight/app/sound/tool.rb +29 -0
  25. data/lib/reight/app/sound.rb +4 -0
  26. data/lib/reight/app/sprite/canvas.rb +23 -18
  27. data/lib/reight/app/sprite/color.rb +3 -1
  28. data/lib/reight/app/sprite/editor.rb +58 -47
  29. data/lib/reight/app/sprite/line.rb +1 -1
  30. data/lib/reight/app/sprite/shape.rb +1 -1
  31. data/lib/reight/app/sprite.rb +0 -1
  32. data/lib/reight/app.rb +24 -5
  33. data/lib/reight/button.rb +10 -7
  34. data/lib/reight/chip.rb +32 -6
  35. data/lib/reight/context.rb +168 -0
  36. data/lib/reight/helpers.rb +2 -2
  37. data/lib/reight/index.rb +87 -0
  38. data/lib/reight/map.rb +141 -11
  39. data/lib/reight/project.rb +45 -6
  40. data/lib/reight/reight.rb +11 -15
  41. data/lib/reight/sound.rb +238 -0
  42. data/lib/reight/sprite.rb +42 -0
  43. data/lib/reight/text.rb +124 -0
  44. data/lib/reight.rb +7 -3
  45. data/reight.gemspec +7 -7
  46. data/res/icons.png +0 -0
  47. data/test/helper.rb +16 -0
  48. data/test/test_map.rb +7 -7
  49. data/test/test_map_chunk.rb +6 -6
  50. data/test/test_sprite.rb +28 -0
  51. metadata +42 -32
  52. data/lib/reight/app/map/chips.rb +0 -84
  53. data/lib/reight/app/music/editor.rb +0 -25
  54. data/lib/reight/app/music.rb +0 -1
  55. data/lib/reight/app/sprite/chips.rb +0 -92
@@ -0,0 +1,238 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::Sound
5
+
6
+ include Enumerable
7
+
8
+ BPM_MAX = 999
9
+
10
+ def initialize(bpm = 120)
11
+ @bpm = bpm
12
+ @sequence = [[]]
13
+ end
14
+
15
+ attr_reader :bpm
16
+
17
+ def bpm=(bpm)
18
+ raise ArgumentError, "Invalid bpm: #{bpm}" if bpm <= 0
19
+ raise ArgumentError, "bpm exceeds the max value (#{BPM_MAX}): #{bpm}" if bpm > 999
20
+ @bpm = bpm
21
+ end
22
+
23
+ def play(gain: 1.0, &block)
24
+ return block&.call false if empty?
25
+ stop
26
+ @playing = sound = to_sound
27
+ sound.play gain: gain
28
+
29
+ if block
30
+ id = "__sound_playing_check_#{sound.object_id}"
31
+ set_interval 0.1, id: id do
32
+ next if sound.playing? == true
33
+ block.call true
34
+ clear_interval id
35
+ end
36
+ end
37
+ end
38
+
39
+ def stop()
40
+ @playing&.stop
41
+ @playing = nil
42
+ end
43
+
44
+ def clear()
45
+ @sequence = [[]]
46
+ end
47
+
48
+ def add_note(time_index, note_index, tone)
49
+ raise 'The note already exists' if note_at time_index, note_index
50
+ (@sequence[time_index] ||= []) << Note.new(note_index, tone)
51
+ end
52
+
53
+ def remove_note(time_index, note_index)
54
+ @sequence[time_index]&.delete_if {_1.index == note_index}
55
+ end
56
+
57
+ def note_at(time_index, note_index)
58
+ @sequence[time_index]&.find {_1.index == note_index}
59
+ end
60
+
61
+ def each_note(time_index: nil, &block)
62
+ return enum_for :each_note, time_index: time_index unless block
63
+ if time_index
64
+ @sequence[time_index]&.each do |note|
65
+ block.call note, time_index
66
+ end
67
+ else
68
+ @sequence.each.with_index do |notes, time_i|
69
+ notes&.each do |note|
70
+ block.call note, time_i
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ alias add add_note
77
+ alias remove remove_note
78
+ alias at note_at
79
+ alias each each_note
80
+
81
+ def playing?()
82
+ @playing = nil if @playing&.playing? == false
83
+ !!@playing
84
+ end
85
+
86
+ def empty? = @sequence.all? {!_1 || _1.empty?}
87
+
88
+ def to_hash()
89
+ {
90
+ bpm: @bpm,
91
+ sequence: @sequence.map {|notes| notes&.map {_1.to_hash}}
92
+ }
93
+ end
94
+
95
+ def self.restore(hash)
96
+ bpm, sequence = hash.values_at :bpm, :sequence
97
+ #hash => {bpm:, sequence:}
98
+ new(bpm).tap do |obj|
99
+ obj.instance_eval do
100
+ @sequence = sequence.map do |notes|
101
+ notes&.map {Note.restore _1}
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ def to_sound()
110
+ RubySketch::Sound.new Beeps::Sound.new(*sequencer)
111
+ end
112
+
113
+ def sequencer()
114
+ seq = Beeps::Sequencer.new
115
+ time = 0
116
+ prevs = {}
117
+ @sequence.each do |notes|
118
+ sec = Note.seconds 4, @bpm
119
+ new_prevs = {}
120
+ notes&.each do |note|
121
+ osc = Note.oscillator note.tone, 32
122
+ osc.freq = note.frequency
123
+ osc.phase = osc.freq * time
124
+ env = Note.envelope sec
125
+ seq.add osc >> env, time, sec
126
+
127
+ new_prevs[note.tone] = [note.index, env, sec]
128
+ pindex, penv, psec = prevs[note.tone]
129
+
130
+ if pindex && pindex == note.index
131
+ env.attack = 0
132
+ penv.release = 0
133
+ penv.note_off psec * 2# skip release phase
134
+ end
135
+ end
136
+ time += sec
137
+ prevs = new_prevs
138
+ end
139
+ return seq >> Note.gain, time
140
+ end
141
+
142
+ end# Sound
143
+
144
+
145
+ class Reight::Sound::Note
146
+
147
+ MAX = 127
148
+
149
+ TONES = %i[
150
+ sine triangle square sawtooth pulse12_5 pulse25 noise
151
+ ]
152
+
153
+ def initialize(index, tone = TONES.first)
154
+ raise "Invalid note index: #{index}" unless (0..MAX).include? index
155
+ raise "Invalid tone: #{tone}" unless TONES.include? tone
156
+ @index, @tone = index, tone
157
+ end
158
+
159
+ attr_reader :index, :tone
160
+
161
+ def play(bpm)
162
+ to_sound(bpm).play
163
+ end
164
+
165
+ def frequency()
166
+ 440 * (2 ** ((@index - 69).to_f / 12))
167
+ end
168
+
169
+ INDEX2NOTE = -> {
170
+ notes = %w[ c c+ d d+ e f f+ g g+ a a+ b ].map {_1.sub '+', '#'}
171
+ octaves = (-1..9).to_a
172
+ octaves.product(notes)
173
+ .each_with_object({}).with_index do |((octave, note), hash), index|
174
+ hash[index] = "#{note}#{octave}"
175
+ end
176
+ }.call
177
+
178
+ def to_s()
179
+ "#{INDEX2NOTE[@index]}:#{@tone}"
180
+ end
181
+
182
+ def to_hash()
183
+ {index: @index, tone: TONES.index(@tone)}
184
+ end
185
+
186
+ def to_sound(bpm)
187
+ osc = self.class.oscillator tone, 32, freq: frequency
188
+ sec = self.class.seconds 4, bpm
189
+ seq = Beeps::Sequencer.new.tap {_1.add osc, 0, sec}
190
+ env = self.class.envelope sec
191
+ gain = self.class.gain
192
+ RubySketch::Sound.new Beeps::Sound.new(seq >> env >> gain, sec)
193
+ end
194
+
195
+ def self.oscillator(type, size, **kwargs)
196
+ case type
197
+ when :noise then Beeps::Oscillator.new type, **kwargs
198
+ else
199
+ samples = (@samples ||= {})[type] ||= create_samples type, size
200
+ Beeps::Oscillator.new samples: samples, **kwargs
201
+ end
202
+ end
203
+
204
+ def self.create_samples(type, size)
205
+ input = size.times.map {_1.to_f / size}
206
+ duty = {pulse12_5: 0.125, pulse25: 0.25, pulse75: 0.75}[type] || 0.5
207
+ case type
208
+ when :sine then input.map {Math.sin _1 * Math::PI * 2}
209
+ when :triangle then input.map {_1 < 0.5 ? _1 * 4 - 1 : 3 - _1 * 4}
210
+ when :sawtooth then input.map {_1 * 2 - 1}
211
+ else input.map {_1 < duty ? 1 : -1}
212
+ end
213
+ end
214
+
215
+ def self.envelope(seconds)
216
+ Beeps::Envelope.new release: seconds * 0.05 do
217
+ note_on
218
+ note_off seconds * 0.95
219
+ end
220
+ end
221
+
222
+ def self.gain(gain = 0.2)
223
+ Beeps::Gain.new gain
224
+ end
225
+
226
+ def self.seconds(length, bpm)
227
+ raise ArgumentError, "Invalid length: #{length}" if length <= 0
228
+ raise ArgumentError, "Invalid bpm: #{bpm}" if bpm <= 0
229
+ 60.0 / bpm / length
230
+ end
231
+
232
+ def self.restore(hash)
233
+ index, tone = hash.values_at :index, :tone
234
+ #hash => {index:, tone:}
235
+ new index, TONES[tone]
236
+ end
237
+
238
+ end# Note
@@ -0,0 +1,42 @@
1
+ class Reight::Sprite < RubySketch::Sprite
2
+
3
+ def initialize(*a, chip: nil, **k, &b)
4
+ @chip, @props = chip, {}
5
+ super(*a, **k, &b)
6
+ end
7
+
8
+ attr_accessor :map_chunk
9
+
10
+ attr_reader :chip, :props
11
+
12
+ def prop(name, value = NilClass, **values)
13
+ @props[name] = value if value != NilClass
14
+ values.each {|k, v| @props[k] = v} unless values.empty?
15
+ @props[name]
16
+ end
17
+
18
+ def [](key)
19
+ @props[key]
20
+ end
21
+
22
+ def []=(key, value)
23
+ @props[key] = value
24
+ end
25
+
26
+ def respond_to_missing?(name, include_private = false)
27
+ name = name.to_s.delete_suffix('=').to_sym if name.end_with? '='
28
+ @props.key?(name) || super
29
+ end
30
+
31
+ def method_missing(name, *args, **kwargs, &block)
32
+ write = name.end_with? '='
33
+ key = write ? name.to_s.delete_suffix('=').to_sym : name
34
+ return super unless @props.key?(key)
35
+ if write
36
+ @props.[]=(key, *args)
37
+ else
38
+ @props[key]
39
+ end
40
+ end
41
+
42
+ end# Sprite
@@ -0,0 +1,124 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::Text
5
+
6
+ include Reight::Activatable
7
+ include Reight::Hookable
8
+ include Reight::HasHelp
9
+
10
+ def initialize(text = '', editable: false, align: LEFT, label: nil, regexp: nil, &changed)
11
+ hook :changed
12
+
13
+ super()
14
+ @editable, @align, @label, @regexp = editable, align, label, regexp
15
+ @shake = 0
16
+ self.changed(&changed) if changed
17
+
18
+ self.value = text
19
+ end
20
+
21
+ attr_accessor :editable, :align, :label
22
+
23
+ attr_reader :value
24
+
25
+ alias editable? editable
26
+
27
+ def revert()
28
+ self.value = @old_value
29
+ @shake = 6
30
+ end
31
+
32
+ def focus=(focus)
33
+ return if focus && !editable?
34
+ return if focus == focus?
35
+ sprite.capture = focus
36
+ unless focus
37
+ revert unless valid? value
38
+ changed! value, self if value != @old_value
39
+ end
40
+ end
41
+
42
+ def focus?()
43
+ sprite.capturing?
44
+ end
45
+
46
+ def value=(text)
47
+ str = text.to_s
48
+ return if str == @value
49
+ return unless valid? str
50
+ @value = str
51
+ end
52
+
53
+ def valid?(value = self.value, ignore_regexp: focus?)
54
+ case
55
+ when !value then false
56
+ when ignore_regexp then true
57
+ when !@regexp then true
58
+ else value =~ @regexp
59
+ end
60
+ end
61
+
62
+ def draw()
63
+ sp = sprite
64
+ no_stroke
65
+
66
+ if @shake != 0
67
+ translate rand(-@shake.to_f..@shake.to_f), 0
68
+ @shake *= rand(0.7..0.9)
69
+ @shake = 0 if @shake.abs < 0.1
70
+ end
71
+
72
+ fill focus? ? 230 : 200
73
+ rect 0, 0, sp.w, sp.h, 3
74
+
75
+ show_old = value == ''
76
+ text = show_old ? @old_value : value
77
+ text = label.to_s + text unless focus?
78
+ x = 2
79
+ fill show_old ? 200 : 50
80
+ text_align @align, CENTER
81
+ text text, x, 0, sp.w - x * 2, sp.h
82
+
83
+ if focus? && (frame_count % 60) < 30
84
+ fill 100
85
+ bounds = text_font.text_bounds value
86
+ xx = (@align == LEFT ? x + bounds.w : (sp.w + bounds.w) / 2) - 1
87
+ rect xx, (sp.h - bounds.h) / 2, 2, bounds.h
88
+ end
89
+ end
90
+
91
+ def key_pressed(key, code)
92
+ case code
93
+ when ESC then self.value = @old_value; self.focus = false
94
+ when ENTER then self.focus = false
95
+ when DELETE, BACKSPACE then self.value = value.split('').tap {_1.pop}.join
96
+ else self.value += key if key && valid?(key)
97
+ end
98
+ end
99
+
100
+ def clicked(x, y)
101
+ if focus?
102
+ return if hit? x, y
103
+ self.value = @old_value if value == '' && !valid?(ignore_regexp: false)
104
+ self.focus = false
105
+ elsif editable?
106
+ self.focus = true
107
+ @old_value, @value = @value.dup, ''
108
+ end
109
+ end
110
+
111
+ def hit?(x, y)
112
+ sp = sprite
113
+ (0...sp.w).include?(x) && (0...sp.h).include?(y)
114
+ end
115
+
116
+ def sprite()
117
+ @sprite ||= RubySketch::Sprite.new(physics: false).tap do |sp|
118
+ sp.draw {draw}
119
+ sp.key_pressed {key_pressed sp.key, sp.key_code}
120
+ sp.mouse_clicked {clicked sp.mouse_x, sp.mouse_y}
121
+ end
122
+ end
123
+
124
+ end# Text
data/lib/reight.rb CHANGED
@@ -4,9 +4,13 @@ require 'reight/all'
4
4
  begin
5
5
  w, c = Reight::WINDOW__, Reight::CONTEXT__
6
6
 
7
- c.class.constants.reject {_1 =~ /__$/}.each do |const|
8
- self.class.const_set const, c.class.const_get(const)
9
- end
7
+ reight_classes = %i[Sprite Sound]
8
+ c.class.constants
9
+ .reject {_1 =~ /__$/}
10
+ .reject {reight_classes.include? _1}
11
+ .each {self.class.const_set _1, c.class.const_get(_1)}
12
+ reight_classes
13
+ .each {self.class.const_set _1, Reight.const_get(_1)}
10
14
 
11
15
  w.__send__ :begin_draw
12
16
  at_exit do
data/reight.gemspec CHANGED
@@ -25,13 +25,13 @@ Gem::Specification.new do |s|
25
25
  s.platform = Gem::Platform::RUBY
26
26
  s.required_ruby_version = '>= 3.0.0'
27
27
 
28
- s.add_dependency 'xot', '~> 0.3.3', '>= 0.3.3'
29
- s.add_dependency 'rucy', '~> 0.3.3', '>= 0.3.3'
30
- s.add_dependency 'beeps', '~> 0.3.3', '>= 0.3.3'
31
- s.add_dependency 'rays', '~> 0.3.3', '>= 0.3.3'
32
- s.add_dependency 'reflexion', '~> 0.3.3', '>= 0.3.3'
33
- s.add_dependency 'processing', '~> 1.1', '>= 1.1.5'
34
- s.add_dependency 'rubysketch', '~> 0.7.6', '>= 0.7.6'
28
+ s.add_dependency 'xot', '~> 0.3.5', '>= 0.3.5'
29
+ s.add_dependency 'rucy', '~> 0.3.5', '>= 0.3.5'
30
+ s.add_dependency 'beeps', '~> 0.3.5', '>= 0.3.5'
31
+ s.add_dependency 'rays', '~> 0.3.5', '>= 0.3.5'
32
+ s.add_dependency 'reflexion', '~> 0.3.5', '>= 0.3.5'
33
+ s.add_dependency 'processing', '~> 1.1', '>= 1.1.7'
34
+ s.add_dependency 'rubysketch', '~> 0.7.8', '>= 0.7.8'
35
35
 
36
36
  s.files = `git ls-files`.split $/
37
37
  s.executables = s.files.grep(%r{^bin/}) {|f| File.basename f}
data/res/icons.png CHANGED
Binary file
data/test/helper.rb CHANGED
@@ -13,3 +13,19 @@ include Xot::Test
13
13
 
14
14
  R8 = Reight
15
15
  RS = RubySketch
16
+
17
+ class R8::Chip
18
+ alias <=> cmp__
19
+ end
20
+
21
+ class R8::ChipList
22
+ alias <=> cmp__
23
+ end
24
+
25
+ class R8::Map
26
+ alias <=> cmp__
27
+ end
28
+
29
+ class R8::Map::Chunk
30
+ alias <=> cmp__
31
+ end
data/test/test_map.rb CHANGED
@@ -81,12 +81,12 @@ class TestMap < Test::Unit::TestCase
81
81
  end
82
82
  end
83
83
 
84
- def test_delete()
84
+ def test_remove()
85
85
  [
86
86
  [0, 0], [10, 20], [90, 90]
87
87
  ].each do |xx, yy|
88
88
  map(chip_size: 10, chunk_size: 30).tap do |m|
89
- m.delete xx, yy
89
+ m.remove xx, yy
90
90
  assert_equal 0, count_all_chips(m)
91
91
  end
92
92
  end
@@ -100,7 +100,7 @@ class TestMap < Test::Unit::TestCase
100
100
  map(chip_size: 10, chunk_size: 30).tap do |m|
101
101
  m.put 10, 20, chip(0, 0, 10, 10)
102
102
  assert_equal 1, count_all_chips(m)
103
- m.delete xx, yy
103
+ m.remove xx, yy
104
104
  assert_equal count, count_all_chips(m)
105
105
  end
106
106
  end
@@ -115,17 +115,17 @@ class TestMap < Test::Unit::TestCase
115
115
  map(chip_size: 10, chunk_size: 30).tap do |m|
116
116
  m.put 10, 20, chip(0, 0, 20, 20)
117
117
  assert_equal 4, count_all_chips(m)
118
- m.delete xx, yy
118
+ m.remove xx, yy
119
119
  assert_equal count, count_all_chips(m)
120
120
  end
121
121
  end
122
122
  end
123
123
 
124
- def test_delete_chip()
124
+ def test_remove_chip()
125
125
  map(chip_size: 10, chunk_size: 30).tap do |m|
126
126
  m.put 10, 20, chip(0, 0, 10, 10)
127
127
  assert_equal 1, count_all_chips(m)
128
- m.delete_chip m[10, 20]
128
+ m.remove_chip m[10, 20]
129
129
  assert_equal 0, count_all_chips(m)
130
130
  end
131
131
  end
@@ -184,7 +184,7 @@ class TestMap < Test::Unit::TestCase
184
184
  img = image
185
185
  m1.put 10, 20, chip(0, 0, 10, 10, id: 1, image: img); assert_not_equal m1, m2
186
186
  m2.put 10, 20, chip(0, 0, 10, 10, id: 1, image: img); assert_equal m1, m2
187
- m2.delete 10, 20
187
+ m2.remove 10, 20
188
188
  m2.put 10, 20, chip(0, 0, 10, 10, id: 2, image: img); assert_not_equal m1, m2
189
189
  end
190
190
 
@@ -77,12 +77,12 @@ class TestMapChunk < Test::Unit::TestCase
77
77
  end
78
78
  end
79
79
 
80
- def test_delete()
80
+ def test_remove()
81
81
  [
82
82
  [0, 0], [20, 30], [90, 90]
83
83
  ].each do |xx, yy|
84
84
  chunk(10, 20, 30, 40, chip_size: 10).tap do |c|
85
- assert_nothing_raised {c.delete xx, yy}
85
+ assert_nothing_raised {c.remove xx, yy}
86
86
  assert_equal 0, count_all_chips(c)
87
87
  end
88
88
  end
@@ -96,7 +96,7 @@ class TestMapChunk < Test::Unit::TestCase
96
96
  chunk(10, 20, 30, 40, chip_size: 10).tap do |c|
97
97
  c.put 20, 30, chip(0, 0, 10, 10)
98
98
  assert_equal 1, count_all_chips(c)
99
- assert_nothing_raised {c.delete xx, yy}
99
+ assert_nothing_raised {c.remove xx, yy}
100
100
  assert_equal count, count_all_chips(c)
101
101
  end
102
102
  end
@@ -111,7 +111,7 @@ class TestMapChunk < Test::Unit::TestCase
111
111
  chunk(10, 20, 30, 40, chip_size: 10).tap do |c|
112
112
  c.put 20, 30, chip(0, 0, 20, 20)
113
113
  assert_equal 4, count_all_chips(c)
114
- assert_nothing_raised {c.delete xx, yy}
114
+ assert_nothing_raised {c.remove xx, yy}
115
115
  assert_equal count, count_all_chips(c)
116
116
  end
117
117
  end
@@ -123,7 +123,7 @@ class TestMapChunk < Test::Unit::TestCase
123
123
  c.put 10, 20, chip(0, 0, 10, 10)
124
124
  assert_equal 8, c.to_hash[:chips].size
125
125
 
126
- c.delete 10, 20
126
+ c.remove 10, 20
127
127
  assert_equal 5, c.to_hash[:chips].size
128
128
  end
129
129
  end
@@ -191,7 +191,7 @@ class TestMapChunk < Test::Unit::TestCase
191
191
  img = image
192
192
  c1.put 10, 20, chip(0, 0, 10, 10, id: 1, image: img); assert_not_equal c1, c2
193
193
  c2.put 10, 20, chip(0, 0, 10, 10, id: 1, image: img); assert_equal c1, c2
194
- c2.delete 10, 20
194
+ c2.remove 10, 20
195
195
  c2.put 10, 20, chip(0, 0, 10, 10, id: 2, image: img); assert_not_equal c1, c2
196
196
  end
197
197
 
@@ -0,0 +1,28 @@
1
+ require_relative 'helper'
2
+ using Reight
3
+
4
+
5
+ class TestSpeite < Test::Unit::TestCase
6
+
7
+ def sprite(...) = R8::Sprite.new(...)
8
+
9
+ def test_prop()
10
+ assert_raise(NoMethodError) {sprite.foo}
11
+ assert_raise(NoMethodError) {sprite.foo = 9}
12
+
13
+ sp = sprite
14
+ assert_nothing_raised {sp[:foo] = 1}
15
+ assert_nothing_raised {sp .foo}
16
+ assert_equal 1, sp[:foo]
17
+ assert_equal 1, sp .foo
18
+
19
+ assert_nothing_raised {sp .foo = 2}
20
+ assert_equal 2, sp[:foo]
21
+ assert_equal 2, sp .foo
22
+
23
+ assert_nothing_raised {sp .foo = 3, 4}
24
+ assert_equal [3, 4], sp[:foo]
25
+ assert_equal [3, 4], sp .foo
26
+ end
27
+
28
+ end# TestChip