rsql 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,86 @@
1
+ = RSQL
2
+
3
+ Homepage:: https://github.com/bradrf/rsql
4
+
5
+ == DESCRIPTION
6
+
7
+ This is an application to make working with a SQL command line more
8
+ convenient by allowing interaction with recipes and Ruby code in
9
+ addition to embedding the common operation of using a SSH connection
10
+ to an intermediary host for access to the SQL server.
11
+
12
+ === Installation
13
+
14
+ gem install rsql
15
+
16
+ == USAGE
17
+
18
+ RSQL is invoked from the comamnd line using:
19
+
20
+ rsql [<options>] <mysql_host> [<database>] [-e [<query>]]
21
+
22
+ === Options
23
+
24
+ -version::
25
+ Display the version of RSQL that is installed.
26
+
27
+ -rc _rcfile_::
28
+ Override loading the .rsqlrc file from the HOME directory for one in
29
+ a different location.
30
+
31
+ -maxrows _max_::
32
+ Override the maximum number of rows to process.
33
+
34
+ -batch _field_separator_::
35
+ Run in batch mode using the separator specifed (e.g. a /t will
36
+ separate fields with a tab character).
37
+
38
+ -ssh _ssh_host_::
39
+ Establish an SSH connection before connecting to the MySQL host.
40
+
41
+ -e [_query_]::
42
+ Run a query from the command line (i.e. not interactive). If a
43
+ _query_ is not provided, STDIN will be read. Multiple commands can
44
+ be issued in one set by separation with semicolons just as if they
45
+ had been provided at the RSQL prompt interactively. This option
46
+ *must* be the last option specified.
47
+
48
+ The _ssh_host_ and _mysql_host_ values may also provide _user_ and
49
+ _password_ values using the following syntax:
50
+
51
+ [<user>[:<password>]@]<host>
52
+
53
+ It is possible to provide empty passwords by simply having nothing
54
+ listed between demarcation points:
55
+
56
+ root:@127.0.0.1
57
+
58
+ Once at the +rsql+ prompt, normal MySQL queries can be entered as
59
+ expected, ending each with a semicolon (;).
60
+
61
+ == EXAMPLE
62
+
63
+ Try walking through link:../example.rsqlrc.
64
+
65
+ == LICENSE
66
+
67
+ Copyright (C) 2011 by Brad Robel-Forrest <brad+rsql@gigglewax.com>
68
+
69
+ Permission is hereby granted, free of charge, to any person obtaining
70
+ a copy of this software and associated documentation files (the
71
+ "Software"), to deal in the Software without restriction, including
72
+ without limitation the rights to use, copy, modify, merge, publish,
73
+ distribute, sublicense, and/or sell copies of the Software, and to
74
+ permit persons to whom the Software is furnished to do so, subject to
75
+ the following conditions:
76
+
77
+ The above copyright notice and this permission notice shall be
78
+ included in all copies or substantial portions of the Software.
79
+
80
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
81
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
82
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
83
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
84
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
85
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
86
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/TODO CHANGED
@@ -24,9 +24,6 @@
24
24
 
25
25
  * Consider using mysql's ping to determine if we need to reconnect.
26
26
 
27
- * Move last_command logic into mysql_results and remember last five
28
- (or so).
29
-
30
27
  * Fix need for SSH password! It never asks.
31
28
 
32
29
  * Find out if it's easy to point at source from the wiki to point
@@ -34,3 +31,7 @@
34
31
 
35
32
  * Consider adding the option to specify a dispalyer when registering a
36
33
  recipe.
34
+
35
+ * Move everything in here into github issues.
36
+
37
+ * Consder renaming "register" to "recipe"
data/bin/rsql CHANGED
@@ -96,11 +96,6 @@ def split_login(str)
96
96
  end
97
97
  end
98
98
 
99
- if ARGV.delete('-help')
100
- eval_context.help
101
- exit
102
- end
103
-
104
99
  if ARGV.delete('-version')
105
100
  puts "#{bn} v#{RSQL::VERSION}"
106
101
  exit
@@ -11,22 +11,30 @@
11
11
 
12
12
  # To use this file, change directory to the one containing this file,
13
13
  # run rsql connecting to your MySQL server (run rsql with no arguments
14
- # for usage). At the RSQL prompt, type ".load 'example.rsqlrc';"
15
- # (without the double quotes).
14
+ # for usage).
15
+ #
16
+ # rsql> .load 'example.rsqlrc';
16
17
 
