diff --git a/Gemfile.lock b/Gemfile.lock index 896ba9e..04444da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - sql_view (0.0.5) + sql_view (0.0.6) rails GEM @@ -95,7 +95,7 @@ GEM mini_mime (1.1.2) mini_portile2 (2.8.2) minitest (5.15.0) - net-imap (0.3.4) + net-imap (0.3.6) date net-protocol net-pop (0.1.2) @@ -114,7 +114,7 @@ GEM pry (0.14.1) coderay (~> 1.1) method_source (~> 1.0) - racc (1.6.2) + racc (1.7.1) rack (2.2.7) rack-test (2.1.0) rack (>= 1.3) diff --git a/README.md b/README.md index 7c7c0dd..ad8e257 100755 --- a/README.md +++ b/README.md @@ -78,6 +78,18 @@ ActiveUserView.model.includes(:profile) If you need to refresh materialized view - `ActiveUserView.sql_view.refresh` (if you need to do it concerrently - `.refresh(concurrently: false)`. +It can also be used with your other models: + +```ruby +class Account < ApplicationRecord + has_many :users + + has_one :account_stat_view, class_name: AccountStatViewView.model.to_s, foreign_key: :account_id + has_many :active_users, join_table: :active_users_views, class_name: ActiveUserView.model.to_s, foreign_key: :account_id +end +``` + + More examples in this file: `./test/sql_view_test.rb` ## Installation diff --git a/lib/sql_view.rb b/lib/sql_view.rb index 82206ea..4c79f22 100755 --- a/lib/sql_view.rb +++ b/lib/sql_view.rb @@ -68,7 +68,7 @@ def refresh(concurrently: false) sql = <<-SQL REFRESH#{materialized_or_not}VIEW#{concurrently_or_not}#{parent.view_name}; SQL - execute(sql) + execute(sql, log: false) end def up @@ -88,8 +88,8 @@ def down execute(sql) end - def execute(sql) - SqlView.log sql + def execute(sql, log: true) + SqlView.log(sql) if log ActiveRecord::Base.connection.execute sql#.wp end @@ -103,6 +103,7 @@ def materialized_or_not class ClassBuilder def ClassBuilder.create_model(parent) + class_name = "#{parent}Model" klass = Class.new(ActiveRecord::Base) do def self.model_name ActiveModel::Name.new(self, nil, parent.view_name) @@ -120,10 +121,11 @@ def readonly? # because of the error undefined scan for nil class klass.class_eval %Q{ def self.name - "#{parent.class}" + "#{class_name}" end } - klass + Object.const_set(class_name, klass) unless const_defined?(class_name) + Object.const_get(class_name) end end diff --git a/lib/sql_view/version.rb b/lib/sql_view/version.rb index 8b0d4a5..536a0d3 100755 --- a/lib/sql_view/version.rb +++ b/lib/sql_view/version.rb @@ -1,3 +1,3 @@ module SqlView - VERSION = "0.0.5" + VERSION = "0.0.6" end diff --git a/test/dummy/app/models/account.rb b/test/dummy/app/models/account.rb index c17a874..f6f0354 100644 --- a/test/dummy/app/models/account.rb +++ b/test/dummy/app/models/account.rb @@ -1,2 +1,6 @@ class Account < ApplicationRecord + has_many :users + + has_one :account_stat_view, class_name: AccountStatViewView.model.to_s, foreign_key: :account_id + has_many :active_users, join_table: :active_users_views, class_name: ActiveUserView.model.to_s, foreign_key: :account_id end diff --git a/test/dummy/app/models/user.rb b/test/dummy/app/models/user.rb index 379658a..2ba50da 100644 --- a/test/dummy/app/models/user.rb +++ b/test/dummy/app/models/user.rb @@ -1,2 +1,3 @@ class User < ApplicationRecord + belongs_to :account, optional: true end diff --git a/test/dummy/app/sql_views/account_stat_view_view.rb b/test/dummy/app/sql_views/account_stat_view_view.rb new file mode 100644 index 0000000..31103bd --- /dev/null +++ b/test/dummy/app/sql_views/account_stat_view_view.rb @@ -0,0 +1,18 @@ +class AccountStatViewView < SQLView::Model + + schema -> { + "SELECT accounts.id as account_id, RANDOM() as factor from accounts" + } + + extend_model_with do + # sample how you can extend it, similar to regular AR model + # + # include SomeConcern + # + # belongs_to :user + # has_many :posts + # + # scope :ordered, -> { order(:created_at) } + # scope :by_role, ->(role) { where(role: role) } + end +end diff --git a/test/dummy/db/migrate/20230618100146_add_account_id_to_user.rb b/test/dummy/db/migrate/20230618100146_add_account_id_to_user.rb new file mode 100644 index 0000000..7163928 --- /dev/null +++ b/test/dummy/db/migrate/20230618100146_add_account_id_to_user.rb @@ -0,0 +1,5 @@ +class AddAccountIdToUser < ActiveRecord::Migration[7.0] + def change + add_column :users, :account_id, :integer + end +end diff --git a/test/dummy/db/migrate/20230618101748_create_account_stat_view_view.rb b/test/dummy/db/migrate/20230618101748_create_account_stat_view_view.rb new file mode 100644 index 0000000..77c530d --- /dev/null +++ b/test/dummy/db/migrate/20230618101748_create_account_stat_view_view.rb @@ -0,0 +1,9 @@ +class CreateAccountStatViewView < ActiveRecord::Migration[7.0] + def up + AccountStatViewView.sql_view.up + end + + def down + AccountStatViewView.sql_view.down + end +end diff --git a/test/dummy/db/migrate/20230618104439_refresh_active_users.rb b/test/dummy/db/migrate/20230618104439_refresh_active_users.rb new file mode 100644 index 0000000..75106fb --- /dev/null +++ b/test/dummy/db/migrate/20230618104439_refresh_active_users.rb @@ -0,0 +1,6 @@ +class RefreshActiveUsers < ActiveRecord::Migration[7.0] + def change + ActiveUserView.sql_view.down + ActiveUserView.sql_view.up + end +end diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index 844e326..a984319 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_06_07_072750) do +ActiveRecord::Schema.define(version: 2023_06_18_104439) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" @@ -36,6 +36,7 @@ t.integer "age" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false + t.integer "account_id" end create_table "workers", force: :cascade do |t| @@ -47,17 +48,6 @@ end - create_sql_view "active_account_views", sql: <<-SQL - CREATE VIEW "active_account_views" AS - SELECT accounts.id, - accounts.name, - accounts.active, - accounts.created_at, - accounts.updated_at - FROM accounts - WHERE (accounts.active = true); - SQL - create_sql_view "deleted_account_views", sql: <<-SQL CREATE MATERIALIZED VIEW "deleted_account_views" AS SELECT accounts.id, @@ -102,6 +92,24 @@ FROM workers; SQL + create_sql_view "account_stat_view_views", sql: <<-SQL + CREATE VIEW "account_stat_view_views" AS + SELECT accounts.id AS account_id, + random() AS factor + FROM accounts; + SQL + + create_sql_view "active_account_views", sql: <<-SQL + CREATE VIEW "active_account_views" AS + SELECT accounts.id, + accounts.name, + accounts.active, + accounts.created_at, + accounts.updated_at + FROM accounts + WHERE (accounts.active = true); + SQL + create_sql_view "active_user_views", sql: <<-SQL CREATE MATERIALIZED VIEW "active_user_views" AS SELECT users.id, @@ -109,7 +117,8 @@ users.country, users.age, users.created_at, - users.updated_at + users.updated_at, + users.account_id FROM users WHERE ((users.age >= 18) AND (users.age <= 60)); SQL diff --git a/test/sql_view_test.rb b/test/sql_view_test.rb index 682dde4..a3e0406 100755 --- a/test/sql_view_test.rb +++ b/test/sql_view_test.rb @@ -61,6 +61,25 @@ class SqlViewTest < ActiveSupport::TestCase assert anna, ActiveWorkerView.model.first.jobable end + test 'association 1' do + account = Account.create + user = User.create(account: account) + assert account.account_stat_view.present? + end + + test 'association 2' do + account = Account.create + user = User.create(account: account) + assert_equal account, Account.joins(:account_stat_view).where(id: account.id).first + assert_equal account, Account.joins(:account_stat_view).where(id: account.id).where("account_stat_view_views.factor > 0").first + assert_equal 0, account.active_users.count + end + + test 'association 3' do + account = Account.create + assert_equal "ActiveUserViewModel", account.active_users.build.class.to_s + end + # test 'migration' do # a = User.create(age: 20) # a = User.create(age: 30)