activerecord-utils 0.3.0 → 0.4.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.
- 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
|