smalruby-editor 0.1.20 → 0.1.21

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/app/assets/javascripts/blocks/character.js.coffee.erb +3 -10
  4. data/app/assets/javascripts/blocks/control.js.coffee.erb +2 -1
  5. data/app/assets/javascripts/blocks/data.js.coffee.erb +2 -1
  6. data/app/assets/javascripts/blocks/etc.js.coffee.erb +2 -1
  7. data/app/assets/javascripts/blocks/events.js.coffee.erb +5 -4
  8. data/app/assets/javascripts/blocks/field_character.js.coffee.erb +9 -0
  9. data/app/assets/javascripts/blocks/hardware.js.coffee.erb +171 -54
  10. data/app/assets/javascripts/blocks/looks.js.coffee.erb +2 -1
  11. data/app/assets/javascripts/blocks/motion.js.coffee.erb +4 -16
  12. data/app/assets/javascripts/blocks/operators.js.coffee.erb +2 -1
  13. data/app/assets/javascripts/blocks/pen.js.coffee.erb +2 -1
  14. data/app/assets/javascripts/blocks/ruby.js.coffee.erb +2 -1
  15. data/app/assets/javascripts/blocks/sensing.js.coffee.erb +25 -3
  16. data/app/assets/javascripts/blocks/sound.js.coffee.erb +2 -1
  17. data/app/assets/javascripts/generators/ruby.js.coffee.erb +27 -5
  18. data/app/assets/javascripts/models/source_code.js.coffee +20 -7
  19. data/app/assets/javascripts/views/main_menu_view.js.coffee +5 -7
  20. data/app/assets/stylesheets/editor.css.scss +5 -1
  21. data/app/assets/stylesheets/toolbox.css.scss.erb +29 -24
  22. data/app/controllers/source_codes_controller.rb +7 -36
  23. data/app/helpers/editor_helper.rb +9 -0
  24. data/app/models/concerns/ruby_to_block/block/hardware_button_down_or_up.rb +25 -0
  25. data/app/models/concerns/ruby_to_block/block/hardware_button_not_use_pullup.rb +18 -0
  26. data/app/models/concerns/ruby_to_block/block/hardware_motor_driver.rb +22 -0
  27. data/app/models/concerns/ruby_to_block/block/hardware_motor_driver_set_speed.rb +20 -0
  28. data/app/models/concerns/ruby_to_block/block/hardware_motor_driver_speed.rb +24 -0
  29. data/app/models/concerns/ruby_to_block/block/hardware_on_button_down_or_up.rb +19 -0
  30. data/app/models/concerns/ruby_to_block/block/hardware_operation.rb +15 -0
  31. data/app/models/concerns/ruby_to_block/block/hardware_two_wheel_drive_car_set_speed.rb +22 -0
  32. data/app/models/concerns/ruby_to_block/block/hardware_two_wheel_drive_car_speed.rb +26 -0
  33. data/app/models/concerns/ruby_to_block/block/ruby_p.rb +19 -0
  34. data/app/models/concerns/ruby_to_block/block/{motion_reach_wall.rb → sensing_reach_wall.rb} +1 -1
  35. data/app/models/concerns/ruby_to_block/block/text.rb +3 -2
  36. data/app/models/source_code.rb +20 -0
  37. data/app/views/editor/_toolbox.html.haml +84 -63
  38. data/app/views/editor/index.html.haml +1 -1
  39. data/demos/adjust_2wd_car.rb.xml +179 -0
  40. data/demos/car_chase.rb.xml +129 -126
  41. data/demos/hardware_led.rb.xml +7 -4
  42. data/demos/pong.rb.xml +17 -14
  43. data/demos/star.rb.xml +115 -112
  44. data/lib/smalruby_editor.rb +16 -0
  45. data/lib/smalruby_editor/version.rb +3 -3
  46. data/public/assets/{application-c8991557a0789ac1bc9220f409e7f1c1.js → application-734570836cdba680c1568220a51af40f.js} +216 -86
  47. data/public/assets/{application-c8991557a0789ac1bc9220f409e7f1c1.js.gz → application-734570836cdba680c1568220a51af40f.js.gz} +0 -0
  48. data/public/assets/{application-d4fe6c0f970efe8eab9267fd08718d96.css → application-898cb749439fba4ce44cffeac1216201.css} +37 -9
  49. data/public/assets/{application-d4fe6c0f970efe8eab9267fd08718d96.css.gz → application-898cb749439fba4ce44cffeac1216201.css.gz} +0 -0
  50. data/public/assets/manifest-332a5a1668194028b55103e0ea45c054.json +1 -1
  51. data/smalruby-editor.gemspec +2 -1
  52. data/spec/acceptance/block_mode/blocks/events/on_hit.feature +2 -0
  53. data/spec/acceptance/block_mode/blocks/hardware/button_down_or_up.feature +79 -0
  54. data/spec/acceptance/block_mode/blocks/hardware/button_not_use_pullup.feature +61 -0
  55. data/spec/acceptance/block_mode/blocks/hardware/motor_driver.feature +73 -0
  56. data/spec/acceptance/block_mode/blocks/hardware/motor_driver_speed.feature +93 -0
  57. data/spec/acceptance/block_mode/blocks/hardware/on_button_down_or_up.feature +87 -0
  58. data/spec/acceptance/block_mode/blocks/hardware/two_wheel_drive_car_speed.feature +111 -0
  59. data/spec/acceptance/block_mode/blocks/motion/reach_wall.feature +1 -1
  60. data/spec/acceptance/block_mode/blocks/operators/text.feature +38 -0
  61. data/spec/acceptance/block_mode/blocks/sensing/hit.feature +98 -52
  62. data/spec/acceptance/block_mode/blocks/sensing/reach_wall.feature +98 -0
  63. data/spec/acceptance/block_mode/demo.feature +3 -1
  64. data/spec/acceptance/standalone/save.feature +51 -0
  65. data/spec/helpers/editor_helper_spec.rb +20 -0
  66. data/spec/javascripts/models/source_code_spec.coffee +40 -0
  67. data/spec/models/concerns/ruby_to_block/block/hardware_spec.rb +269 -0
  68. data/spec/models/concerns/ruby_to_block/block/motion_spec.rb +1 -1
  69. data/spec/models/concerns/ruby_to_block/block/{motion_reach_wall_spec.rb → sensing_reach_wall_spec.rb} +2 -2
  70. data/spec/models/concerns/ruby_to_block/block/text_spec.rb +39 -0
  71. data/spec/models/concerns/ruby_to_block_spec.rb +2 -2
  72. data/spec/models/source_code_spec.rb +53 -0
  73. data/spec/steps/base_steps.rb +5 -0
  74. metadata +56 -12
  75. data/demos/rgb_led_anode.rb.xml +0 -83