17
- # After it's loaded try out the command ".list;" (without the
18
- # quotes).
18
+ # After it's loaded try listing out all the registered recipes (along
19
+ # with parameter notes and descriptions).
20
+ #
21
+ # rsql> .list;
19
22
 
20
23
  # If you make changes to the example to try out new things (and please
21
- # do!), you can simply enter ".reload;" (without the double quotes) to
22
- # have your changes pulled in immediately without exiting your
23
- # session.
24
+ # do!), you can simply have the recipe file reloaded to have your
25
+ # changes pulled in immediately without exiting your session.
26
+ #
27
+ # rsql> .reload;
28
+
29
+ # Notice that any command issued starting with a period (.) results in
30
+ # evaluation of Ruby. Thus, any valid Ruby syntax is applicable
31
+ # following a period on a command.
24
32
 
25
33
  ################################################################################
26
34
 
27
35
  # This type of registration is automatically invoked when this file is
28
36
  # loaded. Often, this is useful to run set up routines like setting
29
- # mysql variables for different read levels (e.g. SET SESSION
37
+ # MySQL variables for different read levels (e.g. SET SESSION
30
38
  # TRANSACTION ISOLATION LEVEL READ COMMITTED). Any number of these may
31
39
  # be defined.
32
40
  #
@@ -35,7 +43,8 @@
35
43
  register_init :setup_example, %q{
36
44
  CREATE TEMPORARY TABLE IF NOT EXISTS #{@rsql_table} (
37
45
  name VARCHAR(100),
38
- value INT(11)
46
+ value INT(11),
47
+ stuff BLOB
39
48
  )
40
49
  }, :desc => 'Sets up example table for trying out RSQL.'
41
50
 
@@ -43,10 +52,15 @@ CREATE TEMPORARY TABLE IF NOT EXISTS #{@rsql_table} (
43
52
  # interpolated into it (our table name). The string will then be used
44
53
  # as if typed at the command line.
45
54
  #
55
+ # rsql> .cleanup_example;
56
+ #
46
57
  # In this case, we are simply dropping the table created by our
47
- # initialization recipe.
58
+ # initialization recipe. If you do this, you'll need to call the
59
+ # setup_example initialization recipe again before moving on.
60
+ #
61
+ # rsql> .setup_example;
48
62
  #
