activerecord-utils 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +2 -0
- data/Rakefile +4 -3
- data/lib/activerecord/utils.rb +7 -8
- data/lib/activerecord/utils/alias.rb +1 -0
- data/lib/activerecord/utils/browser.rb +282 -0
- data/lib/activerecord/utils/comp3.rb +1 -1
- data/lib/activerecord/utils/random.rb +1 -1
- data/lib/activerecord/utils/version.rb +4 -5
- data/test/test_browser.rb +36 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0170989d89e7f272711aa9fb84b0deec2b1c51f3
|
4
|
+
data.tar.gz: d0b7a914857f0a9a8f98a73b8cc93069188fde30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5589fde05b548766683fbe697cef2ae9850a771aa79d6ea6745e8a29f489edbbab059ef0d09952cd05c0a230c4c3a44a9ace9d4f7422a5f94eb2ffd2e218ad36
|
7
|
+
data.tar.gz: f988280ea2755298f2372db81478307754b4a873df33e2ce9e6a4519808ef2eaca24c333014b1d79d819b8817a7ee2b427e300e1dd310d8c7f3b1f983531fb8f
|
data/Manifest.txt
CHANGED
@@ -4,10 +4,12 @@ README.md
|
|
4
4
|
Rakefile
|
5
5
|
lib/activerecord/utils.rb
|
6
6
|
lib/activerecord/utils/alias.rb
|
7
|
+
lib/activerecord/utils/browser.rb
|
7
8
|
lib/activerecord/utils/comp3.rb
|
8
9
|
lib/activerecord/utils/random.rb
|
9
10
|
lib/activerecord/utils/version.rb
|
10
11
|
test/helper.rb
|
12
|
+
test/test_browser.rb
|
11
13
|
test/test_finders.rb
|
12
14
|
test/test_random.rb
|
13
15
|
test/test_readers.rb
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require './lib/activerecord/utils/version.rb'
|
|
3
3
|
|
4
4
|
Hoe.spec 'activerecord-utils' do
|
5
5
|
|
6
|
-
self.version =
|
6
|
+
self.version = ActiveRecordUtils::VERSION
|
7
7
|
|
8
8
|
self.summary = 'activerecord-utils - utilities (e.g. random, alias_attr, etc.) for activerecord'
|
9
9
|
self.description = summary
|
@@ -18,7 +18,8 @@ Hoe.spec 'activerecord-utils' do
|
|
18
18
|
self.history_file = 'History.md'
|
19
19
|
|
20
20
|
self.extra_deps = [
|
21
|
-
['logutils']
|
21
|
+
['logutils'],
|
22
|
+
['activerecord']
|
22
23
|
]
|
23
24
|
|
24
25
|
self.licenses = ['Public Domain']
|
@@ -28,4 +29,4 @@ Hoe.spec 'activerecord-utils' do
|
|
28
29
|
}
|
29
30
|
|
30
31
|
|
31
|
-
end
|
32
|
+
end
|
data/lib/activerecord/utils.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
|
2
|
-
require 'activerecord/utils/version' # let version always go first
|
3
3
|
|
4
|
+
require 'activerecord/utils/version' # let version always go first
|
4
5
|
|
5
|
-
module ActiveRecord
|
6
6
|
|
7
|
-
module
|
7
|
+
module ActiveRecordUtils
|
8
8
|
|
9
9
|
def self.banner
|
10
10
|
## add ar PRE too? how?
|
@@ -16,9 +16,7 @@ module Utils
|
|
16
16
|
"#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}"
|
17
17
|
end
|
18
18
|
|
19
|
-
end # module
|
20
|
-
|
21
|
-
end # module ActiveRecord
|
19
|
+
end # module ActiveRecordUtils
|
22
20
|
|
23
21
|
|
24
22
|
########################
|
@@ -31,9 +29,10 @@ end
|
|
31
29
|
|
32
30
|
require 'activerecord/utils/alias'
|
33
31
|
require 'activerecord/utils/random'
|
32
|
+
require 'activerecord/utils/browser'
|
34
33
|
|
35
34
|
|
36
|
-
puts
|
35
|
+
puts ActiveRecordUtils.banner # say hello
|
37
36
|
|
38
|
-
### puts "root: >#{
|
37
|
+
### puts "root: >#{ActiveRecordUtils.root}<" # check root path (remove later)
|
39
38
|
|
@@ -0,0 +1,282 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module ActiveRecordUtils
|
4
|
+
|
5
|
+
####
|
6
|
+
# simple (generic) database browser - no models required
|
7
|
+
|
8
|
+
|
9
|
+
class Browser # also (formerly) known as connection manager
|
10
|
+
|
11
|
+
# get connection names
|
12
|
+
# def connection_names
|
13
|
+
# ActiveRecord::Base.configurations.keys
|
14
|
+
# end
|
15
|
+
|
16
|
+
CONNECTS = {} # cache connections
|
17
|
+
|
18
|
+
def connection_for( key )
|
19
|
+
# cache connections - needed? why? why not??
|
20
|
+
|
21
|
+
# hack: for now only use cached connection if still active
|
22
|
+
# if not; get a new one to avoid connection closed errors in rails
|
23
|
+
con = CONNECTS[ key ]
|
24
|
+
if con
|
25
|
+
puts "[Browser] cached connection found; con.active? #{con.active?}"
|
26
|
+
unless con.active?
|
27
|
+
puts "[Browser] *** reset cached connection (reason: connection stale/closed/not active)"
|
28
|
+
con = CONNECTS[ key ] = nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if con.nil?
|
33
|
+
con = CONNECTS[ key ] = AbstractModel.connection_for( key )
|
34
|
+
end
|
35
|
+
|
36
|
+
# note: make sure connection is active?
|
37
|
+
# use verify! - will try active? followed by reconnect!
|
38
|
+
# - todo: check ourselves if active? - why? why not??
|
39
|
+
# -- not working w/ rails - after verify! still getting error w/ closed connection
|
40
|
+
# -- con.verify!
|
41
|
+
|
42
|
+
# wrap ActiveRecord connection in our own connection class
|
43
|
+
Connection.new( con, key )
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
class AbstractModel < ActiveRecord::Base
|
48
|
+
self.abstract_class = true # no table; class just used for getting db connection
|
49
|
+
|
50
|
+
def self.connection_for( key )
|
51
|
+
establish_connection( key )
|
52
|
+
connection
|
53
|
+
end
|
54
|
+
|
55
|
+
end # class AbstractModel
|
56
|
+
|
57
|
+
|
58
|
+
class Connection
|
59
|
+
|
60
|
+
def initialize( connection, key )
|
61
|
+
@connection = connection
|
62
|
+
@key = key
|
63
|
+
end
|
64
|
+
|
65
|
+
attr_reader :connection
|
66
|
+
attr_reader :key
|
67
|
+
|
68
|
+
delegate :select_value, :select_all, :adapter_name,
|
69
|
+
:to => :connection
|
70
|
+
|
71
|
+
def class_name
|
72
|
+
@connection.class.name
|
73
|
+
end
|
74
|
+
|
75
|
+
# delegate :quote_table_name, :quote_column_name, :quote,
|
76
|
+
# :update, :insert, :delete,
|
77
|
+
# :add_limit_offset!,
|
78
|
+
# :to => :connection
|
79
|
+
|
80
|
+
def tables
|
81
|
+
@tables ||= fetch_table_defs
|
82
|
+
end
|
83
|
+
|
84
|
+
def table( name )
|
85
|
+
tables.find { |t| t.name.downcase == name.downcase }
|
86
|
+
end
|
87
|
+
|
88
|
+
# getting list of column definitions
|
89
|
+
# and order them to be more human readable
|
90
|
+
def table_columns( name )
|
91
|
+
cols = fetch_table_column_defs( name )
|
92
|
+
### fix/to be done
|
93
|
+
# cols.sort_by{|col|
|
94
|
+
# [
|
95
|
+
# fields_to_head.index(col.name) || 1e6,
|
96
|
+
# -(fields_to_tail.index(col.name) || 1e6),
|
97
|
+
# col.name
|
98
|
+
# ]
|
99
|
+
# }
|
100
|
+
cols
|
101
|
+
end
|
102
|
+
|
103
|
+
def fetch_table_defs
|
104
|
+
@connection.tables.sort.map do |name|
|
105
|
+
Table.new( self, name )
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def fetch_table_column_defs( name )
|
110
|
+
### fix/todo: add reference to table_def
|
111
|
+
@connection.columns( name ).map do |col|
|
112
|
+
Column.new( col.name, col.sql_type, col.default, col.null )
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def fetch_table_select_all( name, opts={} )
|
118
|
+
limit = (opts[:limit] || 33).to_i # 33 records limit/per page (for now default)
|
119
|
+
limit = 33 if limit == 0 # use default page size if limit 0 (from not a number para)
|
120
|
+
|
121
|
+
offset = (opts[:offset] || 0).to_i
|
122
|
+
|
123
|
+
sql = "select * from #{name} limit #{limit}"
|
124
|
+
|
125
|
+
sql << " offset #{offset}" if offset > 0 # add offset if present (e.g greater zero)
|
126
|
+
|
127
|
+
# page = (opts[:page] || 1 ).try(:to_i)
|
128
|
+
# fields = opts[:fields] || nil
|
129
|
+
|
130
|
+
# rez = { :fields => fields }
|
131
|
+
# if sql =~ /\s*select/i && per_page > 0
|
132
|
+
# rez[:count] = select_value("select count(*) from (#{sql}) as t").to_i
|
133
|
+
# rez[:pages] = (rez[:count].to_f / per_page).ceil
|
134
|
+
# sql = "select * from (#{sql}) as t"
|
135
|
+
# add_limit_offset!( sql,
|
136
|
+
# :limit => per_page,
|
137
|
+
# :offset => per_page * (page - 1))
|
138
|
+
# end
|
139
|
+
|
140
|
+
result = {}
|
141
|
+
result[ :sql ] = sql # note: lets also always add sql query to result too
|
142
|
+
result[ :rows ] = select_all( sql )
|
143
|
+
|
144
|
+
# unless rez[:rows].blank?
|
145
|
+
# rez[:fields] ||= []
|
146
|
+
# rez[:fields].concat( self.sort_fields(rez[:rows].first.keys) - rez[:fields] )
|
147
|
+
# end
|
148
|
+
|
149
|
+
Result.new( result )
|
150
|
+
rescue StandardError => ex
|
151
|
+
Result.new( error: ex )
|
152
|
+
end # fetch_table
|
153
|
+
|
154
|
+
|
155
|
+
=begin
|
156
|
+
def column_names(table)
|
157
|
+
columns(table).map{|c| c.name}
|
158
|
+
end
|
159
|
+
|
160
|
+
# fields to see first
|
161
|
+
def fields_to_head
|
162
|
+
@fields_to_head ||= %w{id name login value}
|
163
|
+
end
|
164
|
+
|
165
|
+
# fields to see last
|
166
|
+
def fields_to_tail
|
167
|
+
@fields_to_tail ||= %w{created_at created_on updated_at updated_on}
|
168
|
+
end
|
169
|
+
|
170
|
+
attr_writer :fields_to_head, :fields_to_tail
|
171
|
+
|
172
|
+
# sort field names in a rezult
|
173
|
+
def sort_fields(fields)
|
174
|
+
fields = (fields_to_head & fields) | (fields - fields_to_head)
|
175
|
+
fields = (fields - fields_to_tail) | (fields_to_tail & fields)
|
176
|
+
fields
|
177
|
+
end
|
178
|
+
|
179
|
+
# performs query with appropriate method
|
180
|
+
def query(sql, opts={})
|
181
|
+
per_page = (opts[:perpage] || nil).to_i
|
182
|
+
page = (opts[:page] || 1 ).try(:to_i)
|
183
|
+
fields = opts[:fields] || nil
|
184
|
+
case sql
|
185
|
+
when /\s*select/i , /\s*(update|insert|delete).+returning/im
|
186
|
+
rez = {:fields => fields}
|
187
|
+
if sql =~ /\s*select/i && per_page > 0
|
188
|
+
rez[:count] = select_value("select count(*) from (#{sql}) as t").to_i
|
189
|
+
rez[:pages] = (rez[:count].to_f / per_page).ceil
|
190
|
+
sql = "select * from (#{sql}) as t"
|
191
|
+
add_limit_offset!( sql,
|
192
|
+
:limit => per_page,
|
193
|
+
:offset => per_page * (page - 1))
|
194
|
+
end
|
195
|
+
|
196
|
+
rez[:rows] = select_all( sql )
|
197
|
+
|
198
|
+
unless rez[:rows].blank?
|
199
|
+
rez[:fields] ||= []
|
200
|
+
rez[:fields].concat( self.sort_fields(rez[:rows].first.keys) - rez[:fields] )
|
201
|
+
end
|
202
|
+
|
203
|
+
Result.new(rez)
|
204
|
+
when /\s*update/i
|
205
|
+
Result.new :value => update( sql )
|
206
|
+
when /\s*insert/i
|
207
|
+
Result.new :value => insert( sql )
|
208
|
+
when /\s*delete/i
|
209
|
+
Result.new :value => delete( sql )
|
210
|
+
end
|
211
|
+
rescue StandardError => e
|
212
|
+
Result.new :error => e
|
213
|
+
end
|
214
|
+
|
215
|
+
=end
|
216
|
+
|
217
|
+
end # class Connection
|
218
|
+
|
219
|
+
|
220
|
+
class Table
|
221
|
+
|
222
|
+
def initialize(connection, name)
|
223
|
+
@connection = connection
|
224
|
+
@name = name
|
225
|
+
end
|
226
|
+
|
227
|
+
attr_reader :connection
|
228
|
+
attr_reader :name
|
229
|
+
|
230
|
+
def count
|
231
|
+
@connection.select_value( "select count(*) from #{name}").to_i
|
232
|
+
end
|
233
|
+
|
234
|
+
def columns
|
235
|
+
# load columns on demand for now (cache on first lookup)
|
236
|
+
@columns ||= @connection.table_columns( @name )
|
237
|
+
end
|
238
|
+
|
239
|
+
def query( opts={})
|
240
|
+
@connection.fetch_table_select_all( @name, opts )
|
241
|
+
end
|
242
|
+
|
243
|
+
end # class Table
|
244
|
+
|
245
|
+
|
246
|
+
class Column
|
247
|
+
def initialize(name, type, default, null)
|
248
|
+
@name = name
|
249
|
+
@type = type # note: is sql_type
|
250
|
+
@default = default
|
251
|
+
@null = null # note: true|false depending if NOT NULL or not
|
252
|
+
end
|
253
|
+
|
254
|
+
attr_reader :name, :type, :default, :null
|
255
|
+
end # class Column
|
256
|
+
|
257
|
+
|
258
|
+
class Result
|
259
|
+
def initialize( opts={} )
|
260
|
+
@sql = opts[:sql] # sql statement as a string
|
261
|
+
|
262
|
+
if opts[:error]
|
263
|
+
@error = opts[:error]
|
264
|
+
else
|
265
|
+
@rows = opts[:rows]
|
266
|
+
# @count = opts[:count] || @rows.size
|
267
|
+
# @pages = opts[:pages] || 1
|
268
|
+
# @fields = opts[:fields]
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
attr_reader :sql, :rows, :error ### to be done :count, :pages, :fields,
|
273
|
+
|
274
|
+
def error?() @error.present?; end
|
275
|
+
def rows?() @rows != nil; end
|
276
|
+
end # class Result
|
277
|
+
|
278
|
+
|
279
|
+
end # class Browser
|
280
|
+
|
281
|
+
end # module ActiveRecordUtils
|
282
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_browser.rb
|
6
|
+
# or better
|
7
|
+
# rake test
|
8
|
+
|
9
|
+
|
10
|
+
require 'helper'
|
11
|
+
|
12
|
+
class TestBrowser < MiniTest::Test
|
13
|
+
|
14
|
+
DB_CONFIG = {
|
15
|
+
adapter: 'sqlite3',
|
16
|
+
database: ':memory:'
|
17
|
+
}
|
18
|
+
|
19
|
+
def test_connect
|
20
|
+
|
21
|
+
browser = ActiveRecordUtils::Browser.new
|
22
|
+
|
23
|
+
## Note: every connect will create a new empty in memory db
|
24
|
+
## check - is it possible to reconnect to in memory db??
|
25
|
+
## use (try) key instead of connection-spec ??
|
26
|
+
|
27
|
+
con = browser.connection_for( DB_CONFIG )
|
28
|
+
tables = con.tables
|
29
|
+
|
30
|
+
pp tables
|
31
|
+
|
32
|
+
assert true # for now if get here; assume it's working so far
|
33
|
+
end
|
34
|
+
|
35
|
+
end # class TestBrowser
|
36
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-utils
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: logutils
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rdoc
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,10 +82,12 @@ files:
|
|
68
82
|
- Rakefile
|
69
83
|
- lib/activerecord/utils.rb
|
70
84
|
- lib/activerecord/utils/alias.rb
|
85
|
+
- lib/activerecord/utils/browser.rb
|
71
86
|
- lib/activerecord/utils/comp3.rb
|
72
87
|
- lib/activerecord/utils/random.rb
|
73
88
|
- lib/activerecord/utils/version.rb
|
74
89
|
- test/helper.rb
|
90
|
+
- test/test_browser.rb
|
75
91
|
- test/test_finders.rb
|
76
92
|
- test/test_random.rb
|
77
93
|
- test/test_readers.rb
|
@@ -102,6 +118,7 @@ signing_key:
|
|
102
118
|
specification_version: 4
|
103
119
|
summary: activerecord-utils - utilities (e.g. random, alias_attr, etc.) for activerecord
|
104
120
|
test_files:
|
121
|
+
- test/test_browser.rb
|
105
122
|
- test/test_readers.rb
|
106
123
|
- test/test_finders.rb
|
107
124
|
- test/test_random.rb
|