@@ -2,7 +2,8 @@
2
2
 
3
3
  <%
4
4
  category = 'operators'
5
- color = 100
5
+ require 'smalruby_editor'
6
+ color = SmalrubyEditor::COLORS[category.to_sym]
6
7
  %>
7
8
 
8
9
  # 変数:( ) + ( )
@@ -2,7 +2,8 @@
2
2
 
3
3
  <%
4
4
  category = 'pen'
5
- color = 160
5
+ require 'smalruby_editor'
6
+ color = SmalrubyEditor::COLORS[category.to_sym]
6
7
  %>
7
8
 
8
9
  # ペンを下ろす
@@ -2,7 +2,8 @@
2
2
 
3
3
  <%
4
4
  category = 'ruby'
5
- color = 340
5
+ require 'smalruby_editor'
6
+ color = SmalrubyEditor::COLORS[category.to_sym]
6
7
  %>
7
8
 
8
9
  # 文
@@ -2,7 +2,8 @@
2
2
 
3
3
  <%
4
4
  category = 'sensing'
5
- color = 190
5
+ require 'smalruby_editor'
6
+ color = SmalrubyEditor::COLORS[category.to_sym]
6
7
  %>
7
8
 
8
9
  # 調べるの環境設定
@@ -26,6 +27,27 @@ window.SmalrubyEditor.Sensing =
26
27
  ['中ボタン', 'M_MBUTTON'],
