Class: Puter::Puterfile

Inherits:
Object
  • Object
show all
Defined in:
lib/puter/puterfile.rb

Constant Summary collapse

BACKSLASH =
"\\"
FROM =
:from
RUN =
:run
COPY =
:copy
BLANK =
:blank
CONTINUE =
:continue
COMMENT =
:comment
COMMANDS =
[ FROM, RUN, COPY ]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#executable_opsObject

Returns the value of attribute executable_ops.



28
29
30
# File 'lib/puter/puterfile.rb', line 28

def executable_ops
  @executable_ops
end

#fromObject

Returns the value of attribute from.



26
27
28
# File 'lib/puter/puterfile.rb', line 26

def from
  @from
end

#linesObject

Returns the value of attribute lines.



25
26
27
# File 'lib/puter/puterfile.rb', line 25

def lines
  @lines
end

#operationsObject

Returns the value of attribute operations.



27
28
29
# File 'lib/puter/puterfile.rb', line 27

def operations
  @operations
end

#rawObject

Returns the value of attribute raw.



24
25
26
# File 'lib/puter/puterfile.rb', line 24

def raw
  @raw
end

Class Method Details

.executable_operations(operations) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/puter/puterfile.rb', line 119

def executable_operations(operations)
  execs = []
  operations.each_with_index do |op, i|
    case op[:operation]
    when COPY, RUN, FROM
      exec = {
        :operation => op[:operation],
        :data      => op[:data].dup
      }
      exec[:start_line] = exec[:end_line] = i
      execs << exec
    when CONTINUE
      execs.last[:data] << op[:data]
      execs.last[:end_line] = i
    end
  end

  execs.select { |e| e[:operation] == COPY }.each do |e|
    e[:from], e[:to] = e[:data].strip.split /\s+/, 2
    raise SyntaxError.new "COPY operation requires two parameters #{e.inspect}" if e[:from].nil? || e[:to].nil?
  end

  execs
end

.from_path(path) ⇒ Object



42
43
44
# File 'lib/puter/puterfile.rb', line 42

def from_path(path)
  parse File.open(path, 'rb') { |f| f.read }
end

.parse(raw) ⇒ Object



46
47
48
49
50
51
52
53
54
# File 'lib/puter/puterfile.rb', line 46

def parse(raw)
  p = Puterfile.new
  p.raw = raw
  p.lines = raw.to_s.split "\n"
  p.operations = parse_operations(p.lines)
  p.executable_ops = executable_operations(p.operations)
  p.from = p.operations[0][:data]
  p
end

.parse_operation(line, previous_line = "") ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/puter/puterfile.rb', line 75

def parse_operation(line, previous_line="")
  op = {}
  line = line.rstrip unless line.nil?

  case
  when line.nil?
    raise SyntaxError.new 'cannot parse nil lines'

  # blank line
  when line.strip.empty?
    op[:operation] = BLANK
    op[:data]      = line
    op[:continue]  = false

  # commented line
  when line =~ /\s*\#/
    op[:operation] = COMMENT
    op[:data]      = line
    op[:continue]  = line[-1] == BACKSLASH

  # continuation of a previous line
  when line =~ /\s/ && previous_line.rstrip[-1] == BACKSLASH
    op[:operation] = CONTINUE
    op[:data]      = line.lstrip
    op[:continue]  = line[-1] == BACKSLASH

  # must be an operation (FROM, COPY, RUN, ...)
  else
    parts = line.split(/\s+/, 2)
    cmd = parts[0].downcase.to_sym
    data = parts[1]

    raise SyntaxError.new "Unknown operation [#{cmd.to_s.upcase}]" unless COMMANDS.include? cmd
    raise SyntaxError.new "Operation [#{cmd.to_s.upcase}] has no data" if data.nil?
    op[:operation] = cmd
    op[:data]      = data
    op[:continue]  = line[-1] == BACKSLASH

  end
  op[:data][-1] = " " if op[:continue]

  op
end

.parse_operations(lines) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/puter/puterfile.rb', line 56

def parse_operations(lines)
  raise Puter::SyntaxError.new "File is empty.  First line must be a FROM command" if lines.length == 0

  ops = []
  previous_line = ""
  lines.each_with_index do | line, i |
    begin
      ops << parse_operation(line, previous_line)
      if i == 0
        raise Puter::SyntaxError.new "First line must be a FROM command" unless ops[i][:operation] == FROM
      end
    rescue Puter::SyntaxError => se
      raise Puter::SyntaxError.new "On line #{i+1}: #{se.message}"
    end
    previous_line = line
  end
  ops
end

Instance Method Details

#apply(context, backend, ui) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/puter/puterfile.rb', line 146

def apply(context, backend, ui)
  dependency_check(context)
  ret = { :exit_status => 0, :exit_signal => nil }

  executable_ops.each_with_index do |op, step|
    ui.info "Step #{step} : #{op[:operation].to_s.upcase} #{op[:data]}"

    case op[:operation]
    when COPY
      backend.copy path_in_context(op[:from], context), op[:to]
    when RUN
      ret = backend.run op[:data] do | type, data |
        case type
        when :stderr
          ui.remote_stderr data
        when :stdout
          ui.remote_stdout data
        end
      end

      if ret[:exit_status] != 0
        line = "#{op[:start_line]+1}"
        plural = ""
        if op[:start_line] != op[:end_line]
          line << "..#{op[:end_line]+1}"
          plural = "s"
        end

        raise RunError.new "On line#{plural} #{line}: RUN command exited non-zero.", ret
      end
    end
  end

  ret
end