sql_view 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +133 -8
- data/lib/sql_view/schema_dumper.rb +13 -33
- data/lib/sql_view/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 453bfcdc70b0ca2eba4be95d6eca062e840e9e3f4950f8fca7471421216d1de9
|
4
|
+
data.tar.gz: 14d65cba7bbcc28dc6741622abef7b91a8863ed8e738b35de373c1758e21f045
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99a4e50942b3322d2a86e04b5c5b56e060718788beee10af35ca79de9331bf882d16f0311c9908d97bd8c5d88926e29fd1360fcaf30c90d5d3fd17b77813a238
|
7
|
+
data.tar.gz: d502bab08d5c44d183f6c741ed2d12c53cd5b27962c9a3eca384c66e213a0d1c5bdbce058184a13fdaf3495fca65f13f7e469a0a2fc59da861f4e8cffc18335d
|
data/README.md
CHANGED
@@ -1,11 +1,86 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# Rails + SQL View
|
2
|
+
|
3
|
+
[![Listed on OpenSource-Heroes.com](https://opensource-heroes.com/badge-v1.svg)](https://opensource-heroes.com/r/igorkasyanchuk/sql_view)
|
4
|
+
|
5
|
+
## The easist way to add and work with SQL view in your app.
|
6
|
+
|
7
|
+
If you are lazy and don't like to write SQL to create SQL view but you know AR use your skills to create views.
|
8
|
+
|
9
|
+
Production-ready.
|
10
|
+
|
11
|
+
![Demo](docs/sql_view.gif?raw=true "Demo")
|
3
12
|
|
4
13
|
## Usage
|
5
|
-
|
14
|
+
|
15
|
+
The most simple way to add a view is to call a generator (examples below):
|
16
|
+
|
17
|
+
```bash
|
18
|
+
rails g sql_view:view DeletedProjects 'Project.only_deleted'
|
19
|
+
rails g sql_view:view ActiveUsers 'User.confirmed.where(active: true)' --materialized
|
20
|
+
```
|
21
|
+
|
22
|
+
Depending on whether you need a materialized view or not add `--materialized` flag (later you can change in "view" class). Materialized views works in Postgres.
|
23
|
+
|
24
|
+
Generator will create a file similar to:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
class ActiveUserView < SQLView::Model
|
28
|
+
materialized
|
29
|
+
|
30
|
+
schema -> { User.where(age: 18..60) }
|
31
|
+
|
32
|
+
extend_model_with do
|
33
|
+
# sample how you can extend it, similar to regular AR model
|
34
|
+
#
|
35
|
+
# include SomeConcern
|
36
|
+
#
|
37
|
+
# belongs_to :user
|
38
|
+
# has_many :posts
|
39
|
+
#
|
40
|
+
# scope :ordered, -> { order(:created_at) }
|
41
|
+
# scope :by_role, ->(role) { where(role: role) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
or if you want to use SQL to create a regular view:
|
47
|
+
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
class ActiveUserView < SQLView::Model
|
51
|
+
schema -> { "SELECT * FROM users WHERE active = TRUE" }
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
or the same but materialized:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class ActiveUserView < SQLView::Model
|
59
|
+
materialized
|
60
|
+
schema -> { "SELECT * FROM users WHERE active = TRUE" }
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
Later with view you can work same way as with any model(ActiveRecord class). For example:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
ActiveUserView.model.count
|
68
|
+
# or
|
69
|
+
ActiveUserView.count
|
70
|
+
# ----
|
71
|
+
ActiveUserView.find(42)
|
72
|
+
# you can apply scopes, relations, methods, BUT add them in extend_model_with block
|
73
|
+
|
74
|
+
ActiveUserView.model.by_role("admin").count
|
75
|
+
ActiveUserView.where(role: "admin").exists?
|
76
|
+
ActiveUserView.model.includes(:profile)
|
77
|
+
```
|
78
|
+
|
79
|
+
If you need to refresh materialized view - `ActiveUserView.sql_view.refresh` (if you need to do it concerrently - `.refresh(concurrently: false)`.
|
80
|
+
|
81
|
+
More examples in this file: `./test/sql_view_test.rb`
|
6
82
|
|
7
83
|
## Installation
|
8
|
-
Add this line to your application's Gemfile:
|
9
84
|
|
10
85
|
```ruby
|
11
86
|
gem "sql_view"
|
@@ -16,17 +91,67 @@ And then execute:
|
|
16
91
|
$ bundle
|
17
92
|
```
|
18
93
|
|
19
|
-
Or
|
20
|
-
|
21
|
-
|
94
|
+
And use generator. Or you can connect it to existing view with `view_name=`:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
class OldUserView < SqlView::Model
|
98
|
+
self.view_name = "all_old_users"
|
99
|
+
|
100
|
+
materialized
|
101
|
+
|
102
|
+
schema -> { User.where("age > 18") }
|
103
|
+
|
104
|
+
extend_model_with do
|
105
|
+
scope :ordered, -> { order(:id) }
|
106
|
+
|
107
|
+
def test_instance_method
|
108
|
+
42
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
## Materialized view + concurrent update
|
115
|
+
|
116
|
+
1. add index
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
add_index SomeView.view_name, :user_id, unique: true
|
22
120
|
```
|
23
121
|
|
122
|
+
2. refresh with parameter
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
SomeView.sql_view.refresh(concurrently: true)
|
126
|
+
```
|
127
|
+
|
128
|
+
3. profit :)
|
129
|
+
|
130
|
+
## TODO
|
131
|
+
|
132
|
+
- CI with different versions of Rails/Ruby
|
133
|
+
- make unit tests works with `rake test`
|
134
|
+
- `cascade` option
|
135
|
+
- move classes to own files
|
136
|
+
- code coverage
|
137
|
+
- verify how it works with other DB's
|
138
|
+
- check if schema was changed on migrate or schema dump?
|
139
|
+
|
24
140
|
## Testing
|
25
141
|
|
26
142
|
`ruby ./test/sql_view_test.rb` (because somehow `rake test` not works, not critical for now)
|
27
143
|
|
28
144
|
## Contributing
|
29
|
-
|
145
|
+
|
146
|
+
You are welcome to contribute.
|
147
|
+
|
148
|
+
## Credits
|
149
|
+
|
150
|
+
I know about and actually using `gem scenic`, which is very nice and I tool some examples from it how to dump view into schema.rb but this gem was created to simplify life and reduce amount of time needed to write SQL to create a sql view.
|
30
151
|
|
31
152
|
## License
|
153
|
+
|
32
154
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
155
|
+
|
156
|
+
[<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
|
157
|
+
/>](https://www.railsjazz.com/?utm_source=github&utm_medium=bottom&utm_campaign=sql_view)
|
@@ -8,20 +8,21 @@ module SqlView
|
|
8
8
|
class DBView < OpenStruct
|
9
9
|
def to_schema
|
10
10
|
<<-DEFINITION
|
11
|
-
create_sql_view "#{
|
12
|
-
CREATE
|
11
|
+
create_sql_view "#{viewname}", sql: <<-\SQL
|
12
|
+
CREATE#{materialized_or_not}VIEW "#{viewname}" AS
|
13
13
|
#{escaped_definition.indent(2)}
|
14
14
|
SQL\n
|
15
15
|
DEFINITION
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
19
|
+
|
19
20
|
def materialized?
|
20
|
-
|
21
|
+
kind == "m"
|
21
22
|
end
|
22
23
|
|
23
24
|
def materialized_or_not
|
24
|
-
materialized? ? " MATERIALIZED " :
|
25
|
+
materialized? ? " MATERIALIZED " : " "
|
25
26
|
end
|
26
27
|
|
27
28
|
def escaped_definition
|
@@ -35,40 +36,18 @@ module SqlView
|
|
35
36
|
end
|
36
37
|
|
37
38
|
def views(stream)
|
38
|
-
if
|
39
|
-
stream.puts
|
40
|
-
end
|
39
|
+
stream.puts if sql_views.any?
|
41
40
|
|
42
|
-
|
43
|
-
next if already_indexed?(viewname)
|
44
|
-
view = DBView.new(get_view_info(viewname))
|
41
|
+
sql_views.each do |view|
|
45
42
|
stream.puts(view.to_schema)
|
46
|
-
indexes(viewname, stream)
|
43
|
+
indexes(view.viewname, stream)
|
47
44
|
end
|
48
45
|
end
|
49
46
|
|
50
47
|
private
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
@already_indexed ||= []
|
55
|
-
return true if @already_indexed.include?(viewname)
|
56
|
-
@already_indexed << viewname
|
57
|
-
false
|
58
|
-
end
|
59
|
-
|
60
|
-
def dumpable_views_in_database
|
61
|
-
@dumpable_views_in_database ||= ActiveRecord::Base.connection.views.reject do |viewname|
|
62
|
-
ignored?(viewname)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def get_view_info(viewname)
|
67
|
-
views_schema.detect{|e| e['viewname'] == viewname}
|
68
|
-
end
|
69
|
-
|
70
|
-
def views_schema
|
71
|
-
@views_schema ||= ActiveRecord::Base.connection.execute(<<-SQL)
|
49
|
+
def sql_views
|
50
|
+
@sql_views ||= ActiveRecord::Base.connection.execute(<<-SQL)
|
72
51
|
SELECT
|
73
52
|
c.relname as viewname,
|
74
53
|
pg_get_viewdef(c.oid) AS definition,
|
@@ -79,10 +58,11 @@ module SqlView
|
|
79
58
|
WHERE
|
80
59
|
c.relkind IN ('m', 'v')
|
81
60
|
AND c.relname NOT IN (SELECT extname FROM pg_extension)
|
61
|
+
AND c.relname != 'pg_stat_statements_info'
|
82
62
|
AND n.nspname = ANY (current_schemas(false))
|
83
63
|
ORDER BY c.oid
|
84
64
|
SQL
|
85
|
-
.to_a
|
65
|
+
.to_a.map(&DBView.method(:new)).reject { |view| ignored?(view.viewname) }
|
86
66
|
end
|
87
67
|
|
88
68
|
unless ActiveRecord::SchemaDumper.private_instance_methods(false).include?(:ignored?)
|
@@ -101,4 +81,4 @@ module SqlView
|
|
101
81
|
end
|
102
82
|
end
|
103
83
|
|
104
|
-
SQLView = SqlView
|
84
|
+
SQLView = SqlView
|
data/lib/sql_view/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sql_view
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Kasyanchuk
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -89,7 +89,7 @@ licenses:
|
|
89
89
|
- MIT
|
90
90
|
metadata:
|
91
91
|
homepage_uri: https://github.com/igorkasyanchuk/sql_view
|
92
|
-
post_install_message:
|
92
|
+
post_install_message:
|
93
93
|
rdoc_options: []
|
94
94
|
require_paths:
|
95
95
|
- lib
|
@@ -104,8 +104,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
104
|
- !ruby/object:Gem::Version
|
105
105
|
version: '0'
|
106
106
|
requirements: []
|
107
|
-
rubygems_version: 3.
|
108
|
-
signing_key:
|
107
|
+
rubygems_version: 3.4.13
|
108
|
+
signing_key:
|
109
109
|
specification_version: 4
|
110
110
|
summary: Simple way to create and interact with your SQL views using ActiveRecord.
|
111
111
|
test_files: []
|