27
28
  ['右ボタン', 'M_RBUTTON'],
28
29
  ]
30
+ DownOrUpDropdown: [
31
+ ['押された', 'down'],
32
+ ['離された', 'up'],
33
+ ]
34
+
35
+ # 条件:端に触れた
36
+ <% n = "#{category}_reach_wall" %>
37
+ Blockly.Blocks['<%= n %>'] =
38
+ init: ()->
39
+ @setHelpUrl('')
40
+ @setColour(<%= color %>)
41
+ @appendDummyInput().appendField('端に触れた')
42
+ @setOutput(true, 'Boolean')
43
+ @setTooltip('')
44
+
45
+ Blockly.Ruby['<%= n %>'] = (block) ->
46
+ Blockly.Ruby.characterMethodCallInput_('reach_wall?')
47
+
48
+ # HACK: 後方互換性のため、motion_reach_wallとして複製する
49
+ Blockly.Blocks['motion_reach_wall'] = Blockly.Blocks['<%= n %>']
50
+ Blockly.Ruby['motion_reach_wall'] = Blockly.Ruby['<%= n %>']
29
51
 
30
52
  # 条件:キーボードの[▼キー]が[▼押された]
31
53
  <% n = "#{category}_input_key_push_or_down" %>
@@ -104,7 +126,7 @@ Blockly.Blocks['<%= n %>'] =
104
126
  @setTooltip('')
105
127
 
106
128
  Blockly.Ruby['<%= n %>'] = (block) ->
107
- char = @getFieldValue('CHAR')
129
+ char = @getCharacterFieldValue()
108
130
  Blockly.Ruby.characterMethodCallInput_('hit?', char)
109
131
 
110
132
  # [ ]と聞いて待つ
@@ -192,7 +214,7 @@ Blockly.Blocks['<%= n %>'] =
192
214
  @setTooltip('')
193
215
 
194
216
  Blockly.Ruby['<%= n %>'] = (block) ->
195
- char = @getFieldValue('CHAR')
217
+ char = @getCharacterFieldValue()
196
218
  property = @getFieldValue('PROPERTY')
197
219
  c = Smalruby.Collections.CharacterSet.findWhere({ name: char })
198
220
  Blockly.Ruby.characterMethodCallInput_(property, null, { object: c })
@@ -2,7 +2,8 @@
2
2
 
3
3
  <%
4
4
  category = 'sound'
5
- color = 300
5
+ require 'smalruby_editor'
6
+ color = SmalrubyEditor::COLORS[category.to_sym]
6
7
  %>
7
8
 
8
9
  # [▼プリセット音声]
@@ -51,6 +51,20 @@ Blockly.Ruby.init = ->
51
51
  @definitions_['receiver_stack'] = ['main']
52
52
  @definitions_['character_stack'] = []
53
53
 
54
+ Blockly.Ruby.defineCharacter = (c) ->
55
+ name = c.get('name')
56
+ blockName = "character_#{name}"
57
+ if !Blockly.Ruby.definitions_[blockName]
58
+ switch c.get('rotationStyle')
59
+ when 'left_right'
60
+ rotationStyle = ', rotation_style: :left_right'
61
+ when 'none'
62
+ rotationStyle = ', rotation_style: :none'
63
+ else
64
+ rotationStyle = ''
65
+ Blockly.Ruby.definitions_[blockName] =
66
+ "#{name} = Character.new(costume: #{Blockly.Ruby.quote_(c.costume())}, x: #{c.get('x')}, y: #{c.get('y')}, angle: #{c.get('angle')}#{rotationStyle})"
67
+
54
68
  Blockly.Ruby.characterStack = ->
