dbbrowser 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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