dbbrowser 0.0.1 → 0.1.0

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.
@@ -3,4 +3,16 @@ Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
5
  lib/dbbrowser.rb
6
+ lib/dbbrowser/connection.rb
7
+ lib/dbbrowser/public/style.css
8
+ lib/dbbrowser/server.rb
6
9
  lib/dbbrowser/version.rb
10
+ lib/dbbrowser/views/db.erb
11
+ lib/dbbrowser/views/debug.erb
12
+ lib/dbbrowser/views/index.erb
13
+ lib/dbbrowser/views/layout.erb
14
+ lib/dbbrowser/views/shared/_debug.erb
15
+ lib/dbbrowser/views/shared/_table_def.erb
16
+ lib/dbbrowser/views/shared/_tables.erb
17
+ lib/dbbrowser/views/shared/_version.erb
18
+ lib/dbbrowser/views/table.erb
data/README.md CHANGED
@@ -13,7 +13,25 @@ TBD
13
13
 
14
14
  ## Alternatives
15
15
 
16
- TBD
16
+ ### Browser
17
+
18
+ [db_explorer](https://github.com/robinbortlik/db_explorer) by Robin Bortlík; Rails web app
19
+
20
+ [dbadmin gem](https://rubygems.org/gems/dbadmin) [(Source)](https://github.com/pjb3/dbadmin) by Paul Barry; last update October 2012
21
+
22
+ [rails_db_browser gem](https://rubygems.org/gems/rails_db_browser) [(Source)](https://github.com/funny-falcon/rails_db_browser) by Sokolov Yura; last update March 2011
23
+
24
+ [rails_db_admin gem](https://rubygems.org/gems/rails_db_admin) [(Source)](https://github.com/portablemind/compass_agile_enterprise) by Rick Koloski, Russell Holmes; last update June 2013
25
+
26
+ [databrowser gem](https://rubygems.org/gems/databrowser) by Carlos Junior; last update August 2008
27
+
28
+
29
+
30
+ ### Admin / ActiveScaffold
31
+
32
+ - ActiveAdmin
33
+ - RailsAdmin
34
+
17
35
 
18
36
  ## License
19
37
 
@@ -1,22 +1,23 @@
1
1
 
2
+ # 3rd party gems/libs
2
3
 
3
- require 'dbbrowser/version' # let it always go first
4
+ require 'textutils'
4
5
 
6
+ require 'active_record'
5
7
 
6
- module DbBrowser
7
8
 
8
- def self.banner
9
- "dbbrowser #{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
10
- end
9
+ # our own code
10
+
11
+ require 'dbbrowser/version' # let it always go first
12
+ require 'dbbrowser/connection'
13
+
14
+ module DbBrowser
11
15
 
12
- =begin
13
16
  def self.root
14
17
  "#{File.expand_path( File.dirname(File.dirname(__FILE__)) )}"
15
18
  end
16
- =end
17
19
 
18
20
  end # module DbBrowser
19
21
 
20
22
 
21
- puts DbBrowser.banner # say hello
22
-
23
+ require 'dbbrowser/server'
@@ -0,0 +1,246 @@
1
+ module DbBrowser
2
+
3
+ class ConnectionMan # connection manager
4
+
5
+ # get connection names
6
+ # def connection_names
7
+ # ActiveRecord::Base.configurations.keys
8
+ # end
9
+
10
+ def connection( key='std' )
11
+ if key == 'std'
12
+ # -- use/try 'standard/default' connection
13
+ con = ActiveRecord::Base.connection
14
+ else
15
+ con = AbstractModel.connection_for( key )
16
+ end
17
+ # wrap ActiveRecord connection in our own connection class
18
+ Connection.new( con, key )
19
+ end
20
+
21
+
22
+ class AbstractModel < ActiveRecord::Base
23
+ self.abstract_class = true # no table; class just used for getting db connection
24
+
25
+ CONNECTS = {} # cache connections
26
+ def self.connection_for( key )
27
+ CONNECTS[ key ] ||= begin
28
+ establish_connection( key )
29
+ connection
30
+ end
31
+ end
32
+ end # class AbstractModel
33
+
34
+
35
+ class Connection
36
+
37
+ def initialize( connection, key )
38
+ @connection = connection
39
+ @key = key
40
+ end
41
+
42
+ attr_reader :connection
43
+ attr_reader :key
44
+
45
+ delegate :select_value, :select_all,
46
+ :to => :connection
47
+
48
+ # delegate :quote_table_name, :quote_column_name, :quote,
49
+ # :update, :insert, :delete,
50
+ # :add_limit_offset!,
51
+ # :to => :connection
52
+
53
+ def tables
54
+ @tables ||= fetch_table_defs
55
+ end
56
+
57
+ def table( name )
58
+ tables.find { |t| t.name.downcase == name.downcase }
59
+ end
60
+
61
+ # getting list of column definitions
62
+ # and order them to be more human readable
63
+ def table_columns( name )
64
+ cols = fetch_table_column_defs( name )
65
+ ### fix/to be done
66
+ # cols.sort_by{|col|
67
+ # [
68
+ # fields_to_head.index(col.name) || 1e6,
69
+ # -(fields_to_tail.index(col.name) || 1e6),
70
+ # col.name
71
+ # ]
72
+ # }
73
+ cols
74
+ end
75
+
76
+ def fetch_table_defs
77
+ @connection.tables.sort.map do |name|
78
+ Table.new( self, name )
79
+ end
80
+ end
81
+
82
+ def fetch_table_column_defs( name )
83
+ ### fix/todo: add reference to table_def
84
+ @connection.columns( name ).map do |col|
85
+ Column.new( col.name, col.sql_type, col.default, col.null )
86
+ end
87
+ end
88
+
89
+
90
+ def fetch_table_select_all( name, opts={} )
91
+ per_page = (opts[:perpage] || 33).to_i # 33 records per page (for now default)
92
+
93
+ sql = "select * from #{name} limit #{per_page}"
94
+
95
+ # page = (opts[:page] || 1 ).try(:to_i)
96
+ # fields = opts[:fields] || nil
97
+
98
+ # rez = { :fields => fields }
99
+ # if sql =~ /\s*select/i && per_page > 0
100
+ # rez[:count] = select_value("select count(*) from (#{sql}) as t").to_i
101
+ # rez[:pages] = (rez[:count].to_f / per_page).ceil
102
+ # sql = "select * from (#{sql}) as t"
103
+ # add_limit_offset!( sql,
104
+ # :limit => per_page,
105
+ # :offset => per_page * (page - 1))
106
+ # end
107
+
108
+ result = {}
109
+ result[ :rows ] = select_all( sql )
110
+
111
+ # unless rez[:rows].blank?
112
+ # rez[:fields] ||= []
113
+ # rez[:fields].concat( self.sort_fields(rez[:rows].first.keys) - rez[:fields] )
114
+ # end
115
+
116
+ Result.new( result )
117
+ rescue StandardError => ex
118
+ Result.new( error: ex )
119
+ end # fetch_table
120
+
121
+
122
+ =begin
123
+ def column_names(table)
124
+ columns(table).map{|c| c.name}
125
+ end
126
+
127
+ # fields to see first
128
+ def fields_to_head
129
+ @fields_to_head ||= %w{id name login value}
130
+ end
131
+
132
+ # fields to see last
133
+ def fields_to_tail
134
+ @fields_to_tail ||= %w{created_at created_on updated_at updated_on}
135
+ end
136
+
137
+ attr_writer :fields_to_head, :fields_to_tail
138
+
139
+ # sort field names in a rezult
140
+ def sort_fields(fields)
141
+ fields = (fields_to_head & fields) | (fields - fields_to_head)
142
+ fields = (fields - fields_to_tail) | (fields_to_tail & fields)
143
+ fields
144
+ end
145
+
146
+ # performs query with appropriate method
147
+ def query(sql, opts={})
148
+ per_page = (opts[:perpage] || nil).to_i
149
+ page = (opts[:page] || 1 ).try(:to_i)
150
+ fields = opts[:fields] || nil
151
+ case sql
152
+ when /\s*select/i , /\s*(update|insert|delete).+returning/im
153
+ rez = {:fields => fields}
154
+ if sql =~ /\s*select/i && per_page > 0
155
+ rez[:count] = select_value("select count(*) from (#{sql}) as t").to_i
156
+ rez[:pages] = (rez[:count].to_f / per_page).ceil
157
+ sql = "select * from (#{sql}) as t"
158
+ add_limit_offset!( sql,
159
+ :limit => per_page,
160
+ :offset => per_page * (page - 1))
161
+ end
162
+
163
+ rez[:rows] = select_all( sql )
164
+
165
+ unless rez[:rows].blank?
166
+ rez[:fields] ||= []
167
+ rez[:fields].concat( self.sort_fields(rez[:rows].first.keys) - rez[:fields] )
168
+ end
169
+
170
+ Result.new(rez)
171
+ when /\s*update/i
172
+ Result.new :value => update( sql )
173
+ when /\s*insert/i
174
+ Result.new :value => insert( sql )
175
+ when /\s*delete/i
176
+ Result.new :value => delete( sql )
177
+ end
178
+ rescue StandardError => e
179
+ Result.new :error => e
180
+ end
181
+
182
+ =end
183
+
184
+ end # class Connection
185
+
186
+
187
+ class Table
188
+
189
+ def initialize(connection, name)
190
+ @connection = connection
191
+ @name = name
192
+ end
193
+
194
+ attr_reader :connection
195
+ attr_reader :name
196
+
197
+ def count
198
+ @connection.select_value( "select count(*) from #{name}").to_i
199
+ end
200
+
201
+ def columns
202
+ # load columns on demand for now (cache on first lookup)
203
+ @columns ||= @connection.table_columns( @name )
204
+ end
205
+
206
+ def query( opts={})
207
+ @connection.fetch_table_select_all( @name, opts )
208
+ end
209
+
210
+ end # class Table
211
+
212
+
213
+ class Column
214
+ def initialize(name, type, default, null)
215
+ @name = name
216
+ @type = type # note: is sql_type
217
+ @default = default
218
+ @null = null # note: true|false depending if NOT NULL or not
219
+ end
220
+
221
+ attr_reader :name, :type, :default, :null
222
+ end # class Column
223
+
224
+
225
+ class Result
226
+ def initialize( opts={} )
227
+ if opts[:error]
228
+ @error = opts[:error]
229
+ else
230
+ @rows = opts[:rows]
231
+ # @count = opts[:count] || @rows.size
232
+ # @pages = opts[:pages] || 1
233
+ # @fields = opts[:fields]
234
+ end
235
+ end
236
+
237
+ attr_reader :rows, :error ### to be done :count, :pages, :fields,
238
+
239
+ def error?() @error.present?; end
240
+ def rows?() @rows != nil; end
241
+ end # class Result
242
+
243
+
244
+ end # class ConnectionMan
245
+
246
+ end # module DbBrowser
@@ -0,0 +1,53 @@
1
+ /* fix: use sass w/ variables */
2
+
3
+ body {
4
+ font-family: sans-serif;
5
+ background-color: #F4F4F4;
6
+ color: #333333;
7
+ }
8
+
9
+ a, a:visited {
10
+ text-decoration: none;
11
+ color: maroon;
12
+ }
13
+
14
+ a:hover {
15
+ text-decoration: underline;
16
+ color: #1E68A6;
17
+ background-color: yellow;
18
+ }
19
+
20
+ a.nav ,
21
+ a.visited.nav ,
22
+ a:hover.nav {
23
+ text-decoration: none;
24
+ color: #333333;
25
+ background-color: #F4F4F4;
26
+ }
27
+
28
+
29
+ .count,
30
+ .key {
31
+ font-size: 12px;
32
+ color: grey;
33
+ }
34
+
35
+ /* fix: use .small instead */
36
+ .smaller {
37
+ font-size: 12px;
38
+ /* color: #666666; */ /* lighter gray than text gray */
39
+ }
40
+
41
+
42
+
43
+ /** version block **********/
44
+
45
+ .version {
46
+ text-align: center;
47
+ margin-top: 10px;
48
+ color: grey; }
49
+ .version a, .version span {
50
+ font-size: 12px;
51
+ color: grey; }
52
+
53
+ /***************************/
@@ -0,0 +1,103 @@
1
+
2
+
3
+ # 3rd party libs/gems
4
+
5
+ require 'sinatra/base'
6
+
7
+
8
+ module DbBrowser
9
+
10
+ class Server < Sinatra::Base
11
+
12
+ def self.banner
13
+ "dbbrowser #{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] on Sinatra/#{Sinatra::VERSION} (#{ENV['RACK_ENV']})"
14
+ end
15
+
16
+ PUBLIC_FOLDER = "#{DbBrowser.root}/lib/dbbrowser/public"
17
+ VIEWS_FOLDER = "#{DbBrowser.root}/lib/dbbrowser/views"
18
+
19
+ puts "[boot] dbbrowser - setting public folder to: #{PUBLIC_FOLDER}"
20
+ puts "[boot] dbbrowser - setting views folder to: #{VIEWS_FOLDER}"
21
+
22
+ set :public_folder, PUBLIC_FOLDER # set up the static dir (with images/js/css inside)
23
+ set :views, VIEWS_FOLDER # set up the views dir
24
+
25
+ set :static, true # set up static file routing
26
+
27
+
28
+ set :man, ConnectionMan.new
29
+
30
+ #################
31
+ # Helpers
32
+
33
+ include TextUtils::HypertextHelper # e.g. lets us use link_to, sanitize, etc.
34
+
35
+ def path_prefix
36
+ request.script_name # request.env['SCRIPT_NAME']
37
+ end
38
+
39
+ def db_path( key )
40
+ "#{path_prefix}/db/#{key}"
41
+ end
42
+
43
+ def table_path( table )
44
+ "#{path_prefix}/db/#{table.connection.key}/#{table.name.downcase}"
45
+ end
46
+
47
+ def root_path
48
+ "#{path_prefix}/"
49
+ end
50
+
51
+ def h( text )
52
+ Rack::Utils.escape_html(text)
53
+ end
54
+
55
+
56
+ def render_table_def( table, opts={} )
57
+ erb( 'shared/_table_def'.to_sym,
58
+ layout: false,
59
+ locals: { table: table } )
60
+ end
61
+
62
+ def render_tables( tables, opts={} )
63
+ erb( 'shared/_tables'.to_sym,
64
+ layout: false,
65
+ locals: { tables: tables } )
66
+ end
67
+
68
+ def render_tables_for( key, opts={} )
69
+ con = settings.man.connection( key )
70
+ erb( 'shared/_tables'.to_sym,
71
+ layout: false,
72
+ locals: { tables: con.tables } )
73
+ end
74
+
75
+
76
+ ##############################################
77
+ # Controllers / Routing / Request Handlers
78
+
79
+ get '/' do
80
+ erb :index
81
+ end
82
+
83
+ get '/db/:key/:table_name' do |key,table_name|
84
+ con = settings.man.connection( key )
85
+ erb :table, locals: { table: con.table( table_name ) }
86
+ end
87
+
88
+ get '/db/:key' do |key|
89
+ con = settings.man.connection( key )
90
+ erb :db, locals: { key: key, tables: con.tables }
91
+ end
92
+
93
+ get '/d*' do
94
+ erb :debug
95
+ end
96
+
97
+
98
+ end # class Server
99
+ end # module DbBrowser
100
+
101
+
102
+ # say hello
103
+ puts DbBrowser::Server.banner
@@ -1,4 +1,4 @@
1
1
 
2
2
  module DbBrowser
3
- VERSION = '0.0.1'
3
+ VERSION = '0.1.0'
4
4
  end
@@ -0,0 +1,16 @@
1
+
2
+ <h2>## <%= key %> ##</h2>
3
+
4
+ <pre>
5
+ <%= ActiveRecord::Base.configurations[key].inspect %>
6
+ </pre>
7
+
8
+
9
+ <%= render_tables( tables ) %>
10
+
11
+
12
+ <% tables.each do |table| %>
13
+ <h4>### <%= table.name %> ###</h4>
14
+
15
+ <%= render_table_def( table ) %>
16
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <h3>Named Route Helpers</h3>
2
+
3
+ <pre>
4
+ root_path: <%= root_path %>
5
+ </pre>
6
+
7
+ <%= erb :'shared/_debug' %>
@@ -0,0 +1,11 @@
1
+
2
+ <% ActiveRecord::Base.configurations.keys.each do |key| %>
3
+
4
+ <h2>## <%= key %> ##</h2>
5
+ <pre>
6
+ <%= ActiveRecord::Base.configurations[key].inspect %>
7
+ </pre>
8
+
9
+ <%= render_tables_for( key ) %>
10
+
11
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset='utf-8'>
5
+ <title>DB Browser</title>
6
+ <link href="<%= url('/style.css') %>" rel='stylesheet'>
7
+ </head>
8
+ <body>
9
+
10
+ <p>
11
+ <%= link_to 'DB Browser', root_path(), class: 'nav' %> |
12
+ <% ActiveRecord::Base.configurations.keys.each do |key| %>
13
+ <%= link_to key, db_path( key ) %>
14
+ <% end %>
15
+ </p>
16
+
17
+ <%= yield %>
18
+
19
+ <%= erb :'shared/_version' %>
20
+
21
+ </body>
22
+ </html>
@@ -0,0 +1,16 @@
1
+ <h3>Request</h3>
2
+
3
+ <pre>
4
+ request.scheme: <%= request.scheme %>
5
+ request.script_name: <%= request.script_name %>
6
+ request.path_info: <%= request.path_info %>
7
+ request.port: <%= request.port %>
8
+ request.request_method: <%= request.request_method %>
9
+ request.query_string: <%= request.query_string %>
10
+ request.host: <%= request.host %>
11
+ request.referrer: <%= request.referrer %>
12
+ request.user_agent: <%= request.user_agent %>
13
+ request.url: <%= request.url %>
14
+ request.path: <%= request.path %>
15
+ request.ip: <%= request.ip %>
16
+ </pre>
@@ -0,0 +1,14 @@
1
+
2
+ <table>
3
+ <% table.columns.each do |col| %>
4
+ <tr>
5
+ <td><%= col.name %></td>
6
+ <td><%= col.type %></td>
7
+ <td><%= col.default %></td>
8
+ <td><% unless col.null %>
9
+ NOT NULL
10
+ <% end %>
11
+ </td>
12
+ </tr>
13
+ <% end %>
14
+ </table>
@@ -0,0 +1,13 @@
1
+
2
+ <h3>Tables
3
+ <span class='count'>(<%= tables.size %>)</span>
4
+ </h3>
5
+
6
+ <table>
7
+ <% tables.each do |table| %>
8
+ <tr>
9
+ <td><%= link_to table.name, table_path( table ) %></td>
10
+ <td style='text-align: right;'><%= table.count %> recs</td>
11
+ </tr>
12
+ <% end %>
13
+ </table>
@@ -0,0 +1,6 @@
1
+ <div class='version'>
2
+ <a href="https://github.com/rubylibs/dbbrowser">dbbrowser/<%= DbBrowser::VERSION %></a>,
3
+ -
4
+ <span>Ruby/<%= "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}/#{RUBY_PLATFORM})" %> on</span>
5
+ <span>Sinatra/<%= Sinatra::VERSION %> (<%= ENV['RACK_ENV'] %>)</span>
6
+ </div>
@@ -0,0 +1,43 @@
1
+
2
+ <h2><%= table.name %>
3
+ <span class='count'>(<%= table.count %>)</span>
4
+ </h2>
5
+
6
+
7
+ <%= render_table_def( table ) %>
8
+
9
+
10
+ <% result = table.query() %>
11
+ <% if result.error? %>
12
+ <p>
13
+ ERROR: <%= result.error %>
14
+ </p>
15
+ <% else %>
16
+ <table>
17
+ <% result.rows.each do |row| %>
18
+ <tr>
19
+ <% row.each do |key,value| %>
20
+
21
+ <% if ['id','created_at', 'updated_at'].include?( key ) %>
22
+ <td class='key'>
23
+ <% else %>
24
+ <td>
25
+ <% end %>
26
+
27
+ <span title='<%= key %> : <%= value.class.name %>'>
28
+ <% if value.nil? %>
29
+ NULL <!-- fix/todo: use special char for null?? -->
30
+ <% else %>
31
+ <% if value.kind_of?( String ) %>
32
+ <%= h( value[0..80] ) %> <!-- cut off after 80 chars; escape html use h-helper -->
33
+ <% else %>
34
+ <%= value %>
35
+ <% end %>
36
+ <% end %>
37
+ </span>
38
+ </td>
39
+ <% end %>
40
+ </tr>
41
+ <% end %>
42
+ </table>
43
+ <% end %>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbbrowser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-10-12 00:00:00.000000000 Z
12
+ date: 2013-10-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: logutils
16
- requirement: &72495300 !ruby/object:Gem::Requirement
16
+ requirement: &69669750 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0.5'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *72495300
24
+ version_requirements: *69669750
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rdoc
27
- requirement: &72495060 !ruby/object:Gem::Requirement
27
+ requirement: &69669480 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '3.10'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *72495060
35
+ version_requirements: *69669480
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: hoe
38
- requirement: &72494820 !ruby/object:Gem::Requirement
38
+ requirement: &69669210 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '3.3'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *72494820
46
+ version_requirements: *69669210
47
47
  description: dbbrowser - database browser (connections, schema, tables, records, etc.)
48
48
  as mountable web app
49
49
  email: webslideshow@googlegroups.com
@@ -57,7 +57,19 @@ files:
57
57
  - README.md
58
58
  - Rakefile
59
59
  - lib/dbbrowser.rb
60
+ - lib/dbbrowser/connection.rb
61
+ - lib/dbbrowser/public/style.css
62
+ - lib/dbbrowser/server.rb
60
63
  - lib/dbbrowser/version.rb
64
+ - lib/dbbrowser/views/db.erb
65
+ - lib/dbbrowser/views/debug.erb
66
+ - lib/dbbrowser/views/index.erb
67
+ - lib/dbbrowser/views/layout.erb
68
+ - lib/dbbrowser/views/shared/_debug.erb
69
+ - lib/dbbrowser/views/shared/_table_def.erb
70
+ - lib/dbbrowser/views/shared/_tables.erb
71
+ - lib/dbbrowser/views/shared/_version.erb
72
+ - lib/dbbrowser/views/table.erb
61
73
  homepage: https://github.com/rubylibs/dbbrowser
62
74
  licenses:
63
75
  - Public Domain