55
69
  @definitions_['character_stack']
56
70
 
@@ -159,12 +173,20 @@ Blockly.Ruby.finish = (code) ->
159
173
  Blockly.Ruby.scrubNakedValue = (line) ->
160
174
  line + '\n'
161
175
 
176
+ Blockly.Ruby.escapeChars_ =
177
+ '"': '\\"'
178
+
162
179
  Blockly.Ruby.quote_ = (string) ->
163
- # TODO: 実装すること
164
- # stringに"を含んでいたら、aから順番にstringに含まれるか試して、含ま
165
- # れていない文字を使って%Qa...aとしようかな。でも生成したソースコード
166
- # の可読性が下がりますね。
167
- '\"' + string + '\"'
180
+ # copy and modified goog.string.quote:
181
+ # http://docs.closure-library.googlecode.com/git/namespace_goog_string.html
182
+ s = String(string)
183
+ sb = ['"']
184
+ for i in [0...s.length]
185
+ do (i) ->
186
+ ch = s.charAt(i)
187
+ sb[i + 1] = Blockly.Ruby.escapeChars_[ch] || ch
188
+ sb.push('"')
189
+ sb.join('')
168
190
 
169
191
  Blockly.Ruby.scrub_ = (block, code) ->
170
192
  if code == null
@@ -15,15 +15,28 @@ Smalruby.SourceCode = Backbone.Model.extend
15
15
  filename = '1.rb'
16
16
  @set('filename', filename)
17
17
 
18
- if @get('filename').match(/\.xml$/)
19
- data = Smalruby.dumpXml()
20
- else
21
- if window.blockMode
22
- data = Blockly.Ruby.workspaceToCode()
18
+ unless @get('data')
19
+ if @get('filename').match(/\.xml$/)
20
+ data = Smalruby.dumpXml()
23
21
  else
24
- data = window.textEditor.getSession().getDocument().getValue()
22
+ if window.blockMode
23
+ data = Blockly.Ruby.workspaceToCode()
24
+ else
25
+ data = window.textEditor.getSession().getDocument().getValue()
26
+
27
+ @set('data', data)
25
28
 
26
- @set('data', data)
29
+ getRbxmlFilename: ->
30
+ filename = @get('filename')
31
+ if filename.match(/\.rb\.xml$/)
32
+ filename
33
+ else
34
+ if filename.match(/\.rb$/)
35
+ filename + '.xml'
36
+ else if filename.match(/\.xml$/)
37
+ filename.replace(/\.xml$/, '.rb.xml')
38
+ else
39
+ filename + '.rb.xml'
27
40
 
28
41
  run: ->
29
42
  @post_('run')
@@ -86,9 +86,8 @@ Smalruby.MainMenuView = Backbone.View.extend
86
86
 
87
87
  sourceCode = @getSourceCode()
88
88
 
89
- filename = sourceCode.get('filename')
90
- filename += '.xml' unless filename.match(/\.xml$/)
91
- xmlSourceCode = new Smalruby.SourceCode({ filename: filename })
89
+ xmlSourceCode =
90
+ new Smalruby.SourceCode({ filename: sourceCode.getRbxmlFilename() })
92
91
 
93
92
  clearMessages()
94
93
 
@@ -200,11 +199,10 @@ Smalruby.MainMenuView = Backbone.View.extend
200
199
  $('#filename').focus()
201
200
  return
202
201
 
202
+ sourceCode = @getSourceCode()
203
203
  if window.blockMode
204
- filename += '.xml' unless filename.match(/\.xml$/)
205
- sourceCode = new Smalruby.SourceCode({ filename: filename })
206
- else
207
- sourceCode = @getSourceCode()
204
+ sourceCode =
205
+ new Smalruby.SourceCode({ filename: sourceCode.getRbxmlFilename() })
208
206
 
