Class: SourceCode

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
RubyToBlock
Defined in:
app/models/source_code.rb

Overview

ソースコードを表現するモデル

Constant Summary collapse

MAX_REMIX_COUNT =
1000

Class Method Summary collapse

Instance Method Summary collapse

Methods included from RubyToBlock

#to_blocks

Class Method Details

.make_remix_filename(home_dir, filename) ⇒ Object

リミックス用のファイル名を生成する



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'app/models/source_code.rb', line 24

def self.make_remix_filename(home_dir, filename)
  home_dir = Pathname(home_dir).expand_path
  filename = filename.dup
  ext = filename.slice!(/\.rb(\.xml)?$/)
  filename.slice!(/(_remix(\d+)?)+$/)
  basename = "#{filename}_remix"
  MAX_REMIX_COUNT.times do |i|
    suffix = (i == 0 ? '' : sprintf('%02d', i + 1))
    remix_name = "#{basename}#{suffix}"
    if !home_dir.join("#{remix_name}.rb").exist? &&
        !home_dir.join("#{remix_name}.rb.xml").exist?
      return "#{remix_name}#{ext}"
    end
  end
  fail "reach max remix count...: #{filename}"
end

Instance Method Details

#check_syntaxObject

シンタックスをチェックする



42
43
44
45
46
47
48
49
50
51
52
53
# File 'app/models/source_code.rb', line 42

def check_syntax
  _, stderr_str, status = *open3_capture3_ruby_c
  return [] if status.success?

  stderr_str.lines.each.with_object([]) { |line, res|
    if (md = /^.*:(\d+): (.*)$/.match(line))
      res << { row: md[1].to_i, column: 0, message: md[2] }
    elsif (md = /( +)\^$/.match(line))
      res[-1][:column] = md[1].length
    end
  }
end

#digestObject

ハッシュ値を計算する



64
65
66
# File 'app/models/source_code.rb', line 64

def digest
  Digest::SHA256.hexdigest(data)
end

#include_block?(type) ⇒ Boolean

指定した命令ブロックを含んでいるかどうかを返す

Parameters:

  • type (String|Regexp)

    命令ブロックの種類

Returns:

  • (Boolean)

    命令ブロックを含む場合はtrue、そうでない場合はfalse



92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/models/source_code.rb', line 92

def include_block?(type)
  if xml?
    doc = Nokogiri::HTML.parse(data)
    if type.is_a?(Regexp)
      doc.xpath(%(//block[matches(@type, '#{type.source}')]),
                MatchesXPathFunction.new).length > 0
    else
      doc.xpath(%(//block[@type="#{type}"])).length > 0
    end
  else
    false
  end
end

#run(path, env = {}) ⇒ Object

プログラムを実行する



56
57
58
59
60
61
# File 'app/models/source_code.rb', line 56

def run(path, env = {})
  _, stderr_str, status = *open3_capture3_run_program(path, env)
  return [] if status.success?

  parse_ruby_error_messages(stderr_str)
end

#summaryObject

ソースコードの概要を取得する



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'app/models/source_code.rb', line 69

def summary
  res = {}
  if xml?
    res[:filename] = filename.sub(/\.xml\z/, '')

    doc = Nokogiri::HTML.parse(data)
    if (attr = doc.xpath('//character[1]/@costumes').first)
      res[:imageUrl] = "/smalruby/assets/#{attr.value}"
    end
    text = doc.xpath('//block[@type="ruby_comment"][1]' +
                     '/field[@name="COMMENT"]/text()').first
    res[:title] = text.to_s if text
  else
    res[:filename] = filename
  end
  res[:title] = res[:filename] unless res[:title]
  res
end

#xml?Boolean

ソースコードの種別がXML形式かどうかを返す

Returns:

  • (Boolean)


107
108
109
# File 'app/models/source_code.rb', line 107

def xml?
  !!(/\.xml\z/ =~ filename)
end