jrubysql 0.1.1

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in jrubysql.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Junegunn Choi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ jrubysql
2
+ ========
3
+ An SQL client for any JDBC-compliant database. Written in JRuby.
4
+
5
+ Installation
6
+ ------------
7
+
8
+ ```
9
+ gem install jrubysql
10
+ ```
11
+
12
+ Usage
13
+ -----
14
+
15
+ ```
16
+ usage: jrubysql [options]
17
+ jrubysql -t DBMS_TYPE -h HOSTNAME [-u USERNAME -p [PASSWORD] [-d DATABASE]] [-f FILENAME]
18
+ jrubysql -c CLASSNAME -j JDBC_URL [-u USERNAME -p [PASSWORD] [-d DATABASE]] [-f FILENAME]
19
+
20
+ -t, --type DBMS_TYPE Database type: mysql/oracle/postgres/sqlserver
21
+ -h, --host HOST DBMS host address
22
+
23
+ -c, --class-name CLASSNAME Class name of the JDBC driver
24
+ -j, --jdbc-url JDBC_URL JDBC URL for the connection
25
+
26
+ -u, --user USERNAME Username
27
+ -p, --password [PASSWORD] Password
28
+ -d, --database DATABASE Name of the database (optional)
29
+
30
+ -f, --filename FILENAME SQL script file
31
+ -o, --output OUTPUT_TYPE Output type: cterm|term|csv (default: cterm)
32
+
33
+ --help Show this message
34
+ --version Show version
35
+ ```
36
+
37
+ Connecting to the database
38
+ --------------------------
39
+
40
+ ### Setting up CLASSPATH
41
+ Add the appropriate JDBC drivers to the CLASSPATH.
42
+
43
+ ```
44
+ export CLASSPATH=$CLASSPATH:~/lib/mysql-connector-java-5.1.17-bin.jar:~/lib/ojdbc6.jar
45
+ ```
46
+
47
+ ### With type (-t) and hostname (-h)
48
+
49
+ ```
50
+ # Supports MySQL/Oracle/PostgreSQL/MSSQL
51
+
52
+ jrubysql -t mysql -h localhost -d test -u user -p
53
+ jrubysql -t oracle -h localhost:1521/orcl -u user -p password
54
+ jrubysql -t postgres -h localhost -u root
55
+ jrubysql -t sqlserver -h 192.168.62.26 -u user -p password
56
+ ```
57
+
58
+ ### Connect with class name of JDBC driver (-c) and JDBC URL (-j)
59
+
60
+ ```
61
+ # You can connect to any database with its JDBC driver
62
+
63
+ bin/jrubysql -corg.postgresql.Driver -jjdbc:postgresql://localhost/test
64
+ bin/jrubysql -ccom.mysql.jdbc.Driver -jjdbc:mysql://localhost/test -uuser -p
65
+ ```
66
+
67
+ Screenshot
68
+ ----------
69
+ ![](https://github.com/junegunn/jrubysql/raw/master/screenshots/simpsons.png)
70
+
71
+ TODO
72
+ ----
73
+ TESTS!!!
74
+
75
+ Copyright
76
+ ---------
77
+ Copyright (c) 2012 Junegunn Choi. See LICENSE.txt for
78
+ further details.
79
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
data/bin/jrubysql ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+ # Junegunn Choi (junegunn.c@gmail.com)
4
+ # 2012/03/07-
5
+
6
+ require 'rubygems'
7
+ require 'jrubysql'
8
+
9
+ JRubySQL.launch ARGV
data/jrubysql.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "jrubysql/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "jrubysql"
7
+ s.version = JRubySQL::VERSION
8
+ s.authors = ["Junegunn Choi"]
9
+ s.email = ["junegunn.c@gmail.com"]
10
+ s.homepage = "https://github.com/junegunn/jrubysql"
11
+ s.summary = %q{An SQL client for any JDBC-compliant database.}
12
+ s.description = %q{An SQL client for any JDBC-compliant database. Written in JRuby.}
13
+
14
+ s.rubyforge_project = "jrubysql"
15
+
16
+ s.files = `git ls-files`.split("\n").reject { |f| f =~ /^screenshots/ }
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "test-unit"
23
+
24
+ s.add_runtime_dependency "jdbc-helper", '~> 0.7.2'
25
+ s.add_runtime_dependency "insensitive_hash", '~> 0.2.3'
26
+ s.add_runtime_dependency "tabularize", '~> 0.1.1'
27
+ s.add_runtime_dependency "each_sql", '~> 0.3.1'
28
+ s.add_runtime_dependency "highline", '~> 1.6.11'
29
+ s.add_runtime_dependency "ansi", '~> 1.4.2'
30
+ s.add_runtime_dependency "erubis", '~> 2.7.0'
31
+ end
@@ -0,0 +1,35 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+
4
+ module JRubySQL
5
+ # A simple key-value config in YAML
6
+ class Config
7
+ DEFAULT_PATH = File.join(ENV['HOME'], '.jrubysqlrc')
8
+
9
+ def initialize path = DEFAULT_PATH
10
+ @path = path
11
+ if @path && File.exists?(@path)
12
+ @yaml = YAML.load(File.read(@path))
13
+ end
14
+ @yaml = @yaml || {}
15
+ end
16
+
17
+ def [] key
18
+ @yaml[key]
19
+ end
20
+
21
+ def []= key, value
22
+ (@yaml[key] = value).tap { dump }
23
+ end
24
+
25
+ private
26
+ def dump
27
+ # Try to write atomically
28
+ File.open(@path + '.tmp', 'w') do |f|
29
+ f << YAML.dump(@yaml)
30
+ end
31
+ FileUtils.mv(@path + '.tmp', @path)
32
+ end
33
+ end#Config
34
+ end#JRubySQL
35
+
@@ -0,0 +1,15 @@
1
+ module JRubySQL
2
+ module Constants
3
+ SUPPORTED_DBMS_TYPES = [ :mysql, :oracle, :postgres, :sqlserver ]
4
+
5
+ # .jrubysqlrc
6
+ MAX_COMMAND_HISTORY = 100
7
+ MAX_CONNECTION_HISTORY = 10
8
+
9
+ # Terminal (TBD)
10
+ MAX_COLUMN_WIDTH = 80
11
+ MIN_SCREEN_ROWS = 10
12
+ MAX_SCREEN_ROWS = 50
13
+
14
+ end#Constants
15
+ end#JRubySQL
@@ -0,0 +1,141 @@
1
+ require 'quote_unquote'
2
+
3
+ module JRubySQL
4
+ class Controller
5
+ include JRubySQL::Messages
6
+
7
+ attr_reader :db_type
8
+
9
+ def initialize options, argv_str
10
+ @config = JRubySQL::Config.new
11
+ histories = @config['connections']
12
+
13
+ if options.nil?
14
+ if histories.nil? || histories.empty?
15
+ JRubySQL::OptionParser.parse []
16
+ else
17
+ # FIXME: Output
18
+ puts m(:choose_parameter)
19
+ histories.each_with_index do |history, idx|
20
+ puts "[#{idx + 1}] #{history.first}"
21
+ end
22
+ print '> '
23
+ select = $stdin.gets
24
+ select = select && select.chomp
25
+ if (1..(histories.length)).include?(select.to_i)
26
+ history = histories[select.to_i - 1]
27
+ @options = history.last
28
+ @argv_str = history.first
29
+ else
30
+ puts
31
+ JRubySQL::OptionParser.parse []
32
+ end
33
+ end
34
+ else
35
+ @options = options
36
+ @argv_str = argv_str
37
+ end
38
+
39
+ @db_type = JRubySQL::RDBMS.get_type(@options[:type] || @options[:driver])
40
+
41
+ # Setting up input: file or console (and more?)
42
+ if @options[:filename]
43
+ @input = JRubySQL::Input::File.new(self, @options[:filename])
44
+ else
45
+ @input = JRubySQL::Input::Console.new(self)
46
+ end
47
+
48
+ # Setting up output: Colored terminal
49
+ case @options[:output]
50
+ when 'cterm'
51
+ @output = JRubySQL::Output::CTerm.new
52
+ when 'term'
53
+ @output = JRubySQL::Output::Term.new
54
+ when 'csv'
55
+ @output = JRubySQL::Output::CSV.new
56
+ end
57
+ end
58
+
59
+ def start
60
+ @output.welcome!
61
+ @output.info m(:connecting)
62
+ begin
63
+ @rdbms = JRubySQL::RDBMS.new @options
64
+ rescue Exception => e
65
+ @output.error e.to_s
66
+ exit 1
67
+ end
68
+ @output.info m(:connected)
69
+
70
+ history = @config['connections'] || []
71
+ history.unshift [@argv_str, @options]
72
+ history.uniq!
73
+ if history.length > JRubySQL::Constants::MAX_CONNECTION_HISTORY
74
+ history = history[0, JRubySQL::Constants::MAX_CONNECTION_HISTORY]
75
+ end
76
+ @config['connections'] = history
77
+
78
+ loop do
79
+ ret = @input.get
80
+
81
+ ret[:sqls].each do |sql|
82
+ begin
83
+ output @rdbms.execute(sql)
84
+ rescue Exception => e
85
+ @output.error e.to_s
86
+ end
87
+ end if ret.has_key?(:sqls)
88
+
89
+ ret[:commands].each do |command|
90
+ process_command command.keys.first, command.values.first
91
+ end if ret.has_key?(:commands)
92
+ end
93
+ end
94
+
95
+ def cursor empty = true
96
+ @output.cursor empty
97
+ end
98
+
99
+ def print_cursor empty = true
100
+ @output.print_cursor empty
101
+ end
102
+
103
+ private
104
+ def output result
105
+ @output.print_result result
106
+ end
107
+
108
+ def process_command cmd, params
109
+ case cmd
110
+ when :help
111
+ @output.print_help
112
+ when :quit
113
+ @output.info m(:goodbye)
114
+ quit!
115
+ when :delimiter
116
+ @output.info m(:set_delimiter, params)
117
+ @input.delimiter = params
118
+ when :now
119
+ @output.info Time.now.strftime('%Y/%m/%d %H:%M:%S.%L')
120
+ when :autocommit
121
+ if params.nil?
122
+ @output.info m(:current_autocommit, @rdbms.autocommit ? 'on' : 'off')
123
+ elsif %[on off].include?(params.downcase)
124
+ @rdbms.autocommit = params.downcase == 'on'
125
+ @output.info m(:turn_autocommit, params.downcase)
126
+ else
127
+ @output.error m(:invalid_autocommit, params)
128
+ end
129
+ else
130
+ # TODO
131
+ @output.error m(:unknown_command)
132
+ end
133
+ end
134
+
135
+ def quit!
136
+ @rdbms.close rescue nil
137
+ exit 0
138
+ end
139
+
140
+ end#Controller
141
+ end#JRubySQL
@@ -0,0 +1,10 @@
1
+ <% title = JRubySQL.name %>
2
+ <%= title %>
3
+ <%= '=' * title.length %>
4
+
5
+ help Display this message.
6
+ <SQL> Execute the SQL.
7
+ autocommit [<on|off>] Show/enable/disable autocommit. (default: on)
8
+ delimiter <DELIMITER> Change SQL delimiter.
9
+ now Display current timestamp.
10
+ exit Exit JRubySQL.
@@ -0,0 +1,84 @@
1
+ require 'readline'
2
+
3
+ module JRubySQL
4
+ module Input
5
+ class Console
6
+ def initialize controller
7
+ @controller = controller
8
+ @esql = JRubySQL::Input.get_parser @controller.db_type
9
+ end
10
+
11
+ def get
12
+ empty_response = { :sqls => [], :commands => [] }
13
+ line = Readline::readline(@controller.cursor(@esql.empty?), false)
14
+
15
+ return(
16
+ # CTRL-D
17
+ if line.nil?
18
+ puts
19
+ @esql.clear
20
+ empty_response
21
+ # Empty input (not inside a block)
22
+ elsif @esql.empty? && line.gsub(/\s/m, '').empty?
23
+ empty_response
24
+ # Console commands
25
+ elsif @esql.empty? && cmd = process_command(line)
26
+ { :commands => [cmd].compact }
27
+ # Line with delimiters
28
+ elsif line.include?(@esql.delimiter)
29
+ @esql << line + ' '
30
+ result = @esql.shift
31
+ result[:sqls].each do |sql|
32
+ Readline::HISTORY << sql + @esql.delimiter
33
+ end
34
+ { :sqls => result[:sqls] }
35
+ # SQL without delimiter
36
+ else
37
+ @esql << line + ' '
38
+ empty_response
39
+ end
40
+ )
41
+ end
42
+
43
+ def delimiter
44
+ @esql.delimiter
45
+ end
46
+
47
+ def delimiter= delim
48
+ @esql.delimiter = delim
49
+ end
50
+
51
+ private
52
+
53
+ def process
54
+ result = @esql.shift
55
+ result[:sqls].each do |sql|
56
+ Readline::HISTORY << sql + ';'
57
+ @controller.execute sql
58
+ end
59
+ end
60
+
61
+ def process_command line
62
+ Readline::HISTORY << line
63
+ case line.chomp.downcase.strip
64
+ when /^help(#{Regexp.escape delimiter})?$/
65
+ { :help => nil }
66
+ when /^delimiter (\S+$)/
67
+ { :delimiter => $1 }
68
+ when 'autocommit'
69
+ { :autocommit => nil }
70
+ when /^autocommit (\S+?)(#{Regexp.escape delimiter})?$/
71
+ { :autocommit => $1 }
72
+ when 'now'
73
+ { :now => nil }
74
+ when /^(exit|quit)(#{Regexp.escape delimiter})?$/
75
+ { :quit => nil }
76
+ else
77
+ Readline::HISTORY.pop
78
+ nil
79
+ end
80
+ end
81
+ end#Console
82
+ end#Input
83
+ end#JRubySQL
84
+
@@ -0,0 +1,19 @@
1
+ require 'each_sql'
2
+
3
+ module JRubySQL
4
+ module Input
5
+ class File
6
+ def initialize controller, file_path
7
+ @controller = controller
8
+ script = ::File.read(file_path)
9
+ sqls = EachSQL(script, JRubySQL::Input.get_each_sql_type(@controller.db_type))
10
+ @ret = { :sqls => sqls }
11
+ end
12
+
13
+ def get
14
+ @ret.tap { @ret = { :commands => [{ :quit => nil }] } }
15
+ end
16
+ end#Console
17
+ end#Input
18
+ end#JRubySQL
19
+
@@ -0,0 +1,17 @@
1
+ require 'each_sql'
2
+
3
+ module JRubySQL
4
+ module Input
5
+ def self.get_each_sql_type db_type
6
+ {
7
+ :mysql => :mysql,
8
+ :oracle => :oracle,
9
+ :postgres => :postgres
10
+ }[db_type] || :default
11
+ end
12
+
13
+ def self.get_parser db_type, delimiter = ';'
14
+ EachSQL.new(get_each_sql_type(db_type), delimiter)
15
+ end
16
+ end#Input
17
+ end#JRubySQL
@@ -0,0 +1,23 @@
1
+ require 'yaml'
2
+ require 'insensitive_hash/minimal'
3
+
4
+ module JRubySQL
5
+ module Messages
6
+ def m *args
7
+ args = args.dup
8
+ type = args.shift
9
+ msg = MESSAGES[type]
10
+ args.each do |arg|
11
+ msg = msg.sub('$$', arg.to_s)
12
+ end
13
+ msg
14
+ end
15
+
16
+ private
17
+ MESSAGES =
18
+ InsensitiveHash[
19
+ YAML.load(File.read File.join( File.dirname(__FILE__), 'messages.yml' ))
20
+ ].tap { |ih| ih.underscore = true }
21
+ end#Messages
22
+ end#JRubySQL
23
+
@@ -0,0 +1,43 @@
1
+ ---
2
+ choose parameter:
3
+ "Parameter not given. Choose one:"
4
+ ask password:
5
+ "Password: "
6
+
7
+ invalid connection:
8
+ Invalid connection specification
9
+ unsupported database:
10
+ $$ is not supported yet. Try with -c and -j options instead.
11
+ oracle service name required:
12
+ Oracle service name must be included in the hostname: e.g. localhost:1521/orcl
13
+ invalid output:
14
+ Invalid output type.
15
+ file not found:
16
+ "File not found: $$"
17
+
18
+ connecting:
19
+ Connecting to the database ...
20
+ connected:
21
+ Connected.
22
+
23
+ set delimiter:
24
+ Setting delimiter to $$
25
+ current autocommit:
26
+ "Current autocommit: $$"
27
+ turn autocommit:
28
+ "Turning autocommit $$"
29
+ invalid autocommit:
30
+ "Invalid option: '$$'. Required: [on|off]"
31
+ unknown command:
32
+ Unknown command. Possibly a bug.
33
+ goodbye:
34
+ Goodbye!
35
+
36
+ interrupted:
37
+ Interrupted.
38
+
39
+ rows returned:
40
+ $$ row$$. $$
41
+ rows affected:
42
+ $$ row$$ affected. $$
43
+
@@ -0,0 +1,124 @@
1
+ require 'optparse'
2
+ require 'highline'
3
+
4
+ module JRubySQL
5
+ module OptionParser
6
+ class << self
7
+ include JRubySQL::Messages
8
+ end
9
+
10
+ def self.parse argv
11
+ {:output => 'cterm'}.tap do |options|
12
+ opts = ::OptionParser.new { |opts|
13
+ opts.banner =
14
+ [
15
+ "usage: jrubysql [options]",
16
+ " jrubysql -t DBMS_TYPE -h HOSTNAME [-u USERNAME -p [PASSWORD] [-d DATABASE]] [-f FILENAME]",
17
+ " jrubysql -c CLASSNAME -j JDBC_URL [-u USERNAME -p [PASSWORD] [-d DATABASE]] [-f FILENAME]"
18
+ ].join($/)
19
+ opts.separator ''
20
+
21
+ opts.on('-t', '--type DBMS_TYPE', 'Database type: mysql/oracle/postgres/sqlserver') do |v|
22
+ options[:type] = v.downcase.to_sym
23
+ end
24
+
25
+ opts.on('-h', '--host HOST', 'DBMS host address') do |v|
26
+ options[:host] = v
27
+ end
28
+
29
+ opts.separator ""
30
+
31
+ opts.on('-c', '--class-name CLASSNAME', 'Class name of the JDBC driver') do |v|
32
+ options[:driver] = v
33
+ end
34
+
35
+ opts.on('-j', '--jdbc-url JDBC_URL', 'JDBC URL for the connection') do |v|
36
+ options[:url] = v
37
+ end
38
+
39
+ opts.separator ""
40
+
41
+ opts.on('-u', '--user USERNAME', 'Username') do |v|
42
+ options[:user] = v
43
+ end
44
+
45
+ opts.on('-p', '--password [PASSWORD]', 'Password') do |v|
46
+ options[:password] = v
47
+ end
48
+
49
+ opts.on('-d', '--database DATABASE', 'Name of the database (optional)') do |v|
50
+ options[:database] = v
51
+ end
52
+
53
+ opts.separator ""
54
+
55
+ opts.on('-f', '--filename FILENAME', 'SQL script file') do |v|
56
+ options[:filename] = v
57
+ end
58
+
59
+ opts.on('-o', '--output OUTPUT_TYPE', 'Output type: cterm|term|csv (default: cterm)') do |v|
60
+ options[:output] = v
61
+ end
62
+
63
+ opts.separator ""
64
+
65
+ opts.on_tail('--help', "Show this message") do
66
+ puts opts
67
+ exit
68
+ end
69
+
70
+ opts.on_tail('--version', "Show version") do
71
+ puts JRubySQL.name
72
+ exit
73
+ end
74
+ }
75
+ begin
76
+ opts.parse! argv
77
+ if options.has_key?(:password) && options[:password].nil?
78
+ options[:password] = ask_password
79
+ end
80
+
81
+ validate options
82
+ rescue SystemExit
83
+ exit 0
84
+ rescue Exception => e
85
+ puts e.to_s
86
+ puts '=' * e.to_s.length
87
+ puts opts
88
+ exit 1
89
+ end
90
+ end#tap
91
+ end
92
+
93
+ private
94
+ def self.validate opts
95
+ unless %w[cterm term csv].include?(opts[:output])
96
+ raise ArgumentError.new m(:invalid_output)
97
+ end
98
+
99
+ if (!opts[:type] && !opts[:driver]) || (opts[:type] && opts[:driver])
100
+ raise ArgumentError.new m(:invalid_connection)
101
+ end
102
+
103
+ unless (opts[:type] && opts[:host]) || (opts[:driver] && opts[:url])
104
+ raise ArgumentError.new m(:invalid_connection)
105
+ end
106
+
107
+ if opts[:type] && !JRubySQL::Constants::SUPPORTED_DBMS_TYPES.include?(opts[:type])
108
+ raise ArgumentError.new m(:unsupported_database, opts[:type])
109
+ end
110
+
111
+ if opts[:filename] && !File.exists?(opts[:filename])
112
+ raise ArgumentError.new m(:file_not_found, opts[:filename])
113
+ end
114
+
115
+ end
116
+
117
+ def self.ask_password
118
+ HighLine.new.ask(m(:ask_password)) { |q| q.echo = "*" }
119
+ end
120
+
121
+ end#OptionParser
122
+ end#JRubySQL
123
+
124
+ # p JRubySQL::OptionParser.parse %w[-h aa gg -t asdfa -p]
@@ -0,0 +1,49 @@
1
+ require 'csv'
2
+
3
+ module JRubySQL
4
+ module Output
5
+ class CSV
6
+ def welcome!; end
7
+ def info msg
8
+ $stderr.puts "[I] #{msg}"
9
+ end
10
+ def result msg
11
+ $stderr.puts "[R] #{msg}"
12
+ end
13
+ def warn msg
14
+ $stderr.puts "[W] #{msg}"
15
+ end
16
+ def error msg
17
+ $stderr.puts "[E] #{msg}"
18
+ end
19
+ def print_help; end
20
+
21
+ def cursor empty
22
+ ''
23
+ end
24
+
25
+ def print_cursor empty; end
26
+
27
+ def print_result ret
28
+ # Footer
29
+ elapsed = "(#{'%.2f' % ret[:elapsed]} sec)"
30
+
31
+ if ret[:set?]
32
+ ret[:result].each_with_index do |row, idx|
33
+ puts ::CSV.generate_line(row.labels) if idx == 0
34
+ puts ::CSV.generate_line row.map { |col|
35
+ case col
36
+ when BigDecimal
37
+ col.to_s('F')
38
+ else
39
+ col
40
+ end
41
+ }
42
+ end
43
+ end
44
+ end
45
+
46
+ end#CSV
47
+ end#Output
48
+ end#JRubySQL
49
+
@@ -0,0 +1,95 @@
1
+ require 'ansi'
2
+ require 'erubis'
3
+ require 'bigdecimal'
4
+
5
+ module JRubySQL
6
+ module Output
7
+ class CTerm < Term
8
+ include ANSI::Code
9
+ HELP = Erubis::Eruby.new(File.read File.join(File.dirname(__FILE__), '../doc/help.txt.erb')).result(binding)
10
+
11
+ def welcome!
12
+ puts bold + JRubySQL.name + reset
13
+ end
14
+
15
+ def cursor empty
16
+ if empty
17
+ wrap('jrubysql', bold) +
18
+ wrap('> ', bold + green)
19
+ else
20
+ wrap(' -', bold + yellow) +
21
+ wrap('> ', bold + red)
22
+ end
23
+ end
24
+
25
+ def print_cursor empty
26
+ print cursor(empty)
27
+ end
28
+
29
+ def print_help
30
+ puts
31
+ puts wrap(HELP, blue + bold)
32
+ puts
33
+ end
34
+
35
+ def info message
36
+ col = blue + bold
37
+ puts wrap(message, col)
38
+ end
39
+
40
+ def result message
41
+ col = green
42
+ puts wrap(message, green)
43
+ end
44
+
45
+ def warn message
46
+ col = yellow + bold
47
+ puts wrap(message, col)
48
+ end
49
+
50
+ def error message
51
+ col = red + bold
52
+ puts wrap(message, col)
53
+ end
54
+
55
+ private
56
+ def separator_for row
57
+ # 13-bytes for ANSI codes
58
+ # - bold/reset: 4
59
+ # - colors: 5
60
+ '+-' + row.map { |e| '-' * (e.length - 13) }.join('-+-') + '-+'
61
+ end
62
+
63
+ def decorate_label label
64
+ white + bold + label + reset
65
+ end
66
+
67
+ # This looks stupid though.
68
+ def decorate value
69
+ case value
70
+ when BigDecimal
71
+ cyan + value.to_s('F') + reset + reset
72
+ when Numeric
73
+ cyan + value.to_s + reset + reset
74
+ when String
75
+ yellow + value + reset + reset
76
+ when Time, Java::JavaSql::Timestamp
77
+ magenta + value.to_s + reset + reset
78
+ when NilClass
79
+ bold + red + '(null)' + reset
80
+ else
81
+ white + reset + value.to_s + reset
82
+ end
83
+ end
84
+
85
+ def cnow
86
+ green + now + reset
87
+ end
88
+
89
+ def wrap text, color
90
+ color + text + reset
91
+ end
92
+
93
+ end#CTerm
94
+ end#Output
95
+ end#JRubySQL
@@ -0,0 +1,122 @@
1
+ require 'erubis'
2
+ require 'tabularize'
3
+ require 'java'
4
+
5
+ module JRubySQL
6
+ module Output
7
+ class Term
8
+ include JRubySQL::Messages
9
+
10
+ HELP = Erubis::Eruby.new(File.read File.join(File.dirname(__FILE__), '../doc/help.txt.erb')).result(binding)
11
+
12
+ def initialize
13
+ # Make use of JLine included in JRuby
14
+ java_import 'jline.Terminal'
15
+ @terminal = Terminal.getTerminal
16
+ trap 'INT' do
17
+ Thread.main.raise Interrupt
18
+ end
19
+ end
20
+
21
+ def welcome!
22
+ puts JRubySQL.name
23
+ end
24
+
25
+ def cursor empty
26
+ if empty
27
+ 'jrubysql> '
28
+ else
29
+ ' -> '
30
+ end
31
+ end
32
+
33
+ def print_cursor empty
34
+ print cursor(empty)
35
+ end
36
+
37
+ def print_help
38
+ puts
39
+ puts HELP
40
+ puts
41
+ end
42
+
43
+ def info message
44
+ puts "[I] #{message}"
45
+ end
46
+
47
+ def result message
48
+ puts "[R] #{message}"
49
+ end
50
+
51
+ def warn message
52
+ puts "[W] #{message}"
53
+ end
54
+
55
+ def error message
56
+ puts "[E] #{message}"
57
+ end
58
+
59
+ def print_result ret
60
+ # Footer
61
+ elapsed = "(#{'%.2f' % ret[:elapsed]} sec)"
62
+
63
+ if ret[:set?]
64
+ begin
65
+ cnt = print_table ret[:result]
66
+ result m(:rows_returned, cnt, cnt > 1 ? 's' : '', elapsed)
67
+ rescue Interrupt
68
+ warn m(:interrupted)
69
+ end
70
+ elsif ret[:result]
71
+ cnt = [0, ret[:result]].max
72
+ result m(:rows_affected, cnt, cnt > 1 ? 's' : '', elapsed)
73
+ else
74
+ result elapsed
75
+ end
76
+ puts
77
+ end
78
+
79
+ private
80
+ def print_table ret
81
+ cnt = 0
82
+ lines = [(@terminal.getTerminalHeight rescue JRubySQL::Constants::MAX_SCREEN_ROWS) - 5,
83
+ JRubySQL::Constants::MIN_SCREEN_ROWS].max
84
+ ret.each_slice(lines) do |slice|
85
+ cnt += slice.length
86
+
87
+ table = [slice.first.labels.map { |l| decorate_label l }] +
88
+ slice.map { |row| row.to_a.map { |v| decorate v } }
89
+
90
+ output = Tabularize.it(table, :unicode_display => true)
91
+ separator = separator_for(output.first)
92
+ output_strs = output.map { |r| '| ' + r.join(' | ') + ' |' }
93
+ [0, 2, -1].each { |l| output_strs.insert l, separator }
94
+ puts output_strs
95
+ end
96
+ cnt
97
+ end
98
+
99
+ def separator_for row
100
+ '+-' + row.map { |e| '-' * e.length }.join('-+-') + '-+'
101
+ end
102
+
103
+ def decorate_label label
104
+ label
105
+ end
106
+
107
+ def decorate value
108
+ case value
109
+ when BigDecimal
110
+ value.to_s('F')
111
+ else
112
+ value.to_s
113
+ end
114
+ end
115
+
116
+ def now
117
+ Time.now.strftime('%Y/%m/%d %H:%M:%S')
118
+ end
119
+ end#Term
120
+ end#Output
121
+ end#JRubySQL
122
+
@@ -0,0 +1,82 @@
1
+ require 'jdbc-helper'
2
+
3
+ module JRubySQL
4
+ class RDBMS
5
+ include JRubySQL::Messages
6
+
7
+ def self.get_type driver_or_type
8
+ case driver_or_type.to_s.downcase
9
+ when /oracle/
10
+ :oracle
11
+ when /mysql/
12
+ :mysql
13
+ when /postgres/
14
+ :postgres
15
+ when /sqlserver/
16
+ :sqlserver
17
+ else
18
+ :unknown
19
+ end
20
+ end
21
+
22
+ def initialize options
23
+ @conn =
24
+ if options[:type]
25
+ case options[:type]
26
+ when :mysql
27
+ JDBCHelper::MySQLConnector.connect(
28
+ options[:host], options[:user], options[:password], options[:database])
29
+ when :oracle
30
+ host, svc = options[:host].split('/')
31
+ if svc.nil?
32
+ # FIXME
33
+ raise ArgumentError.new m(:oracle_service_name_required)
34
+ end
35
+ JDBCHelper::OracleConnector.connect(
36
+ host, options[:user], options[:password], svc)
37
+ when :postgres
38
+ JDBCHelper::PostgresConnector.connect(
39
+ options[:host], options[:user], options[:password], options[:database])
40
+ when :sqlserver
41
+ JDBCHelper::SqlServerConnector.connect(
42
+ options[:host], options[:user], options[:password], options[:database])
43
+ end
44
+ elsif options[:driver]
45
+ JDBCHelper::Connection.new(
46
+ {
47
+ :driver => options[:driver],
48
+ :url => options[:url],
49
+ :user => options[:user],
50
+ :password => options[:password]
51
+ }.reject { |k, v| v.nil? }
52
+ )
53
+ else
54
+ raise ArgumentError.new m(:invalid_connection)
55
+ end
56
+ end
57
+
58
+ def autocommit
59
+ @conn.java_obj.get_auto_commit
60
+ end
61
+
62
+ def autocommit= ac
63
+ @conn.java_obj.set_auto_commit ac
64
+ end
65
+
66
+ def close
67
+ @conn.close
68
+ end
69
+
70
+ def execute sql
71
+ st = Time.now
72
+ result = @conn.execute sql
73
+ elapsed = Time.now - st
74
+
75
+ {
76
+ :set? => result.respond_to?(:each),
77
+ :result => result,
78
+ :elapsed => elapsed
79
+ }
80
+ end
81
+ end#RDBMS
82
+ end#JRubySQL
@@ -0,0 +1,3 @@
1
+ module JRubySQL
2
+ VERSION = "0.1.1"
3
+ end
data/lib/jrubysql.rb ADDED
@@ -0,0 +1,36 @@
1
+ require "jrubysql/version"
2
+
3
+ if RUBY_PLATFORM.match(/java/).nil?
4
+ puts 'Sorry. jrubysql only runs on JRuby.'
5
+ exit 1
6
+ end
7
+
8
+ require 'jrubysql/messages'
9
+ require 'jrubysql/config'
10
+ require 'jrubysql/constants'
11
+ require 'jrubysql/rdbms'
12
+ require 'jrubysql/option_parser'
13
+ require 'jrubysql/input/input'
14
+ require 'jrubysql/input/console'
15
+ require 'jrubysql/input/file'
16
+ require 'jrubysql/output/csv'
17
+ require 'jrubysql/output/term'
18
+ require 'jrubysql/output/cterm'
19
+ require 'jrubysql/controller'
20
+
21
+ module JRubySQL
22
+ def self.name
23
+ "JRubySQL #{JRubySQL::VERSION}"
24
+ end
25
+
26
+ def self.launch argv
27
+ if argv.empty?
28
+ JRubySQL::Controller.new(nil, nil).start
29
+ else
30
+ argv_str = argv.join(' ')
31
+ options = JRubySQL::OptionParser.parse argv
32
+ JRubySQL::Controller.new(options, argv_str).start
33
+ end
34
+ end
35
+ end
36
+
File without changes
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ ENV['BUNDLE_GEMFILE'] = File.join(File.dirname(__FILE__), '..', 'Gemfile')
4
+ Bundler.setup(:default, :development)
5
+ require 'test/unit'
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+
8
+ class TestJRubySQL < Test::Unit::TestCase
9
+ end
File without changes
File without changes
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jrubysql
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.1
6
+ platform: ruby
7
+ authors:
8
+ - Junegunn Choi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-15 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: test-unit
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ none: false
22
+ requirement: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ none: false
28
+ prerelease: false
29
+ type: :development
30
+ - !ruby/object:Gem::Dependency
31
+ name: jdbc-helper
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ~>
35
+ - !ruby/object:Gem::Version
36
+ version: 0.7.2
37
+ none: false
38
+ requirement: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ version: 0.7.2
43
+ none: false
44
+ prerelease: false
45
+ type: :runtime
46
+ - !ruby/object:Gem::Dependency
47
+ name: insensitive_hash
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ~>
51
+ - !ruby/object:Gem::Version
52
+ version: 0.2.3
53
+ none: false
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ version: 0.2.3
59
+ none: false
60
+ prerelease: false
61
+ type: :runtime
62
+ - !ruby/object:Gem::Dependency
63
+ name: tabularize
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.1
69
+ none: false
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ~>
73
+ - !ruby/object:Gem::Version
74
+ version: 0.1.1
75
+ none: false
76
+ prerelease: false
77
+ type: :runtime
78
+ - !ruby/object:Gem::Dependency
79
+ name: each_sql
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ~>
83
+ - !ruby/object:Gem::Version
84
+ version: 0.3.1
85
+ none: false
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: 0.3.1
91
+ none: false
92
+ prerelease: false
93
+ type: :runtime
94
+ - !ruby/object:Gem::Dependency
95
+ name: highline
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ~>
99
+ - !ruby/object:Gem::Version
100
+ version: 1.6.11
101
+ none: false
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ~>
105
+ - !ruby/object:Gem::Version
106
+ version: 1.6.11
107
+ none: false
108
+ prerelease: false
109
+ type: :runtime
110
+ - !ruby/object:Gem::Dependency
111
+ name: ansi
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ~>
115
+ - !ruby/object:Gem::Version
116
+ version: 1.4.2
117
+ none: false
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ~>
121
+ - !ruby/object:Gem::Version
122
+ version: 1.4.2
123
+ none: false
124
+ prerelease: false
125
+ type: :runtime
126
+ - !ruby/object:Gem::Dependency
127
+ name: erubis
128
+ version_requirements: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ~>
131
+ - !ruby/object:Gem::Version
132
+ version: 2.7.0
133
+ none: false
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ~>
137
+ - !ruby/object:Gem::Version
138
+ version: 2.7.0
139
+ none: false
140
+ prerelease: false
141
+ type: :runtime
142
+ description: An SQL client for any JDBC-compliant database. Written in JRuby.
143
+ email:
144
+ - junegunn.c@gmail.com
145
+ executables:
146
+ - jrubysql
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - .gitignore
151
+ - Gemfile
152
+ - LICENSE.txt
153
+ - README.md
154
+ - Rakefile
155
+ - bin/jrubysql
156
+ - jrubysql.gemspec
157
+ - lib/jrubysql.rb
158
+ - lib/jrubysql/config.rb
159
+ - lib/jrubysql/constants.rb
160
+ - lib/jrubysql/controller.rb
161
+ - lib/jrubysql/doc/help.txt.erb
162
+ - lib/jrubysql/input/console.rb
163
+ - lib/jrubysql/input/file.rb
164
+ - lib/jrubysql/input/input.rb
165
+ - lib/jrubysql/messages.rb
166
+ - lib/jrubysql/messages.yml
167
+ - lib/jrubysql/option_parser.rb
168
+ - lib/jrubysql/output/csv.rb
169
+ - lib/jrubysql/output/cterm.rb
170
+ - lib/jrubysql/output/term.rb
171
+ - lib/jrubysql/rdbms.rb
172
+ - lib/jrubysql/version.rb
173
+ - test/test_cterm.rb
174
+ - test/test_jrubysql.rb
175
+ - test/test_option_parser.rb
176
+ - test/test_term_output.rb
177
+ homepage: https://github.com/junegunn/jrubysql
178
+ licenses: []
179
+ post_install_message:
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ! '>='
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ none: false
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ! '>='
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ none: false
195
+ requirements: []
196
+ rubyforge_project: jrubysql
197
+ rubygems_version: 1.8.18
198
+ signing_key:
199
+ specification_version: 3
200
+ summary: An SQL client for any JDBC-compliant database.
201
+ test_files:
202
+ - test/test_cterm.rb
203
+ - test/test_jrubysql.rb
204
+ - test/test_option_parser.rb
205
+ - test/test_term_output.rb
206
+ ...