Class: RSQL::Commands

Inherits:
Object
  • Object
show all
Defined in:
lib/rsql/commands.rb

Overview

A wrapper to parse and handle commands

Defined Under Namespace

Classes: Command

Constant Summary collapse

SEPARATORS =

Split commands on these characters.

';|!'

Instance Method Summary collapse

Constructor Details

#initialize(input, default_displayer) ⇒ Commands

Split on separators, allowing for escaping;



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/rsql/commands.rb', line 52

def initialize(input, default_displayer)
    @default_displayer = default_displayer
    @cmds = []
    esc = ''
    bangs = {}
    match_before_bang = nil
    in_pipe_arg = false
    next_is_ruby = false

    input.scan(/[^#{SEPARATORS}]+.?/) do |match|
        orig_match = match

        if i = SEPARATORS.index(match[-1])
            sep = SEPARATORS[i]
            match.chop!

            if match[-1] == ?\\
                # unescape the separator and save the content away
                esc << match[0..-2] << sep
                next
            end
        else
            sep = nil
        end

        unless esc.empty?
            esc << match
            match = esc
            esc = ''
        end

        found_maps = false
        if match_before_bang
            new_bangs = {}
            match.split(/\s*,\s*/).each do |ent|
                (key,val) = ent.split(/\s*=>\s*/)
                unless key && val
                    # they are using a bang but have no maps
                    # so we assume this is a != or something
                    # similar and let it go through unmapped
                    match = match_before_bang + '!' + match
                    match_before_bang = nil
                else
                    found_maps = true
                    if val.strip == 'nil'
                        new_bangs[key.strip] = nil
                    else
                        new_bangs[key.strip] = val.to_sym
                    end
                end
            end
            if found_maps
                next unless match_before_bang
                match = match_before_bang
                match_before_bang = nil
                bangs.merge!(new_bangs)
            end
        end

        if sep == ?!
            match_before_bang = match
            next
        end

        if sep == ?|
            # we've split on a pipe so we need to handle the
            # case where ruby code is declaring a block with
            # arguments (e.g. {|x| p x} or do |x| p x end)
            if in_pipe_arg
                in_pipe_arg = false
                esc << match << '|'
                next
            elsif orig_match =~ /\{\s*|do\s*/
                in_pipe_arg = true
                esc << match << '|'
                next
            end
        end

        add_command(match, bangs, next_is_ruby, sep)

        bangs = {}
        next_is_ruby = sep == ?|
    end

    add_command(esc, bangs, next_is_ruby)
end

Instance Method Details

#add_command(content, bangs, is_ruby, separator = nil) ⇒ Object (private)



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/rsql/commands.rb', line 183

def add_command(content, bangs, is_ruby, separator=nil)
    content.strip!

    case content[0]
    when ?.
        content.slice!(0)
        declarator = :ruby
    else
        declarator = is_ruby ? :ruby : nil
    end

    if content.end_with?('\G')
        # emulate mysql's \G output
        content.slice!(-2,2)
        displayer = :display_by_line
    elsif separator == ?|
        displayer = :pipe
    else
        displayer = @default_displayer
    end

    unless content.empty?
        @cmds << Command.new(content, bangs, declarator, displayer)
        return true
    end

    return false
end

#concat(other) ⇒ Object



144
145
146
# File 'lib/rsql/commands.rb', line 144

def concat(other)
    @cmds.concat(other)
end

#empty?Boolean

Returns:

  • (Boolean)


140
141
142
# File 'lib/rsql/commands.rb', line 140

def empty?
    return @cmds.empty?
end

#lastObject



148
149
150
# File 'lib/rsql/commands.rb', line 148

def last
    @cmds.last
end

#run!(eval_context) ⇒ Object



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
# File 'lib/rsql/commands.rb', line 152

def run!(eval_context)
    last_results = nil
    while @cmds.any?
        cmd = @cmds.shift
        results = run_command(cmd, last_results, eval_context)
        return :done if results == :done

        if cmd.displayer == :pipe
            last_results = results
        elsif MySQLResults === results
            last_results = nil
            results.send(cmd.displayer)
        elsif EvalResults === results
            last_results = nil
            if MySQLResults === results.value
                # This happens if their recipe returns MySQL
                # results...just display it like above.
                results.value.send(cmd.displayer)
            else
                if results.stdout && 0 < results.stdout.size
                    puts results.stdout.string
                end
                puts "=> #{results.value.inspect}" if results.value
            end
        end
    end
end

#run_command(cmd, last_results, eval_context) ⇒ Object (private)



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/rsql/commands.rb', line 212

def run_command(cmd, last_results, eval_context)
    eval_context.bangs = cmd.bangs

    if cmd.declarator
        stdout = cmd.displayer == :pipe ? StringIO.new : nil
        value = eval_context.safe_eval(cmd.content, last_results, stdout)
        if String === value
            cmds = Commands.new(value, cmd.displayer)
            unless cmds.empty?
                # need to carry along the bangs into the
                # last command so we don't lose them
                if cmds.last.bangs.empty? && cmd.bangs.any?
                    cmds.last.bangs = cmd.bangs
                end
                @cmds = cmds.concat(@cmds)
            end
            return
        end
    else
        value = cmd.content
    end

    return :done if value == 'exit' || value == 'quit'

    if String === value
        begin
            last_results = MySQLResults.query(value, eval_context)
        rescue MySQLResults::MaxRowsException => ex
            $stderr.puts "refusing to process #{ex.rows} rows (max: #{ex.max})--" <<
                "consider raising this via set_max_rows"
        rescue Mysql::Error => ex
            $stderr.puts ex.message
        rescue Exception => ex
            $stderr.puts ex.inspect
            raise
        end
    else
        last_results = EvalResults.new(value, stdout)
    end

    return last_results
end