49
- register :cleanup, %q{
63
+ register :cleanup_example, %q{
50
64
  DROP TEMPORARY TABLE IF EXISTS #{@rsql_table}
51
65
  }, :desc => 'Cleans up the example table.'
52
66
 
@@ -55,8 +69,10 @@ DROP TEMPORARY TABLE IF EXISTS #{@rsql_table}
55
69
  #
56
70
  # Here we are just populating the table (if it isn't already).
57
71
  #
58
- # Notice that we are also making use of one of EvalContext's helper
59
- # methods, "squeeze!" to make the SQL compact.
72
+ # rsql> .fill_table;
73
+ #
74
+ # Notice the use of hexify and squeeze! methods available from
75
+ # EvalContext.
60
76
  #
61
77
  register :fill_table, :desc => 'Populate the example table.' do
62
78
  sql = ''
@@ -64,25 +80,65 @@ register :fill_table, :desc => 'Populate the example table.' do
64
80
  sql << "
65
81
  INSERT IGNORE INTO #{@rsql_table}
66
82
  SET name='fancy#{i}',
67
- value=#{i**i};
83
+ value=#{i**i},
84
+ stuff=#{hexify(rand((i+1)**100))};
68
85
  "
69
86
  end
70
87
  sqeeze!(sql)
71
88
  end
72
89
 
90
+ # A very common reason for recipes is simply to add parameters to be
91
+ # dropped in to our query. To facilitate this, simply declare one or
92
+ # more variables immediately following the name of the recipe. Then
93
+ # these values can be listed by embedded interpolation points into the
94
+ # string (just as you would with any Ruby string).
95
+ #
96
+ # This call will simply return results only for those bigger than some
97
+ # value passed in.
98
+ #
99
+ # rsql> .get_big_values 80000;
100
+ #
101
+ register :get_big_values, :val, %q{
102
+ SELECT name, value FROM #{@rsql_table} WHERE #{val} <= value
103
+ }, :desc => 'Get values bigger than the one provided as an argument.'
104
+
105
+ # Sometimes we make mistakes (never!). Normally, the command history
106
+ # kept in RSQL only stores the last thing entered at the prompt--not
107
+ # any query that the previous command may have generated and invoked.
108
+ # When writing a recipe that generates a query that has an error
109
+ # reported by MySQL, it is really handy to see the query.
110
+ #
111
+ # Here's an example of a recipe that will fail. Run it and then hit the
112
+ # "up arrow" key to see the previous command.
113
+ #
114
+ # rsql> .bad_query;
115
+ #
116
+ # So the command in our history is the recipe and not the query. To
117
+ # see the query the EvalContext has a recipe ready for us:
118
+ #
119
+ # rsql> .last_query;
120
+ #
121
+ register :bad_query, %q{
122
+ SELECT name, value FROM #{@rsql_table} WHERE valu < 10000
123
+ }, :desc => 'Make a query that will result in an error.'
124
+
73
125
  # After you have a table with content in it, you can run queries
74
126
  # against it and have the contents changed into something a little
75
127
  # more meaningful. For example, what if the values in our table were
76
128
  # bytes that we wanted to humanize? Try this command:
77
129
  #
78
- # rsql> select * from rsql_example ! value => humanize_bytes;
130
+ # rsql> select name, value from rsql_example ! value => humanize_bytes;
79
131
  #
80
132
  # The humanize_bytes method is a helper in the EvalContext
81
- # class. There are several others avaialble. Check out the rdoc for
133
+ # class. There are several others available. Check out the rdoc for
82
134
  # details.
83
135
  #
84
- # You can also declare these column mappings in your recipes too,
85
- # though the syntax is slightly different, using Ruby symbols.
136
+ # Additional mappings can be added, separated by commas.
137
+ #
138
+ # You can also declare these column mappings in your recipes, though
139
+ # the syntax is slightly different, using Ruby symbols.
140
+ #
141
+ # rsql> .show_values_as_bytes;
86
142
  #
87
143
  register :show_values_as_bytes, %q{
88
144
  SELECT value FROM #{@rsql_table}
@@ -95,13 +151,15 @@ SELECT value FROM #{@rsql_table}
95
151
  # will be replaced as the column entry's content. Your method is
96
152
  # called once for each value in the column from the results.
97
153
  #
154
+ # rsql> .show_pretty_names;
155
+ #
98
156
  # Make sure if your method doesn't understand the content passed to it
99
157
  # that it just reflects it back out so you don't lose data when
100
158
  # printed.
101
159
  #
102
160
  def pretty_names(name)
103
- if name =~ /^(\w+)(\d+)$/
104
- "#{$1} (#{$2})"
161
+ if m = name.match(/^(\w+)(\d+)$/)
162
+ "#{m[1]} (#{m[2]})"
105
163
  else
106
164
  name
107
165
  end
@@ -112,14 +170,13 @@ SELECT name FROM #{@rsql_table}
112
170
  }, 'name' => :pretty_names,
113
171
  :desc => 'Show names separated to be more readable.'
114
172
 
115
-
116
173
  # It's also possible to work with the full set of query results in a
117
174
  # recipe. This can be useful if there is some coordination necessary
118
175
  # across multiple columns to result in some new kind of report. Much
119
176
  # like a shell's ability to pipe output from one command to the next,
120
- # RSQL takes a similar approch. Try this:
177
+ # RSQL takes a similar approach. Try this:
121
178
  #
122
- # rsql> select * from rsql_example | p @results;
179
+ # rsql> select name, value from rsql_example | p @results;
123
180
  #
124
181
  # The EvalContext manages the results from a previous query in the
125
182
  # @results member variable accessible by any Ruby recipe code. This is
@@ -130,9 +187,9 @@ SELECT name FROM #{@rsql_table}
130
187
  # Here's an example that writes a simple report of the data we are
131
188
  # working with. To try this out, enter the following at the prompt:
132
189
  #
133
- # rsql> select * from rsql_example | to_report;
190
+ # rsql> select name, value from rsql_example | to_report;
134
191
  #
135
- register :to_report do
192
+ register :to_report, :desc => 'Report on a count of small and big values.' do
136
193
  small_cnt = 0
137
194
  big_cnt = 0
138
195
  @results.each_hash do |row|
@@ -145,4 +202,78 @@ register :to_report do
145
202
  puts "There are #{small_cnt} small values and #{big_cnt} big values."
146
203
  end
147
204
 
205
+ # There may be other moments where it's necessary to take arguments,
206
+ # say if we want to process results and keep our data around in a
207
+ # file.
208
+ #
209
+ # rsql> select name, value from rsql_example | save_values 'myobj'
210
+ #
211
+ # After running this, a myobj.yml file should be created in the local
212
+ # directory containing all the content from the query. To accomplish
213
+ # this, the use of EvalContext's safe_save method is invoked which
214
+ # serializes our object so that we may later decided to run some post
215
+ # processing on the content.
216
+ #
217
+ register :save_values, :desc => 'Save results from a query into a file.' do |fn|
218
+ myobj = {}
219
+ @results.each_hash do |row|
220
+ myobj[row['name']] = row['value']
221
+ end
222
+ safe_save(myobj, fn)
223
+ end
224
+
225
+ # Dealing with variable arguments is pretty straightforward as well,
226
+ # but with a little syntactic twist.
227
+ #
228
+ # rsql> .find_names 'fancy3', 'fancy8'
229
+ #
230
+ # Here we simply expand the arguments.
231
+ #
232
+ register :find_names, :'*names', %q{
233
+ SELECT name, value
234
+ FROM #{@rsql_table}
235
+ WHERE name IN (#{names.collect{|n| "'#{n}'"}.join(',')})
236
+ }, :desc => 'Find names from example table.'
237
+
238
+ # Sometimes it just isn't enough to be able to rely on generating SQL
239
+ # queries and piping into handlers. Sometimes we just need to roll up
240
+ # our sleeves and run queries directly so we can start processing
241
+ # results and dealing with presentation all on our own. That's where
242
+ # EvalContext's query helper comes in handy.
243
+ #
244
+ # The intention here is to just create a series of sentences out of
245
+ # two separate queries.
246
+ #
247
+ # rsql> .show_sentences;
248
+ #
249
+ register :show_sentences, :desc => 'Show results as sentences.' do
250
+ query("SELECT name FROM #{@rsql_table}").each_hash do |nrow|
251
+ name = nrow['name']
252
+ vals = query("SELECT value FROM #{@rsql_table} WHERE name='#{name}'")
253
+ puts "The #{name} has #{vals[0]['value']} fanciness levels."
254
+ end
255
+ end
256
+
257
+ # The MySQLResults class built in to RSQL handles binary content
258
+ # gracefully, automatically converting it to something a little nicer
259
+ # to our consoles than just dumping it. It converts it into a
260
+ # hexadecimal string.
261
+ #
262
+ # rsql> SELECT stuff FROM rsql_example
263
+ #
264
+ # The default is to limit the hex strings to 32 "bytes" reported. This
265
+ # can be configured any time by setting the @hexstr_limit.
266
+ #
267
+ # RSQL makes querying for hex strings from within a recipe easy too.
268
+ #
269
+ # rsql> .find_stuff 0x346950d3c051c8ac51092a3a2eff7503ef7f571c
270
+ #
271
+ register :find_stuff, :stuff, %q{
272
+ SELECT * FROM #{@rsql_table} WHERE stuff=#{hexify stuff}
273
+ }, :desc => 'Find some hex stuff.'
274
+
275
+ # There are many other things to try out left as an "exercise for the
276
+ # reader". Browsing the rdoc for EvalContext and MySQLResults would be
277
+ # an excellent start.
278
+
148
279
  # vi: set filetype=ruby
@@ -1,5 +1,8 @@
1
+ # A module encapsulating classes to manage MySQLResults and process
2
+ # Commands using an EvalContext for handling recipes.
3
+ #
1
4
  module RSQL
2
- VERSION = '0.1.4'
5
+ VERSION = '0.1.5'
3
6
 
4
7
  require 'rsql/mysql'
5
8
  require 'rsql/mysql_results'
@@ -1,3 +1,4 @@
1
+ #--
1
2
  # Copyright (C) 2011 by Brad Robel-Forrest <brad+rsql@gigglewax.com>
2
3
  #
3
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -33,9 +34,11 @@ module RSQL
33
34
 
34
35
  ########################################
35
36
 
36
- # split on separators, allowing for escaping
37
- #
37
+ # Split commands on these characters.
38
38
  SEPARATORS = ';|!'
39
+
40
+ # Split on separators, allowing for escaping;
41
+ #
39
42
  def initialize(input, default_displayer)
40
43
  @default_displayer = default_displayer
41
44
  @cmds = []
@@ -1,3 +1,4 @@
1
+ #--
1
2
  # Copyright (C) 2011 by Brad Robel-Forrest <brad+rsql@gigglewax.com>
2
3
  #
3
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -22,7 +23,7 @@ module RSQL
22
23
 
23
24
  ################################################################################
24
25
  # This class wraps all dynamic evaluation and serves as the reflection
25
- # class for adding new methods dynamically.
26
+ # class for adding methods dynamically.
26
27
  #
27
28
  class EvalContext
28
29
 
@@ -32,8 +33,8 @@ module RSQL
32
33
 
33
34
  def initialize
34
35
  @hexstr_limit = HEXSTR_LIMIT
35
- @last_cmd = nil
36
36
  @results = nil
37
+ @last_query = nil
37
38
 
38
39
  @loaded_fns = []
39
40
  @init_registrations = []
@@ -48,10 +49,10 @@ module RSQL
48
49
  method(:reload),
49
50
  'reload',
50
51
  'Reload the rsqlrc file.'),
51
- :last_cmd => Registration.new('last_cmd', [], {},
52
- Proc.new{puts @last_cmd},
53
- 'last_cmd',
54
- 'Print the last command generated.'),
52
+ :last_query => Registration.new('last_query', [], {},
53
+ Proc.new{puts(@last_query)},
54
+ 'last_query',
55
+ 'Print the last query made from generated results.'),
55
56
  :set_max_rows => Registration.new('set_max_rows', [], {},
56
57
  Proc.new{|r| MySQLResults.max_rows = r},
57
58
  'set_max_rows',
@@ -109,7 +110,7 @@ module RSQL
109
110
  return val
110
111
  end
111
112
 
112
- # safely evaluate Ruby content within our context
113
+ # Safely evaluate Ruby content within our context.
113
114
  #
114
115
  def safe_eval(content, results, stdout)
115
116
  @results = results
@@ -141,12 +142,12 @@ module RSQL
141
142
  $stdout = orig_stdout if stdout
142
143
  end
143
144
 
144
- @last_cmd = value if String === value
145
+ @last_query = value if String === value
145
146
 
146
147
  return value
147
148
  end
148
149
 
149
- # provide a list of tab completions given the prompted value
150
+ # Provide a list of tab completions given the prompted value.
150
151
  #
151
152
  def complete(str)
152
153
  if str[0] == ?.
@@ -170,13 +171,13 @@ module RSQL
170
171
  ret
171
172
  end
172
173
 
173
- # reset the hexstr limit back to the default value
174
+ # Reset the hexstr limit back to the default value.
174
175
  #
175
176
  def reset_hexstr_limit
176
177
  @hexstr_limit = HEXSTR_LIMIT
177
178
  end
178
179
 
179
- # convert a binary string value into a hexadecimal string
180
+ # Convert a binary string value into a hexadecimal string.
180
181
  #
181
182
  def to_hexstr(bin, limit=@hexstr_limit, prefix='0x')
182
183
  cnt = 0
@@ -201,9 +202,9 @@ module RSQL
201
202
  ########################################
202
203
  private
203
204
 
204
- # display a listing of all registered helpers
205
+ # Display a listing of all registered helpers.
205
206
  #
206
- def list
207
+ def list # :doc:
207
208
  usagelen = 0
208
209
  desclen = 0
209
210
 
@@ -226,35 +227,35 @@ module RSQL
226
227
  return nil
227
228
  end
228
229
 
229
- # show all the pertinent version data we have about our
230
- # software and the mysql connection
230
+ # Show all the pertinent version data we have about our
231
+ # software and the mysql connection.
231
232
  #
232
- def version
233
+ def version # :doc:
233
234
  puts "rsql:v#{RSQL::VERSION} client:v#{MySQLResults.conn.client_info} " \
234
235
  "server:v#{MySQLResults.conn.server_info}"
235
236
  end
236
237
 
237
- # provide a helper utility in the event a registered
238
- # method would like to make its own queries
238
+ # Provide a helper utility in the event a registered
239
+ # method would like to make its own queries.
239
240
  #
240
- def query(content, *args)
241
+ def query(content, *args) # :doc:
241
242
  MySQLResults.query(content, self, *args)
242
243
  end
243
244
 
244
- # exactly like register below except in addition to registering as
245
+ # Exactly like register below except in addition to registering as
245
246
  # a usable call for later, we will also use these as soon as we
246
247
  # have a connection to MySQL.
247
248
  #
248
- def register_init(sym, *args, &block)
249
+ def register_init(sym, *args, &block) # :doc:
249
250
  register(sym, *args, &block)
250
251
  @init_registrations << sym
251
252
  end
252
253
 
253
- # if given a block, allow the block to be called later, otherwise,
254
+ # If given a block, allow the block to be called later, otherwise,
254
255
  # create a method whose sole purpose is to dynmaically generate
255
- # sql with variable interpolation
256
+ # sql with variable interpolation.
256
257
  #
257
- def register(sym, *args, &block)
258
+ def register(sym, *args, &block) # :doc:
258
259
  name = usage = sym.to_s
259
260
 
260
261
  if Hash === args.last
@@ -282,9 +283,9 @@ module RSQL
282
283
  @registrations[sym] = Registration.new(name, args, bangs, block, usage, desc)
283
284
  end
284
285
 
285
- # convert a collection of values into to hexadecimal strings
286
+ # Convert a collection of values into hexadecimal strings.
286
287
  #
287
- def hexify(*ids)
288
+ def hexify(*ids) # :doc:
288
289
  ids.collect do |id|
289
290
  case id
290
291
  when String
@@ -301,7 +302,7 @@ module RSQL
301
302
  end.join(',')
302
303
  end
303
304
 
304
- # convert a number of bytes into a human readable string
305
+ # Convert a number of bytes into a human readable string.
305
306
  #
306
307
  def humanize_bytes(bytes)
307
308
  abbrev = ['B','KB','MB','GB','TB','PB','EB','ZB','YB']
@@ -322,9 +323,9 @@ module RSQL
322
323
  return bytes.to_s
323
324
  end
324
325
 
325
- # convert a human readable string of bytes into an integer
326
+ # Convert a human readable string of bytes into an integer.
326
327
  #
327
- def dehumanize_bytes(str)
328
+ def dehumanize_bytes(str) # :doc:
328
329
  abbrev = ['B','KB','MB','GB','TB','PB','EB','ZB','YB']
329
330
 
330
331
  if str =~ /(\d+(\.\d+)?)\s*(\w+)?/
@@ -340,9 +341,9 @@ module RSQL
340
341
  raise "unable to parse '#{str}'"
341
342
  end
342
343
 
343
- # convert a time into a relative string from now
344
+ # Convert a time into a relative string from now.
344
345
  #
345
- def relative_time(dt)
346
+ def relative_time(dt) # :doc:
346
347
  return dt unless String === dt
347
348
 
348
349
  now = Time.now.utc
@@ -372,19 +373,19 @@ module RSQL
372
373
  return "#{fmt % diff} seconds #{postfix}"
373
374
  end
374
375
 
375
- # squeeze out any spaces
376
+ # Squeeze out any spaces.
376
377
  #
377
- def sqeeze!(sql)
378
+ def sqeeze!(sql) # :doc:
378
379
  sql.gsub!(/\s+/,' ')
379
380
  sql.strip!
380
381
  sql << ';' unless sql[-1] == ?;
381
382
  sql
382
383
  end
383
384
 
384
- # safely store an object into a file keeping at most one
385
- # backup if the file already exists
385
+ # Safely store an object into a file keeping at most one
386
+ # backup if the file already exists.
386
387
  #
387
- def safe_save(obj, name)
388
+ def safe_save(obj, name) # :doc:
388
389
  name += '.yml' unless File.extname(name) == '.yml'
389
390
  tn = "#{name}.tmp"
390
391
  File.open(tn, 'w'){|f| YAML.dump(obj, f)}
@@ -1,3 +1,4 @@
1
+ #--
1
2
  # Copyright (C) 2011 by Brad Robel-Forrest <brad+rsql@gigglewax.com>
2
3
  #
3
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -21,7 +22,7 @@
21
22
  module RSQL
22
23
 
23
24
  ########################################
24
- # A wrapper to make it easier to work with MySQL results (and prettier)
25
+ # A wrapper to make it easier to work with MySQL results (and prettier).
25
26
  #
26
27
  class MySQLResults
27
28
 
@@ -51,20 +52,21 @@ module RSQL
51
52
  def max_rows; @@max_rows; end
52
53
  def max_rows=(cnt); @@max_rows = cnt; end
53
54
 
54
- # get the name of the current database in use
55
+ # Get the name of the current database in use.
55
56
  #
56
57
  def database_name; @@database_name; end
57
58
 
58
- # get the list of databases available
59
+ # Get the list of databases available.
59
60
  #
60
61
  def databases
61
62
  @@databases ||= @@conn.list_dbs.sort if @@conn
62
63
  end
63
64
 
64
- # get the list of tables available (if a database is
65
- # selected) at most once every ten seconds
66
- #
67
65
  @@last_table_list = Hash.new{|h,k| h[k] = [Time.at(0), []]}
66
+
67
+ # Get the list of tables available (if a database is
68
+ # selected) at most once every ten seconds.
69
+ #
68
70
  def tables(database = nil)
69
71
  now = Time.now
70
72
  (last, tables) = @@last_table_list[database]
@@ -85,8 +87,8 @@ module RSQL
85
87
  tables
86
88
  end
87
89
 
88
- # provide a list of tab completions given the prompted
89
- # value
90
+ # Provide a list of tab completions given the prompted
91
+ # value.
90
92
  #
91
93
  def complete(str)
92
94
  return [] unless @@conn
@@ -116,7 +118,7 @@ module RSQL
116
118
  return ret
117
119
  end
118
120
 
119
- # get results from a query
121
+ # Get results from a query.
120
122
  #
121
123
  def query(sql, eval_context, raw=false, max_rows=@@max_rows)
122
124
  start = Time.now.to_f
@@ -199,31 +201,35 @@ module RSQL
199
201
  end
200
202
  end
201
203
 
202
- # get the number of rows that were affected by the query
204
+ # Get the query associated with these results.
203
205
  #
204
- attr_reader :sql, :affected_rows
206
+ attr_reader :sql
205
207
 
206
- # determine if there are any results
208
+ # Get the number of rows that were affected by the query.
209
+ #
210
+ attr_reader :affected_rows
211
+
212
+ # Determine if there are any results.
207
213
  #
208
214
  def any?
209
215
  !@table.nil?
210
216
  end
211
217
 
212
- # determine if there are no results
218
+ # Determine if there are no results.
213
219
  #
214
220
  def empty?
215
221
  @table.nil?
216
222
  end
217
223
 
218
- # get the number of rows available in the results
224
+ # Get the number of rows available in the results.
219
225
  #
220
226
  def num_rows
221
227
  @table ? @table.size : 0
222
228
  end
223
229
 
224
- # get a row from the table hashed with the field names
230
+ # Get a row from the table hashed with the field names.
225
231
  #
226
- def row_hash(index)
232
+ def [](index)
227
233
  hash = {}
228
234
  if @fields && @table
229
235
  row = @table[index]
@@ -232,8 +238,8 @@ module RSQL
232
238
  return hash
233
239
  end
234
240
 
235
- # iterate through each row of the table hashed with the field
236
- # names
241
+ # Iterate through each row of the table hashed with the field
242
+ # names.
237
243
  #
238
244
  def each_hash(&block)
239
245
  if @table
@@ -245,7 +251,7 @@ module RSQL
245
251
  end
246
252
  end
247
253
 
248
- # show a set of results in a decent fashion
254
+ # Show a set of results in a decent fashion.
249
255
  #
250
256
  def display_by_column(io=$stdout)
251
257
  if @fields && @table
@@ -268,7 +274,7 @@ module RSQL
268
274
  end
269
275
  end
270
276
 
271
- # show a set of results with a single character separation
277
+ # Show a set of results with a single character separation.
272
278
  #
273
279
  def display_by_batch(io=$stdout)
274
280
  if @fields && @table
@@ -277,7 +283,7 @@ module RSQL
277
283
  end
278
284
  end
279
285
 
280
- # show a set of results line separated
286
+ # Show a set of results line separated.
281
287
  #
282
288
  def display_by_line(io=$stdout)
283
289
  if @fields && @table
@@ -297,6 +303,8 @@ module RSQL
297
303
  display_stats(io)
298
304
  end
299
305
 
306
+ # Show a summary line of the results.
307
+ #
300
308
  def display_stats(io=$stdout, hdr='')
301
309
  if @table
302
310
  if @database_changed
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsql
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 4
10
- version: 0.1.4
9
+ - 5
10
+ version: 0.1.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brad Robel-Forrest
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-15 00:00:00 Z
18
+ date: 2011-05-16 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: net-ssh
@@ -34,7 +34,7 @@ dependencies:
34
34
  type: :runtime
35
35
  version_requirements: *id001
36
36
  description: |
37
- Rsql makes working with a MySQL command line more convenient through
37
+ RSQL makes working with a MySQL command line more convenient through
38
38
  the use of recipes and embedding the common operation of using a SSH
39
39
  connection to an intermediary host for access to the MySQL server.
40
40
 
@@ -43,11 +43,11 @@ executables:
43
43
  - rsql
44
44
  extensions: []
45
45
 
46
- extra_rdoc_files: []
47
-
46
+ extra_rdoc_files:
47
+ - README.rdoc
48
48
  files:
49
49
  - LICENSE
50
- - README.txt
50
+ - README.rdoc
51
51
  - TODO
52
52
  - bin/rsql
53
53
  - example.rsqlrc
@@ -60,8 +60,11 @@ homepage: https://github.com/bradrf/rsql
60
60
  licenses: []
61
61
 
62
62
  post_install_message:
63
- rdoc_options: []
64
-
63
+ rdoc_options:
64
+ - --title
65
+ - RSQL Documentation
66
+ - --main
67
+ - README.rdoc
65
68
  require_paths:
66
69
  - lib
67
70
  required_ruby_version: !ruby/object:Gem::Requirement
data/README.txt DELETED
@@ -1,130 +0,0 @@
1
- = rsql
2
-
3
- https://github.com/bradrf/rsql
4
-
5
- == DESCRIPTION
6
-
7
- This is an application to make working with a SQL command line more
8
- convenient by allowing interaction with Ruby in addition to embedding
9
- the common operation of using a SSH connection to an intermediary host
10
- for access to the SQL server.
11
-
12
- == SYNOPSIS
13
-
14
- Run rsql with no arguments for usage.
15
-
16
- Aside from the standard MySQL command syntax, the following
17
- functionality allows for a little more expressive processing.
18
-
19
- Multiple commands can be issued in one set by separation with
20
- semicolons.
21
-
22
- Generating SQL
23
- --------------
24
-
25
- Ruby code may be called to generate the SQL that is to be executed.
26
- This is done by starting any command string with a period. If the
27
- final result of evaluating the command string is another string, it is
28
- executed as SQL. Any semicolons meant to be processed by Ruby must be
29
- escaped. Example:
30
-
31
- rsql> . puts 'hello world!' \; 'select * from Account';
32
-
33
- Utilizing Canned Methods (aka "Recipes")
34
- ----------------------------------------
35
-
36
- Commands can be stored in the .rsqlrc file in your HOME directory to
37
- expose methods that may be invoked to generate SQL with variable
38
- interpolation. Use of the 'register' helper is recommended for this
39
- approach. These can then be called in the same way as above. Example:
40
-
41
- In the .rsqlrc file...
42
-
43
- register :users_by_email, :email %q{
44
- SELECT * FROM Users WHERE email = '#{email}'
45
- }
46
-
47
- ...then from the prompt:
48
-
49
- rsql> . users_by_email 'brad@gigglewax.com';
50
-
51
- If a block is provided to the registration, it will be called as a
52
- method. Example:
53
-
54
- In the .sqlrc file...
55
-
56
- register :dummy, :hello do |*args|
57
- p args
58
- end
59
-
60
- rsql> . dummy :world;
61
-
62
- All registered methods can be listed using the built-in 'list'
63
- command.
64
-
65
- Changes to a sourced file can be reloaded using the built-in 'reload'
66
- command.
67
-
68
- Processing Column Data
69
- ----------------------
70
-
71
- Ruby can be called to process any data on a per-column basis before a
72
- displayer is used to render the output. In this way, one can write
73
- Ruby to act like MySQL functions on all the data for a given column,
74
- converting it into a more readable value. A bang indicator (exlamation
75
- point: !) is used to demarcate a mapping of column names to Ruby
76
- methods that should be invoked to processes content. Example:
77
-
78
- rsql> select IpAddress from Devices ! IpAddress => bin_to_str;
79
-
80
- This will call 'bin_to_str' for each 'IpAddress' returned from the
81
- query. Mulitple mappings are separated by a comma. These mappings can
82
- also be utilized in a canned method. Example:
83
-
84
- register :all_ips, 'select IpAddress from Devices', 'IpAddress' => :bin_to_str
85
-
86
- Redirection
87
- -----------
88
-
89
- Output from one or more queries may be post-processed dynamically. If
90
- any set of commands is follwed by a pipe symbol, results from the
91
- previous command will be available in a member variable as
92
- @results. The results can be used through any of the MySQLResults
93
- class' public methods (e.g. each_hash) and the final Ruby code will be
94
- evaluated with access to them. Any result of the evaluation that is a
95
- string is then executed as SQL. Example:
96
-
97
- rsql> select * from Users | @results.each_hash{|r| p r};
98
-
99
- And again, it's most useful to encapsulate the processing logic within
100
- a registered ruby block that can be called after the pipe as a single
101
- command.
102
-
103
- Even More!
104
- ----------
105
-
106
- For additional examples and functionality, read and try the
107
- example.rsqlrc file.
108
-
109
- == LICENSE
110
-
111
- Copyright (C) 2011 by Brad Robel-Forrest <brad+rsql@gigglewax.com>
112
-
113
- Permission is hereby granted, free of charge, to any person obtaining
114
- a copy of this software and associated documentation files (the
115
- "Software"), to deal in the Software without restriction, including
116
- without limitation the rights to use, copy, modify, merge, publish,
117
- distribute, sublicense, and/or sell copies of the Software, and to
118
- permit persons to whom the Software is furnished to do so, subject to
119
- the following conditions:
120
-
121
- The above copyright notice and this permission notice shall be
122
- included in all copies or substantial portions of the Software.
123
-
124
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
125
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
126
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
127
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
128
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
129
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
130
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.