209
207
  clearMessages()
210
208
 
@@ -13,7 +13,11 @@ $left-pane-width: $character-size + $margin * 2;
13
13
  #messages {
14
14
  position: absolute;
15
15
  right: 20px;
16
- z-index: 9999;
16
+ z-index: 999;
17
+ max-height: 80%;
18
+ overflow: auto;
19
+ bottom: 20px;
20
+ width: 25%;
17
21
  }
18
22
 
19
23
  .left-pane {
@@ -23,30 +23,35 @@
23
23
  }.join
24
24
  end
25
25
 
26
- hues = %w(
27
- 208
28
- 208
29
- 208
30
- 208
31
- 270
32
- 300
33
- 160
34
- 330
35
- 330
36
- 33
37
- 43
38
- 43
39
- 43
40
- 43
41
- 190
42
- 100
43
- 100
44
- 100
45
- 100
46
- 340
47
- 340
48
- 340
49
- ).map(&:to_i)
26
+ require 'smalruby_editor'
27
+ colors = SmalrubyEditor::COLORS
28
+
29
+ hues = [
30
+ colors[:motion],
31
+ colors[:motion],
32
+ colors[:motion],
33
+ colors[:motion],
34
+ colors[:looks],
35
+ colors[:sound],
36
+ colors[:pen],
37
+ colors[:data],
38
+ colors[:data],
39
+ colors[:events],
40
+ colors[:control],
41
+ colors[:control],
42
+ colors[:control],
43
+ colors[:control],
44
+ colors[:sensing],
45
+ colors[:sensing],
46
+ colors[:sensing],
47
+ colors[:operators],
48
+ colors[:operators],
49
+ colors[:operators],
50
+ colors[:operators],
51
+ colors[:etc],
52
+ colors[:etc],
53
+ colors[:etc],
54
+ ]
50
55
 
