rails_on_pg 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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +53 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/rails_on_pg.rb +10 -0
- data/lib/rails_on_pg/foreign_keys.rb +38 -0
- data/lib/rails_on_pg/functions.rb +62 -0
- data/lib/rails_on_pg/views.rb +115 -0
- data/rails/init.rb +1 -0
- data/test/foreign_keys_test.rb +14 -0
- data/test/functions_test.rb +43 -0
- data/test/helper.rb +33 -0
- data/test/schema.rb +26 -0
- data/test/views_test.rb +59 -0
- metadata +75 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Alex Tretyakov
|
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.rdoc
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
h1. RailsOnPG
|
2
|
+
|
3
|
+
This is timesaver for middle/large Rails application which used PostgreSQL as database.
|
4
|
+
Create/drop Views, Functions, Triggers, Foreign keys in your migrations using ruby syntax.
|
5
|
+
|
6
|
+
h2. Installation
|
7
|
+
|
8
|
+
<pre>
|
9
|
+
script/plugin install git://github.com/alex3t/rails_on_pg.git
|
10
|
+
</pre>
|
11
|
+
|
12
|
+
h2. Views
|
13
|
+
|
14
|
+
<pre>
|
15
|
+
create_view :active_patients do |view|
|
16
|
+
view.select 'p.patient_id as id' ,'p.id as visit_id'
|
17
|
+
view.from 'patients as p'
|
18
|
+
view.join 'left join demographics d on d.visit_id=v.id'
|
19
|
+
view.conditions 'p.status'=>'active','p.name' => 'John' #or "p.status='active' and p.name='John'"
|
20
|
+
end
|
21
|
+
</pre>
|
22
|
+
|
23
|
+
h2. Functions
|
24
|
+
|
25
|
+
<pre>
|
26
|
+
create_function 'format_name', {:returns=>'character varying'}, 'first_name character varying(125)', 'middle_name character varying(15)', "last_name character varying(20)" do
|
27
|
+
"RETURN COALESCE(last_name, 'no last name') || ', ' || COALESCE(first_name, 'no first name');"
|
28
|
+
end
|
29
|
+
</pre>
|
30
|
+
|
31
|
+
h2. Triggers
|
32
|
+
|
33
|
+
<pre>
|
34
|
+
create_trigger "update_status", :before, "users", "insert","update"
|
35
|
+
#update status function body here
|
36
|
+
end
|
37
|
+
</pre>
|
38
|
+
|
39
|
+
h2. Foreign keys
|
40
|
+
|
41
|
+
<pre>
|
42
|
+
add_foreign_key :order_items, :product_id, :orders, :on_delete=>""
|
43
|
+
</pre>
|
44
|
+
|
45
|
+
For more details see rdoc or tests
|
46
|
+
|
47
|
+
|
48
|
+
h3. Todo
|
49
|
+
|
50
|
+
Make as gem
|
51
|
+
|
52
|
+
|
53
|
+
Copyright (c) 2009 Alex Tretyakov, released under MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rails_on_pg"
|
8
|
+
gem.summary = %Q{Add PostgreSQL functionality to create/drop views, functions, triggers and foreign keys in migrations.}
|
9
|
+
gem.description = %Q{This is timesaver for middle/large Rails application which used PostgreSQL as database. Create/drop Views, Functions, Triggers, Foreign keys in your migrations using ruby syntax.}
|
10
|
+
gem.email = "marcus.wyatt@visfleet.com"
|
11
|
+
gem.homepage = "http://github.com/alex3t/rails_on_pg"
|
12
|
+
gem.authors = ["Alex Tretyakov"]
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/test_*.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'rcov/rcovtask'
|
29
|
+
Rcov::RcovTask.new do |test|
|
30
|
+
test.libs << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
task :rcov do
|
36
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
task :test => :check_dependencies
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "rails_on_pg #{version}"
|
50
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/rails_on_pg.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
require "rails_on_pg/foreign_keys"
|
3
|
+
require "rails_on_pg/views"
|
4
|
+
require "rails_on_pg/functions"
|
5
|
+
|
6
|
+
ActiveRecord::Migration.send(:extend, RailsOnPg::ForeignKeys)
|
7
|
+
ActiveRecord::Migration.send(:extend, RailsOnPg::Views)
|
8
|
+
ActiveRecord::Migration.send(:extend, RailsOnPg::Functions)
|
9
|
+
|
10
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RailsOnPg
|
2
|
+
module ForeignKeys
|
3
|
+
|
4
|
+
# Define new foreign key
|
5
|
+
# Example:
|
6
|
+
# add_foreign_key :order_items,:product_id,:orders
|
7
|
+
# where orders is orders is referenced table and order_items is referencing table
|
8
|
+
# foreign key will be: 'fk_order_items_product_id'
|
9
|
+
# optional options:
|
10
|
+
# <tt>:on_delete</tt>
|
11
|
+
# <tt>:on_update</tt>
|
12
|
+
# <tt>:column</tt>
|
13
|
+
def add_foreign_key(from_table, from_column, to_table, options={})
|
14
|
+
# default delete and on update actions
|
15
|
+
options.reverse_merge!({:on_delete=>'NO ACTION', :on_update=>'NO ACTION',:column=>'id'})
|
16
|
+
constraint_name = "fk_#{from_table}_#{from_column}"
|
17
|
+
|
18
|
+
execute %{ALTER TABLE #{from_table}
|
19
|
+
ADD CONSTRAINT #{constraint_name}
|
20
|
+
FOREIGN KEY (#{from_column})
|
21
|
+
REFERENCES #{to_table}(#{options[:column]})
|
22
|
+
ON UPDATE #{options[:on_update]}
|
23
|
+
ON DELETE #{options[:on_delete]} }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Remove prev created key
|
27
|
+
def remove_foreign_key(from_table, from_column, to_table)
|
28
|
+
constraint_name = "fk_#{from_table}_#{from_column}"
|
29
|
+
# check if constraint already exist
|
30
|
+
count = ActiveRecord::Base.connection.select_value("select count(1) from pg_constraint where conname='#{constraint_name}'")
|
31
|
+
|
32
|
+
unless count.to_i == 0
|
33
|
+
execute %{ALTER TABLE #{from_table} DROP CONSTRAINT #{constraint_name}}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module RailsOnPg
|
2
|
+
module Functions
|
3
|
+
|
4
|
+
# Create new plpgsql function
|
5
|
+
# Example:
|
6
|
+
# create_function 'format_name', {:returns=>'character varying'}, 'first_name character varying(125)', 'middle_name character varying(15)', "last_name character varying(20)" do
|
7
|
+
# "RETURN COALESCE(last_name, 'no last name') || ', ' || COALESCE(first_name, 'no first name') || ' ' || COALESCE(middle_name || '.','');"
|
8
|
+
# end
|
9
|
+
def create_function name, options={}, *params
|
10
|
+
options.reverse_merge!({:as=>'$$'})
|
11
|
+
returns = options[:returns]
|
12
|
+
declare = %{DECLARE
|
13
|
+
#{options[:declare].join(';')}} if options[:declare]
|
14
|
+
drop_function name, params
|
15
|
+
# execute
|
16
|
+
set_lang
|
17
|
+
execute %{CREATE FUNCTION #{name}(#{params.join(',')}) RETURNS #{returns} AS #{options[:as]}
|
18
|
+
#{declare}
|
19
|
+
BEGIN
|
20
|
+
#{yield}
|
21
|
+
END;
|
22
|
+
#{options[:as]} LANGUAGE 'plpgsql';
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
# drop function
|
27
|
+
def drop_function name, *params
|
28
|
+
execute "DROP FUNCTION IF EXISTS #{name}(#{params.join(',')}) CASCADE"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Create trigger function for it
|
32
|
+
# <tt>name</tt> - trigger name
|
33
|
+
# <tt>type</tt> - :before or :after
|
34
|
+
# <tt>table_name</tt> - table name
|
35
|
+
# <tt>actions</tt> - "insert","update","delete"
|
36
|
+
# Example:
|
37
|
+
# create_trigger "some_tr", :before, "users", "insert","update"
|
38
|
+
def create_trigger name, type, table_name, *actions
|
39
|
+
create_function "#{name}_f", :returns=>'trigger',:as=>'$BODY$' do
|
40
|
+
yield
|
41
|
+
end
|
42
|
+
execute %{CREATE TRIGGER #{name} #{type.to_s.upcase} #{actions.map{|str|str.upcase}.join(' OR ')}
|
43
|
+
ON "#{table_name}" FOR EACH ROW
|
44
|
+
EXECUTE PROCEDURE #{name}_f();}
|
45
|
+
end
|
46
|
+
|
47
|
+
# Drop trigger
|
48
|
+
def drop_trigger name, table_name
|
49
|
+
execute "DROP TRIGGER #{name} on #{table_name} CASCADE"
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def set_lang lang='plpgsql'
|
54
|
+
begin
|
55
|
+
execute("CREATE LANGUAGE #{lang}")
|
56
|
+
rescue ActiveRecord::StatementInvalid => ex
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module RailsOnPg
|
2
|
+
module Views
|
3
|
+
|
4
|
+
# Create new view
|
5
|
+
# <tt>name</tt> - name of view
|
6
|
+
# Example:
|
7
|
+
# create_view :active_patients do |v|
|
8
|
+
# v.select 'p.patient_id as id' ,'p.id as visit_id'
|
9
|
+
# v.from 'patients as p'
|
10
|
+
# v.join 'left join demographics d on d.visit_id=v.id'
|
11
|
+
# v.join 'left join diagnoses di on di.visit_id=v.id and di.row_index=0'
|
12
|
+
# v.conditions 'p.status'=>'active','p.name' => 'John' #or "p.status='active' and p.name='John'"
|
13
|
+
# end
|
14
|
+
# See ViewDefinition class
|
15
|
+
def create_view name, options={}, &block
|
16
|
+
view_def = ViewDefinition.new name, &block
|
17
|
+
|
18
|
+
drop_views name, options[:dependent_views]
|
19
|
+
execute view_def.to_sql
|
20
|
+
end
|
21
|
+
|
22
|
+
# Update view's select columns
|
23
|
+
# <tt>name</tt> - name of existed view
|
24
|
+
# <tt>type</tt> - type of action(:add,:remove or :replace)
|
25
|
+
# <tt>columns</tt> - array of columns or string
|
26
|
+
# <tt>options</tt> - options
|
27
|
+
# Options:
|
28
|
+
# <tt>:dependent_views</tt> - if view has dependent views(views where current view used) then you need list them here
|
29
|
+
# Example:
|
30
|
+
# update_view :active_patients, :add, ['p.first_name as name','p.age as dob']
|
31
|
+
# update_view :active_patients, :add, 'p.first_name as name', :dependent_views=>['view0','view1']
|
32
|
+
# update_view :active_patients, :remove, 'p.first_name as name', :dependent_views=>['view0','view1']
|
33
|
+
# update_view :active_patients, :replace, ['p.first_name as name','p.age as dob'] #replace all select columns
|
34
|
+
def update_view name, type, columns, options={}
|
35
|
+
view_structure = ActiveRecord::Base.connection.select_value("select definition from pg_views where viewname='#{name}'")
|
36
|
+
raise ViewNotExistException("View #{name} does not exist in current db") unless view_structure
|
37
|
+
|
38
|
+
columns_str = columns.is_a?(Array) ? columns.join(',') : columns
|
39
|
+
|
40
|
+
select_pattern = /select (.*) from/i
|
41
|
+
select_str = view_structure[select_pattern,1]
|
42
|
+
|
43
|
+
case type
|
44
|
+
when :add
|
45
|
+
view_structure.gsub!(select_pattern, "SELECT #{select_str}, #{columns_str} FROM")
|
46
|
+
when :remove
|
47
|
+
select_str.gsub!(", #{columns_str}", '')
|
48
|
+
view_structure.gsub!(select_pattern, "SELECT #{select_str} FROM")
|
49
|
+
when :replace
|
50
|
+
view_structure.gsub!(select_pattern, "SELECT #{columns_str} FROM")
|
51
|
+
end
|
52
|
+
|
53
|
+
drop_views name, options[:dependent_views]
|
54
|
+
execute "CREATE VIEW #{name} AS #{view_structure};"
|
55
|
+
end
|
56
|
+
|
57
|
+
# drop dependent views before if exists
|
58
|
+
# Options
|
59
|
+
# <tt>:dependent_views</tt> - if view has dependent views(views where current view used) then you need list them here
|
60
|
+
def drop_views name, defs=nil
|
61
|
+
defs = defs.delete(:dependent_views) if defs.is_a?(Hash)
|
62
|
+
defs.each do |dependent_view|
|
63
|
+
execute "DROP VIEW IF EXISTS #{dependent_view}"
|
64
|
+
end if defs
|
65
|
+
|
66
|
+
execute "DROP VIEW IF EXISTS #{name}"
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
# recreate view without changes
|
71
|
+
def recreate_view name
|
72
|
+
view_structure = ActiveRecord::Base.connection.select_value("select definition from pg_views where viewname='#{name}'")
|
73
|
+
if view_structure
|
74
|
+
execute "DROP VIEW IF EXISTS #{name}"
|
75
|
+
execute "CREATE VIEW #{name} AS #{view_structure};"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# ===========
|
80
|
+
# = Classes =
|
81
|
+
# ===========
|
82
|
+
class ViewNotExistException < Exception; end
|
83
|
+
|
84
|
+
# View definition, see create_view dsl
|
85
|
+
class ViewDefinition
|
86
|
+
def initialize name, &block
|
87
|
+
@joins = []
|
88
|
+
@name = name
|
89
|
+
instance_eval &block
|
90
|
+
end
|
91
|
+
|
92
|
+
def select *columns
|
93
|
+
@select = columns.join(',')
|
94
|
+
end
|
95
|
+
def from *tables
|
96
|
+
@from = tables.join(',')
|
97
|
+
end
|
98
|
+
def join value
|
99
|
+
@joins << value
|
100
|
+
end
|
101
|
+
def conditions cond
|
102
|
+
|
103
|
+
@where = cond.collect{ |attrib, value| "#{attrib} = #{value}"}.join(" AND ") if cond.is_a?(Hash)
|
104
|
+
@where = cond if cond.is_a?(String)
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_sql
|
108
|
+
@where ||= '1=1'
|
109
|
+
"CREATE VIEW #{@name} AS SELECT #{@select} FROM #{@from} #{@joins.join(' ')} WHERE #{@where};"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rails_on_pg'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
class ForeignKeysTest < ActiveSupport::TestCase
|
6
|
+
# Replace this with your real tests.
|
7
|
+
test "should create new foreign key" do
|
8
|
+
MigrationTest.remove_foreign_key :projects, :user_id, :users
|
9
|
+
MigrationTest.add_foreign_key :projects, :user_id, :users
|
10
|
+
fk_name = 'fk_projects_user_id'
|
11
|
+
count = ActiveRecord::Base.connection.select_value("select count(1) from pg_constraint where conname='#{fk_name}'")
|
12
|
+
assert_equal 1, count.to_i
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FunctionsTest < ActiveSupport::TestCase
|
4
|
+
def setup
|
5
|
+
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
test "should create new function" do
|
10
|
+
MigrationTest.create_function 'format_name', {:returns=>'character varying'}, 'first_name character varying(125)', 'middle_name character varying(15)', "last_name character varying(20)" do
|
11
|
+
"RETURN COALESCE(last_name, 'no last name') || ', ' || COALESCE(first_name, 'no first name') || ' ' || COALESCE(middle_name || '.','');"
|
12
|
+
end
|
13
|
+
f_count = ActiveRecord::Base.connection.select_value(
|
14
|
+
"select count(1) from pg_proc where proname='format_name'")
|
15
|
+
assert_equal 1, f_count.to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
test "should succefully drop function" do
|
19
|
+
MigrationTest.drop_function 'format_name', 'first_name character varying(125)', 'middle_name character varying(15)', "last_name character varying(20)"
|
20
|
+
f_count = ActiveRecord::Base.connection.select_value(
|
21
|
+
"select count(1) from pg_proc where proname='format_name'")
|
22
|
+
assert_equal 0, f_count.to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
test "should create new trigger" do
|
26
|
+
MigrationTest.create_trigger "some_tr", :before, "users", "insert","update" do
|
27
|
+
|
28
|
+
end
|
29
|
+
f_count = ActiveRecord::Base.connection.select_value(
|
30
|
+
"select count(1) from pg_trigger where tgname='some_tr'")
|
31
|
+
assert_equal 1, f_count.to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
test "should succefully drop trigger" do
|
35
|
+
MigrationTest.drop_trigger 'some_tr','users'
|
36
|
+
f_count = ActiveRecord::Base.connection.select_value(
|
37
|
+
"select count(1) from pg_trigger where tgname='some_tr'")
|
38
|
+
assert_equal 0, f_count.to_i
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/test_case'
|
4
|
+
require 'active_record'
|
5
|
+
require 'test/unit'
|
6
|
+
|
7
|
+
# ENV['RAILS_ENV'] = 'test'
|
8
|
+
ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
|
9
|
+
|
10
|
+
require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
# config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
15
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
16
|
+
|
17
|
+
|
18
|
+
# ActiveRecord::Base.establish_connection(config['test'])
|
19
|
+
|
20
|
+
ActiveRecord::Base.establish_connection({
|
21
|
+
:adapter => 'postgresql',
|
22
|
+
:database => 'test2',
|
23
|
+
:username => 'postgres',
|
24
|
+
:password => 'postgres',
|
25
|
+
:host => 'localhost',
|
26
|
+
})
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
31
|
+
|
32
|
+
class MigrationTest < ActiveRecord::Migration
|
33
|
+
end
|
data/test/schema.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
|
3
|
+
create_table "projects", :force => true do |t|
|
4
|
+
t.string "name"
|
5
|
+
t.integer "user_id"
|
6
|
+
t.datetime "created_at"
|
7
|
+
t.datetime "updated_at"
|
8
|
+
end
|
9
|
+
|
10
|
+
create_table "tasks", :force => true do |t|
|
11
|
+
t.string "value"
|
12
|
+
t.integer "project_id"
|
13
|
+
t.datetime "created_at"
|
14
|
+
t.datetime "updated_at"
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table "users", :force => true do |t|
|
18
|
+
t.string "login"
|
19
|
+
t.string "email"
|
20
|
+
t.text "about"
|
21
|
+
t.boolean "is_active"
|
22
|
+
t.datetime "created_at"
|
23
|
+
t.datetime "updated_at"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/test/views_test.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ViewsTest < ActiveSupport::TestCase
|
4
|
+
def setup
|
5
|
+
|
6
|
+
MigrationTest.create_view 'active_users' do |v|
|
7
|
+
v.select 'u.id','u.login'
|
8
|
+
v.from 'users as u'
|
9
|
+
v.conditions 'is_active'=>'true'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
test "create view" do
|
15
|
+
|
16
|
+
def_expected = %{SELECT u.id, u.login FROM users u WHERE (u.is_active = true);}
|
17
|
+
def_created = ActiveRecord::Base.connection.select_value("select definition from pg_views where viewname='active_users'")
|
18
|
+
assert_equal def_expected, def_created
|
19
|
+
MigrationTest.drop_views 'active_users'
|
20
|
+
end
|
21
|
+
|
22
|
+
test "add column to view" do
|
23
|
+
|
24
|
+
MigrationTest.update_view 'active_users', :add, 'u.email', :dependent_views=>[:active_test_users]
|
25
|
+
def_expected = %{SELECT u.id, u.login, u.email FROM users u WHERE (u.is_active = true);}
|
26
|
+
def_created = ActiveRecord::Base.connection.select_value("select definition from pg_views where viewname='active_users'")
|
27
|
+
assert_equal def_expected, def_created
|
28
|
+
MigrationTest.drop_views 'active_users'
|
29
|
+
end
|
30
|
+
test "remove column to view" do
|
31
|
+
|
32
|
+
MigrationTest.update_view 'active_users', :remove, 'u.login', :dependent_views=>[:active_test_users]
|
33
|
+
def_expected = %{SELECT u.id FROM users u WHERE (u.is_active = true);}
|
34
|
+
def_created = ActiveRecord::Base.connection.select_value("select definition from pg_views where viewname='active_users'")
|
35
|
+
assert_equal def_expected, def_created
|
36
|
+
MigrationTest.drop_views 'active_users'
|
37
|
+
end
|
38
|
+
test "replace columns" do
|
39
|
+
|
40
|
+
MigrationTest.update_view 'active_users', :replace, 'u.login,u.email', :dependent_views=>[:active_test_users]
|
41
|
+
def_expected = %{SELECT u.login, u.email FROM users u WHERE (u.is_active = true);}
|
42
|
+
def_created = ActiveRecord::Base.connection.select_value("select definition from pg_views where viewname='active_users'")
|
43
|
+
assert_equal def_expected, def_created
|
44
|
+
MigrationTest.drop_views 'active_users'
|
45
|
+
end
|
46
|
+
|
47
|
+
test "drop dependent views" do
|
48
|
+
MigrationTest.create_view 'active_test_users' do |v|
|
49
|
+
v.select '*'
|
50
|
+
v.from 'active_users'
|
51
|
+
v.conditions "active_users.login like '%test%'"
|
52
|
+
end
|
53
|
+
MigrationTest.drop_views 'active_users', :dependent_views=>[:active_test_users]
|
54
|
+
count_active_test_users = ActiveRecord::Base.connection.select_value("select count(1) from pg_views where viewname='active_test_users'")
|
55
|
+
assert_equal 0, count_active_test_users.to_i
|
56
|
+
count_active_users = ActiveRecord::Base.connection.select_value("select count(1) from pg_views where viewname='active_users'")
|
57
|
+
assert_equal 0, count_active_users.to_i
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_on_pg
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Tretyakov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-19 00:00:00 +13:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: This is timesaver for middle/large Rails application which used PostgreSQL as database. Create/drop Views, Functions, Triggers, Foreign keys in your migrations using ruby syntax.
|
17
|
+
email: marcus.wyatt@visfleet.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .document
|
27
|
+
- .gitignore
|
28
|
+
- LICENSE
|
29
|
+
- README.rdoc
|
30
|
+
- Rakefile
|
31
|
+
- VERSION
|
32
|
+
- lib/rails_on_pg.rb
|
33
|
+
- lib/rails_on_pg/foreign_keys.rb
|
34
|
+
- lib/rails_on_pg/functions.rb
|
35
|
+
- lib/rails_on_pg/views.rb
|
36
|
+
- rails/init.rb
|
37
|
+
- test/foreign_keys_test.rb
|
38
|
+
- test/functions_test.rb
|
39
|
+
- test/helper.rb
|
40
|
+
- test/schema.rb
|
41
|
+
- test/views_test.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/alex3t/rails_on_pg
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Add PostgreSQL functionality to create/drop views, functions, triggers and foreign keys in migrations.
|
70
|
+
test_files:
|
71
|
+
- test/foreign_keys_test.rb
|
72
|
+
- test/functions_test.rb
|
73
|
+
- test/helper.rb
|
74
|
+
- test/schema.rb
|
75
|
+
- test/views_test.rb
|