sql_view 0.0.1
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +32 -0
- data/Rakefile +3 -0
- data/lib/generators/sql_view/generators.rb +4 -0
- data/lib/generators/sql_view/view/USAGE +16 -0
- data/lib/generators/sql_view/view/view_generator.rb +88 -0
- data/lib/sql_view/railtie.rb +10 -0
- data/lib/sql_view/schema_dumper.rb +94 -0
- data/lib/sql_view/statements.rb +7 -0
- data/lib/sql_view/version.rb +3 -0
- data/lib/sql_view.rb +110 -0
- data/lib/tasks/sql_view_tasks.rake +4 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e0c483bd4b3381f12564e802d7e4169de63644dee304521156471397e5214e4e
|
4
|
+
data.tar.gz: c720532cb86979da70f0bcade52e1e74e5240ebe42e82f21288781511aa73861
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0613cd3a590e6a163c4f035cc6ff8361f2432c155e536dff873f5f6d8d60a78eceb6f9a2946512291c06bbe6d603e0c8da4c4c548e41fbc26079544fe3a6cfb8
|
7
|
+
data.tar.gz: e9c49cfcac2404cd093a88e280d6bb4f8968cb40662bd04aec6ed3893b5bc68aedb108f194f3545add3946a82eb7e5b2c9abf997b241d535519d95b7dae96a95
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022 Igor Kasyanchuk
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# SqlView
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem "sql_view"
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install sql_view
|
22
|
+
```
|
23
|
+
|
24
|
+
## Testing
|
25
|
+
|
26
|
+
`ruby ./test/sql_view_test.rb` (because somehow `rake test` not works, not critical for now)
|
27
|
+
|
28
|
+
## Contributing
|
29
|
+
Contribution directions go here.
|
30
|
+
|
31
|
+
## License
|
32
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Description:
|
2
|
+
Create a new database view in your application.
|
3
|
+
|
4
|
+
To create a materialized view, pass the '--materialized' option.
|
5
|
+
|
6
|
+
Examples:
|
7
|
+
rails generate sql_view:view ActiveUser 'User.where(active: true)'
|
8
|
+
|
9
|
+
create: app/sql_views/search_view.rb
|
10
|
+
create: db/migrate/20140803191158_create_active_users_views.rb
|
11
|
+
|
12
|
+
Examples:
|
13
|
+
rails generate sql_view:view ArchivedAccount Account.archived --materialized --view-name=inactive_accounts
|
14
|
+
|
15
|
+
create: app/sql_views/search_view.rb
|
16
|
+
create: db/migrate/20140803191158_create_search_views.rb
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
require "rails/generators/active_record"
|
3
|
+
|
4
|
+
module SqlView
|
5
|
+
module Generators
|
6
|
+
class ViewGenerator < Rails::Generators::NamedBase
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
|
9
|
+
class_option :"view-name", type: :string, default: nil
|
10
|
+
class_option :materialized, type: :boolean, default: false
|
11
|
+
|
12
|
+
def create_everything
|
13
|
+
create_file "app/sql_views/#{file_name}_view.rb", <<-FILE
|
14
|
+
class #{class_name}View < SqlView::Model
|
15
|
+
#{top_code}
|
16
|
+
|
17
|
+
schema -> {#{schema_code} }
|
18
|
+
|
19
|
+
extend_model_with do
|
20
|
+
# sample how you can extend it, similar to regular AR model
|
21
|
+
#
|
22
|
+
# belongs_to :user
|
23
|
+
# has_many :posts
|
24
|
+
#
|
25
|
+
# scope :ordered, -> { order(:created_at) }
|
26
|
+
# scope :by_role, ->(role) { where(role: role) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
FILE
|
30
|
+
|
31
|
+
create_file "db/migrate/#{self.class.next_migration_number("db/migrate")}_create_#{file_name}s_view.rb", <<-FILE
|
32
|
+
class #{migration_class_name} < #{activerecord_migration_class}
|
33
|
+
def up
|
34
|
+
#{class_name}View.sql_view.up
|
35
|
+
end
|
36
|
+
|
37
|
+
def down
|
38
|
+
#{class_name}View.sql_view.down
|
39
|
+
end
|
40
|
+
end
|
41
|
+
FILE
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.next_migration_number(dir)
|
45
|
+
::ActiveRecord::Generators::Base.next_migration_number(dir)
|
46
|
+
end
|
47
|
+
|
48
|
+
no_tasks do
|
49
|
+
def top_code
|
50
|
+
[view_name_code, materialized_code].compact.join("\n\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
def view_name_code
|
54
|
+
options["view-name"] ? " self.view_name = '#{options["view-name"]}'" : nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def materialized_code
|
58
|
+
options[:materialized] ? " materialized" : nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def schema_code
|
62
|
+
" #{args[0].presence || "\n # ActiveRecord::Relation or SQL\n # for example: User.where(active: true)\n " }"
|
63
|
+
end
|
64
|
+
|
65
|
+
def migration_class_name
|
66
|
+
"Create#{class_name.tr('.', '').pluralize}View"
|
67
|
+
end
|
68
|
+
|
69
|
+
def activerecord_migration_class
|
70
|
+
if ActiveRecord::Migration.respond_to?(:current_version)
|
71
|
+
"ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
|
72
|
+
else
|
73
|
+
"ActiveRecord::Migration"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
alias singular_name file_name
|
81
|
+
|
82
|
+
def file_name
|
83
|
+
super.tr(".", "_")
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module SqlView
|
2
|
+
class Railtie < ::Rails::Railtie
|
3
|
+
initializer "sql_view.load" do
|
4
|
+
ActiveSupport.on_load :active_record do
|
5
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.include SqlView::Statements
|
6
|
+
ActiveRecord::SchemaDumper.prepend SqlView::SchemaDumper
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "rails"
|
2
|
+
|
3
|
+
# Copy-pasted from scenic game. Scenic is a very nice gem
|
4
|
+
|
5
|
+
module SqlView
|
6
|
+
# @api private
|
7
|
+
module SchemaDumper
|
8
|
+
class DBView < OpenStruct
|
9
|
+
|
10
|
+
def materialized?
|
11
|
+
self.kind == "m"
|
12
|
+
end
|
13
|
+
|
14
|
+
def materialized_or_not
|
15
|
+
materialized? ? " MATERIALIZED " : nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_schema
|
19
|
+
<<-DEFINITION
|
20
|
+
create_sql_view "#{self.viewname}", sql: <<-\SQL
|
21
|
+
CREATE #{materialized_or_not} VIEW "#{self.viewname}" AS
|
22
|
+
#{escaped_definition.indent(2)}
|
23
|
+
SQL
|
24
|
+
DEFINITION
|
25
|
+
end
|
26
|
+
|
27
|
+
def escaped_definition
|
28
|
+
definition.gsub("\\", "\\\\\\")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def tables(stream)
|
33
|
+
super
|
34
|
+
views(stream)
|
35
|
+
end
|
36
|
+
|
37
|
+
def views(stream)
|
38
|
+
if dumpable_views_in_database.any?
|
39
|
+
stream.puts
|
40
|
+
end
|
41
|
+
|
42
|
+
dumpable_views_in_database.each do |viewname|
|
43
|
+
view = DBView.new(get_view_info(viewname))
|
44
|
+
#puts view.to_schema
|
45
|
+
stream.puts(view.to_schema)
|
46
|
+
#indexes(view.name, stream)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def dumpable_views_in_database
|
53
|
+
@dumpable_views_in_database ||= ActiveRecord::Base.connection.views.reject do |viewname|
|
54
|
+
ignored?(viewname)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_view_info(viewname)
|
59
|
+
views_schema.detect{|e| e['viewname'] == viewname}
|
60
|
+
end
|
61
|
+
|
62
|
+
def views_schema
|
63
|
+
@views_schema ||= ActiveRecord::Base.connection.execute(<<-SQL)
|
64
|
+
SELECT
|
65
|
+
c.relname as viewname,
|
66
|
+
pg_get_viewdef(c.oid) AS definition,
|
67
|
+
c.relkind AS kind,
|
68
|
+
n.nspname AS namespace
|
69
|
+
FROM pg_class c
|
70
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
71
|
+
WHERE
|
72
|
+
c.relkind IN ('m', 'v')
|
73
|
+
AND c.relname NOT IN (SELECT extname FROM pg_extension)
|
74
|
+
AND n.nspname = ANY (current_schemas(false))
|
75
|
+
ORDER BY c.oid
|
76
|
+
SQL
|
77
|
+
.to_a
|
78
|
+
end
|
79
|
+
|
80
|
+
unless ActiveRecord::SchemaDumper.private_instance_methods(false).include?(:ignored?)
|
81
|
+
# This method will be present in Rails 4.2.0 and can be removed then.
|
82
|
+
def ignored?(table_name)
|
83
|
+
["schema_migrations", ignore_tables].flatten.any? do |ignored|
|
84
|
+
case ignored
|
85
|
+
when String then remove_prefix_and_suffix(table_name) == ignored
|
86
|
+
when Regexp then remove_prefix_and_suffix(table_name) =~ ignored
|
87
|
+
else
|
88
|
+
raise StandardError, "ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values."
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/sql_view.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require "singleton"
|
2
|
+
require_relative "./sql_view/schema_dumper.rb"
|
3
|
+
require_relative "./sql_view/statements.rb"
|
4
|
+
require "sql_view/version"
|
5
|
+
require "sql_view/railtie"
|
6
|
+
|
7
|
+
module SqlView
|
8
|
+
# mattr_accessor :klasses
|
9
|
+
# @@klasses = {}
|
10
|
+
|
11
|
+
class Model
|
12
|
+
class_attribute :view, :sql_view_options
|
13
|
+
|
14
|
+
class << self
|
15
|
+
delegate_missing_to :model
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.inherited(subclass)
|
19
|
+
# puts subclass
|
20
|
+
subclass.sql_view_options = {}
|
21
|
+
# SqlView.klasses[subclass] = subclass.sql_view
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.view_name=(name)
|
25
|
+
@view_name = name
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.view_name
|
29
|
+
@view_name.presence || (self.view_name=self.to_s.underscore.pluralize)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.model
|
33
|
+
@model ||= ClassBuilder.create_model(self)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.sql_view
|
37
|
+
@sql_view ||= Migration.new(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.materialized
|
41
|
+
self.sql_view_options[:materialized] = true
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.schema(sql_or_proc)
|
45
|
+
self.sql_view_options[:sql_or_proc] = sql_or_proc
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.extend_model_with(&block)
|
49
|
+
self.sql_view_options[:extend_model_with] = block
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Migration
|
54
|
+
attr_reader :parent
|
55
|
+
|
56
|
+
def initialize(parent)
|
57
|
+
@parent = parent
|
58
|
+
end
|
59
|
+
|
60
|
+
def refresh
|
61
|
+
down
|
62
|
+
up
|
63
|
+
end
|
64
|
+
|
65
|
+
def up
|
66
|
+
view_sql = parent.sql_view_options[:sql_or_proc].call
|
67
|
+
sql = <<-SQL
|
68
|
+
CREATE #{materialized_or_not} VIEW #{parent.view_name} AS
|
69
|
+
#{view_sql.respond_to?(:to_sql) ? view_sql.to_sql : view_sql };
|
70
|
+
SQL
|
71
|
+
puts sql if Rails.env.development?
|
72
|
+
ActiveRecord::Base.connection.execute sql#.wp
|
73
|
+
end
|
74
|
+
|
75
|
+
def down
|
76
|
+
sql = <<-SQL
|
77
|
+
drop #{materialized_or_not} view if exists #{parent.view_name};
|
78
|
+
SQL
|
79
|
+
puts sql if Rails.env.development?
|
80
|
+
ActiveRecord::Base.connection.execute sql#.wp
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def materialized_or_not
|
86
|
+
parent.sql_view_options[:materialized] ? "MATERIALIZED" : nil
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
class ClassBuilder
|
92
|
+
def ClassBuilder.create_model(parent)
|
93
|
+
klass = Class.new(ActiveRecord::Base) do
|
94
|
+
def self.model_name
|
95
|
+
ActiveModel::Name.new(self, nil, parent.view_name)
|
96
|
+
end
|
97
|
+
def readonly?
|
98
|
+
true
|
99
|
+
end
|
100
|
+
self.table_name = parent.view_name
|
101
|
+
self.inheritance_column = nil
|
102
|
+
end
|
103
|
+
if parent.sql_view_options[:extend_model_with].present?
|
104
|
+
klass.class_eval(&parent.sql_view_options[:extend_model_with])
|
105
|
+
end
|
106
|
+
klass
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sql_view
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Igor Kasyanchuk
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pg
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: wrapped_print
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Simple way to create and interact with your SQL views using ActiveRecord.
|
70
|
+
email:
|
71
|
+
- igorkasyanchuk@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- MIT-LICENSE
|
77
|
+
- README.md
|
78
|
+
- Rakefile
|
79
|
+
- lib/generators/sql_view/generators.rb
|
80
|
+
- lib/generators/sql_view/view/USAGE
|
81
|
+
- lib/generators/sql_view/view/view_generator.rb
|
82
|
+
- lib/sql_view.rb
|
83
|
+
- lib/sql_view/railtie.rb
|
84
|
+
- lib/sql_view/schema_dumper.rb
|
85
|
+
- lib/sql_view/statements.rb
|
86
|
+
- lib/sql_view/version.rb
|
87
|
+
- lib/tasks/sql_view_tasks.rake
|
88
|
+
homepage: https://github.com/igorkasyanchuk/sql_view
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata:
|
92
|
+
homepage_uri: https://github.com/igorkasyanchuk/sql_view
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubygems_version: 3.3.3
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Simple way to create and interact with your SQL views using ActiveRecord.
|
112
|
+
test_files: []
|