51
56
  colors = hues.map { |h|
52
57
  [hsv_to_rgb(h, 100, 80), hsv_to_rgb(h, 100, 100)]
@@ -4,50 +4,21 @@ require 'nkf'
4
4
  class SourceCodesController < ApplicationController
5
5
  before_filter :check_whether_standalone, only: [:write, :run, :load_local]
6
6
 
7
- # デモプログラム
8
- DEMO_PROGRAMS =
9
- [
10
- {
11
- title: '車のおいかけっこ',
12
- filename: 'car_chase.rb',
13
- imageUrl: '/smalruby/assets/car2.png',
14
- },
15
- {
16
- title: 'クリックスターだにゃ~',
17
- filename: 'star.rb',
18
- imageUrl: '/smalruby/assets/cat1.png',
19
- },
20
- {
21
- title: 'ピンポンゲーム',
22
- filename: 'pong.rb',
23
- imageUrl: '/smalruby/assets/cat2.png',
24
- },
25
- {
26
- title: 'ライトをぴかっとさせるでよ',
27
- filename: 'hardware_led.rb',
28
- imageUrl: '/smalruby/assets/frog1.png',
29
- },
30
- ]
31
-
32
7
  def index
33
8
  res = {
34
9
  localPrograms: [],
35
10
  demoPrograms: [],
36
11
  }
37
12
  if standalone?
38
- local_program_paths.each do |path|
39
- # TODO: XMLからタイトルを抽出する
40
- # TODO: XMLからキャラクターの画像を抽出する
41
- filename = rb_basename(path)
42
- res[:localPrograms] << {
43
- title: filename,
44
- filename: filename,
45
- }
46
- end
13
+ res[:localPrograms] = local_program_paths.map { |path|
14
+ SourceCode.new(filename: path.basename.to_s, data: path.read).summary
15
+ }
47
16
  end
48
17
 
49
- # TODO: XMLから情報を抽出する
50
- res[:demoPrograms] = DEMO_PROGRAMS
18
+ res[:demoPrograms] = Pathname.glob(Rails.root.join('demos/*.rb.xml')).map {
19
+ |path|
20
+ SourceCode.new(filename: path.basename.to_s, data: path.read).summary
21
+ }
51
22
 
52
23
  render json: res
53
24
  end
@@ -17,6 +17,15 @@ module EditorHelper
17
17
  %(<field name="#{h name}">#{h value}</field>).html_safe
18
18
  end
19
19
 
20
+ # ツールボックスのブロックに対して、PINの入力フィールドの値を設定する
21
+ #
22
+ # @param [String] value ピン
23
+ # @param [String] name 名前
24
+ # @return [String] XML
25
+ def toolbox_pin_field(value, name = 'PIN')
26
+ %(<field name="#{h name}">#{h value}</field>).html_safe
27
+ end
28
+
20
29
  # ツールボックスのブロックに対して、数値型の入力のブロックを設定する
21
30
  #
22
31
  # @param [String] name 入力値の名前
@@ -0,0 +1,25 @@
1
+ module RubyToBlock
2
+ module Block
3
+ class HardwareButtonDownOrUp < Value
4
+ include CharacterOperation
5
+ include HardwareOperation
6
+
7
+ blocknize '^\s*' + CHAR_RE +
8
+ 'button\(' + DIO_PIN_RE + '\)\.(down|up)\?\s*$',
9
+ value: true
10
+
11
+ def self.process_match_data(md, context)
12
+ md2 = regexp.match(md[type])
13
+
14
+ character = get_character(context, md2[1])
15
+ return false if context.receiver && context.receiver != character
16
+
17
+ block = new(fields: { PIN: md2[2], DOU: md2[3] })
18
+ context.add_value(block)
19
+ block.character = character
20
+
21
+ true
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module RubyToBlock
2
+ module Block
3
+ class HardwareButtonNotUsePullup < CharacterMethodCall
4
+ include HardwareOperation
5
+
6
+ blocknize '^\s*' + CHAR_RE +
7
+ 'button\(' + DIO_PIN_RE + '\)\.not_use_pullup\s*$',
8
+ statement: true
9
+
10
+ def self.process_match_data(md, context)
11
+ md2 = regexp.match(md[type])
12
+ add_character_method_call_block(context, md2[1],
13
+ new(fields: { PIN: md2[2] }))
14
+ true
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+ module RubyToBlock
3
+ module Block
4
+ class HardwareMotorDriver < CharacterMethodCall
5
+ # rubocop:disable LineLength
6
+ blocknize '^\s*' + CHAR_RE +
7
+ 'motor_driver\("(D(?:3|5|6|9|10|11))"\)\.(forward|backward|stop)\s*$',
8
+ statement: true
9
+ # rubocop:enable LineLength
10
+
11
+ attr_accessor :method_name
12
+
13
+ def self.process_match_data(md, context)
14
+ md2 = regexp.match(md[type])
15
+ add_character_method_call_block(context, md2[1],
16
+ new(fields: { PIN: md2[2],
17
+ METHOD: md2[3] }))
18
+ true
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+ module RubyToBlock
3
+ module Block
4
+ class HardwareMotorDriverSetSpeed < CharacterMethodCall
5
+ include HardwareOperation
6
+
7
+ blocknize '^\s*' + CHAR_RE +
8
+ 'motor_driver\(' + PWM_PIN_RE + '\)\.speed\s*=\s*(\S+)\s*$',
9
+ statement: true, inline: true
10
+
11
+ def self.process_match_data(md, context)
12
+ md2 = regexp.match(md[type])
13
+ add_character_method_call_block(context, md2[1],
14
+ new(fields: { PIN: md2[2] }),
15
+ SPEED: md2[3])
16
+ true
17
+ end
18
+ end
19
+ end
20
+ end