pg_morph 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +119 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +62 -0
  6. data/Rakefile +8 -0
  7. data/docs/pg_morph.png +0 -0
  8. data/lib/pg_morph/adapter.rb +39 -0
  9. data/lib/pg_morph/naming.rb +37 -0
  10. data/lib/pg_morph/polymorphic.rb +158 -0
  11. data/lib/pg_morph/railtie.rb +11 -0
  12. data/lib/pg_morph/version.rb +3 -0
  13. data/lib/pg_morph.rb +16 -0
  14. data/pg_morph.gemspec +31 -0
  15. data/spec/dummy/.rspec +1 -0
  16. data/spec/dummy/Rakefile +6 -0
  17. data/spec/dummy/app/models/.keep +0 -0
  18. data/spec/dummy/app/models/comment.rb +3 -0
  19. data/spec/dummy/app/models/like.rb +3 -0
  20. data/spec/dummy/app/models/post.rb +3 -0
  21. data/spec/dummy/bin/bundle +3 -0
  22. data/spec/dummy/bin/rails +4 -0
  23. data/spec/dummy/bin/rake +4 -0
  24. data/spec/dummy/config/application.rb +24 -0
  25. data/spec/dummy/config/boot.rb +4 -0
  26. data/spec/dummy/config/database.yml +8 -0
  27. data/spec/dummy/config/database.yml.example +8 -0
  28. data/spec/dummy/config/environment.rb +5 -0
  29. data/spec/dummy/config/environments/test.rb +36 -0
  30. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  31. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  32. data/spec/dummy/config/initializers/inflections.rb +16 -0
  33. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  34. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  35. data/spec/dummy/config/initializers/session_store.rb +3 -0
  36. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  37. data/spec/dummy/config/locales/en.yml +23 -0
  38. data/spec/dummy/config/routes.rb +56 -0
  39. data/spec/dummy/config.ru +4 -0
  40. data/spec/dummy/db/migrate/20140401124656_create_comments.rb +9 -0
  41. data/spec/dummy/db/migrate/20140401124705_create_posts.rb +9 -0
  42. data/spec/dummy/db/migrate/20140401124727_create_likes.rb +10 -0
  43. data/spec/dummy/db/schema.rb +38 -0
  44. data/spec/dummy/db/seeds.rb +7 -0
  45. data/spec/pg_morph/adapter_integration_spec.rb +89 -0
  46. data/spec/pg_morph/adapter_spec.rb +31 -0
  47. data/spec/pg_morph/naming_spec.rb +29 -0
  48. data/spec/pg_morph/polymorphic_integration_spec.rb +86 -0
  49. data/spec/pg_morph/polymorphic_spec.rb +98 -0
  50. data/spec/spec_helper.rb +16 -0
  51. metadata +246 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ spec/dummy/log
2
+ spec/dummy/config/*.yml
3
+ tags
4
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pg_morph.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,119 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pg_morph (0.1.0)
5
+ activerecord (~> 3)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actionmailer (3.2.19)
11
+ actionpack (= 3.2.19)
12
+ mail (~> 2.5.4)
13
+ actionpack (3.2.19)
14
+ activemodel (= 3.2.19)
15
+ activesupport (= 3.2.19)
16
+ builder (~> 3.0.0)
17
+ erubis (~> 2.7.0)
18
+ journey (~> 1.0.4)
19
+ rack (~> 1.4.5)
20
+ rack-cache (~> 1.2)
21
+ rack-test (~> 0.6.1)
22
+ sprockets (~> 2.2.1)
23
+ activemodel (3.2.19)
24
+ activesupport (= 3.2.19)
25
+ builder (~> 3.0.0)
26
+ activerecord (3.2.19)
27
+ activemodel (= 3.2.19)
28
+ activesupport (= 3.2.19)
29
+ arel (~> 3.0.2)
30
+ tzinfo (~> 0.3.29)
31
+ activeresource (3.2.19)
32
+ activemodel (= 3.2.19)
33
+ activesupport (= 3.2.19)
34
+ activesupport (3.2.19)
35
+ i18n (~> 0.6, >= 0.6.4)
36
+ multi_json (~> 1.0)
37
+ arel (3.0.3)
38
+ awesome_print (1.2.0)
39
+ builder (3.0.4)
40
+ coderay (1.1.0)
41
+ diff-lcs (1.2.5)
42
+ erubis (2.7.0)
43
+ hike (1.2.3)
44
+ i18n (0.6.11)
45
+ journey (1.0.4)
46
+ json (1.8.1)
47
+ mail (2.5.4)
48
+ mime-types (~> 1.16)
49
+ treetop (~> 1.4.8)
50
+ method_source (0.8.2)
51
+ mime-types (1.25.1)
52
+ multi_json (1.10.1)
53
+ pg (0.17.0)
54
+ polyglot (0.3.5)
55
+ pry (0.10.1)
56
+ coderay (~> 1.1.0)
57
+ method_source (~> 0.8.1)
58
+ slop (~> 3.4)
59
+ rack (1.4.5)
60
+ rack-cache (1.2)
61
+ rack (>= 0.4)
62
+ rack-ssl (1.3.4)
63
+ rack
64
+ rack-test (0.6.2)
65
+ rack (>= 1.0)
66
+ rails (3.2.19)
67
+ actionmailer (= 3.2.19)
68
+ actionpack (= 3.2.19)
69
+ activerecord (= 3.2.19)
70
+ activeresource (= 3.2.19)
71
+ activesupport (= 3.2.19)
72
+ bundler (~> 1.0)
73
+ railties (= 3.2.19)
74
+ railties (3.2.19)
75
+ actionpack (= 3.2.19)
76
+ activesupport (= 3.2.19)
77
+ rack-ssl (~> 1.3.2)
78
+ rake (>= 0.8.7)
79
+ rdoc (~> 3.4)
80
+ thor (>= 0.14.6, < 2.0)
81
+ rake (10.3.2)
82
+ rdoc (3.12.2)
83
+ json (~> 1.4)
84
+ rspec-core (2.14.8)
85
+ rspec-expectations (2.14.5)
86
+ diff-lcs (>= 1.1.3, < 2.0)
87
+ rspec-mocks (2.14.6)
88
+ rspec-rails (2.14.2)
89
+ actionpack (>= 3.0)
90
+ activemodel (>= 3.0)
91
+ activesupport (>= 3.0)
92
+ railties (>= 3.0)
93
+ rspec-core (~> 2.14.0)
94
+ rspec-expectations (~> 2.14.0)
95
+ rspec-mocks (~> 2.14.0)
96
+ slop (3.6.0)
97
+ sprockets (2.2.2)
98
+ hike (~> 1.2)
99
+ multi_json (~> 1.0)
100
+ rack (~> 1.0)
101
+ tilt (~> 1.1, != 1.3.0)
102
+ thor (0.19.1)
103
+ tilt (1.4.1)
104
+ treetop (1.4.15)
105
+ polyglot
106
+ polyglot (>= 0.3.1)
107
+ tzinfo (0.3.41)
108
+
109
+ PLATFORMS
110
+ ruby
111
+
112
+ DEPENDENCIES
113
+ awesome_print (~> 1.2)
114
+ pg (~> 0.17)
115
+ pg_morph!
116
+ pry (~> 0.10)
117
+ rails (~> 3)
118
+ rake (~> 10.3)
119
+ rspec-rails
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Lunar Logic
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # PgMorph
2
+ # ![PgMorph logo](docs/pg_morph.png)
3
+
4
+ PgMorph gives you a way to handle DB consistency for polymorphic relations and is based on postgreSQL inheritance and partitioning features.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'pg_morph'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ ```console
17
+ $ bundle
18
+ ```
19
+
20
+ Or install it yourself as:
21
+
22
+ ```console
23
+ $ gem install pg_morph
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ Let's say you have a `Like` class and it's in polymorphic relation with `Post` and `Comment` classes. You can't add foreign keys for those relations, and there's where PgMorph comes.
29
+
30
+ By adding migration:
31
+
32
+ ```ruby
33
+ add_polymorphic_foreign_key :likes, :comments, column: :likeable
34
+ ```
35
+
36
+ PgMorph creates a partition table named `likes_comments` which inherits from `likes` table, sets foreign key on it and redirects all inserts to `likes` to this partition table if `likeable_type` is `Like`. It's done by using before insert trigger.
37
+
38
+ You will have to add polymorphic foreign key on all related tables and each time new relation is added, before insert trigger function will be updated to reflect all defined relations and redirect new records to proper partitions.
39
+
40
+ From the Rails point of view it's totally transparent, so all inserts, updates and selections work as they were on original `likes` table.
41
+
42
+ You can remove polymorphic foreign keys with below migration:
43
+
44
+ ```ruby
45
+ remove_polymorphic_foreign_key :likes, :comments, column: :likeable
46
+ ```
47
+
48
+ Because it means that whole partition table would be removed, you will be forbidden to do that if partition table contains any data.
49
+
50
+ ## Issues
51
+
52
+ ActiveRecord uses `INSERT ... RETURNING id` query which was impossible to keep while while using regular tables without some trick. In ideal situation there should be inly one insert to partition table, omitting main table, but than `id` of newly created record would become `nil` which would frustrate most of us. To preserve `id` of new record main table is not omitted, two records are being made and in after insert trigger duplicated record from master table is removed.
53
+
54
+ This extra database operations may be skipped by using view for main table, however it requires more work to make it so transparent for ActiveRecord as it is now, and is going to be done in next release.
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork it
59
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
60
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
61
+ 4. Push to the branch (`git push origin my-new-feature`)
62
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ desc 'test pg_morph'
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.pattern = 'test/**/*_test.rb'
7
+ t.verbose = true
8
+ end
data/docs/pg_morph.png ADDED
Binary file
@@ -0,0 +1,39 @@
1
+ module PgMorph
2
+
3
+ module Adapter
4
+
5
+ def add_polymorphic_foreign_key(parent_table, child_table, options = {})
6
+ raise_unless_postgres
7
+
8
+ polymorphic = PgMorph::Polymorphic.new(parent_table, child_table, options)
9
+
10
+ sql = polymorphic.create_proxy_table_sql
11
+ sql << polymorphic.create_before_insert_trigger_fun_sql
12
+ sql << polymorphic.create_before_insert_trigger_sql
13
+ sql << polymorphic.create_after_insert_trigger_fun_sql
14
+ sql << polymorphic.create_after_insert_trigger_sql
15
+
16
+ execute(sql)
17
+ end
18
+
19
+ def remove_polymorphic_foreign_key(parent_table, child_table, options = {})
20
+ raise_unless_postgres
21
+
22
+ polymorphic = PgMorph::Polymorphic.new(parent_table, child_table, options)
23
+
24
+ sql = polymorphic.remove_before_insert_trigger_sql
25
+ sql << polymorphic.remove_partition_table
26
+ sql << polymorphic.remove_after_insert_trigger_sql
27
+
28
+ execute(sql)
29
+ end
30
+
31
+ private
32
+
33
+ def raise_unless_postgres
34
+ raise PgMorph::Exception.new("This functionality is supported only by PostgreSQL") unless self.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,37 @@
1
+ module PgMorph
2
+ module Naming
3
+
4
+ def type
5
+ child_table.to_s.singularize.camelize
6
+ end
7
+
8
+ def column_name_type
9
+ "#{column_name}_type"
10
+ end
11
+
12
+ def column_name_id
13
+ "#{column_name}_id"
14
+ end
15
+
16
+ def proxy_table
17
+ "#{parent_table}_#{child_table}"
18
+ end
19
+
20
+ def before_insert_fun_name
21
+ "#{parent_table}_#{column_name}_fun"
22
+ end
23
+
24
+ def before_insert_trigger_name
25
+ "#{parent_table}_#{column_name}_insert_trigger"
26
+ end
27
+
28
+ def after_insert_fun_name
29
+ "delete_from_#{parent_table}_master_fun"
30
+ end
31
+
32
+ def after_insert_trigger_name
33
+ "#{parent_table}_after_insert_trigger"
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,158 @@
1
+ module PgMorph
2
+
3
+ class Polymorphic
4
+ include PgMorph::Naming
5
+ attr_reader :parent_table, :child_table, :column_name
6
+
7
+ def initialize(parent_table, child_table, options)
8
+ @parent_table = parent_table
9
+ @child_table = child_table
10
+ @column_name = options[:column]
11
+
12
+ raise PgMorph::Exception.new("Column not specified") unless @column_name
13
+ end
14
+
15
+ def create_proxy_table_sql
16
+ %Q{
17
+ CREATE TABLE #{proxy_table} (
18
+ CHECK (#{column_name_type} = '#{type}'),
19
+ PRIMARY KEY (id),
20
+ FOREIGN KEY (#{column_name_id}) REFERENCES #{child_table}(id)
21
+ ) INHERITS (#{parent_table});
22
+ }
23
+ end
24
+
25
+ def create_before_insert_trigger_fun_sql
26
+ before_insert_trigger_content do
27
+ create_trigger_body.strip
28
+ end
29
+ end
30
+
31
+ def create_before_insert_trigger_sql
32
+ fun_name = before_insert_fun_name
33
+ trigger_name = before_insert_trigger_name
34
+
35
+ create_trigger_sql(parent_table, trigger_name, fun_name, 'BEFORE INSERT')
36
+ end
37
+
38
+ def create_after_insert_trigger_sql
39
+ fun_name = after_insert_fun_name
40
+ trigger_name = after_insert_trigger_name
41
+
42
+ create_trigger_sql(parent_table, trigger_name, fun_name, 'AFTER INSERT')
43
+ end
44
+
45
+ def create_after_insert_trigger_fun_sql
46
+ fun_name = after_insert_fun_name
47
+ create_trigger_fun(fun_name) do
48
+ %Q{DELETE FROM ONLY #{parent_table} WHERE id = NEW.id;}
49
+ end
50
+ end
51
+
52
+ def remove_before_insert_trigger_sql
53
+ trigger_name = before_insert_trigger_name
54
+ fun_name = before_insert_fun_name
55
+
56
+ prosrc = get_function(fun_name)
57
+ raise PG::Error.new("There is no such function #{fun_name}()\n") unless prosrc
58
+
59
+ scan = prosrc.scan(/(( +(ELS)?IF.+\n)(\s+INSERT INTO.+;\n))/)
60
+ cleared = scan.reject { |x| x[0].match("#{proxy_table}") }
61
+
62
+ if cleared.present?
63
+ cleared[0][0].sub!('ELSIF', 'IF')
64
+ before_insert_trigger_content do
65
+ cleared.map { |m| m[0] }.join('').strip
66
+ end
67
+ else
68
+ drop_trigger_and_fun_sql(trigger_name, parent_table, fun_name)
69
+ end
70
+ end
71
+
72
+ def remove_partition_table
73
+ table_empty = ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM #{parent_table}_#{child_table}").to_i.zero?
74
+ if table_empty
75
+ %Q{ DROP TABLE IF EXISTS #{proxy_table}; }
76
+ else
77
+ raise PG::Error.new("Partition table #{proxy_table} contains data.\nRemove them before if you want to drop that table.\n")
78
+ end
79
+ end
80
+
81
+ def remove_after_insert_trigger_sql
82
+ prosrc = get_function(before_insert_fun_name)
83
+ scan = prosrc.scan(/(( +(ELS)?IF.+\n)(\s+INSERT INTO.+;\n))/)
84
+ cleared = scan.reject { |x| x[0].match("#{proxy_table}") }
85
+
86
+ return '' if cleared.present?
87
+ fun_name = after_insert_fun_name
88
+ trigger_name = after_insert_trigger_name
89
+
90
+ drop_trigger_and_fun_sql(trigger_name, parent_table, fun_name)
91
+ end
92
+
93
+ private
94
+
95
+ def create_trigger_fun(fun_name, &block)
96
+ %Q{
97
+ CREATE OR REPLACE FUNCTION #{fun_name}() RETURNS TRIGGER AS $$
98
+ BEGIN
99
+ #{block.call}
100
+ RETURN NEW;
101
+ END; $$ LANGUAGE plpgsql;
102
+ }
103
+ end
104
+
105
+ def before_insert_trigger_content( &block)
106
+ create_trigger_fun(before_insert_fun_name) do
107
+ %Q{#{block.call}
108
+ ELSE
109
+ RAISE EXCEPTION 'Wrong "#{column_name}_type"="%" used. Create proper partition table and update #{before_insert_fun_name} function', NEW.#{column_name}_type;
110
+ END IF;}
111
+ end
112
+ end
113
+
114
+ def create_trigger_body
115
+ prosrc = get_function(before_insert_fun_name)
116
+
117
+ if prosrc
118
+ scan = prosrc.scan(/(( +(ELS)?IF.+\n)(\s+INSERT INTO.+;\n))/)
119
+ raise PG::Error.new("Condition for #{proxy_table} table already exists in trigger function") if scan[0][0].match proxy_table
120
+ %Q{
121
+ #{scan.map { |m| m[0] }.join.strip}
122
+ ELSIF (NEW.#{column_name}_type = '#{child_table.to_s.singularize.camelize}') THEN
123
+ INSERT INTO #{parent_table}_#{child_table} VALUES (NEW.*);
124
+ }
125
+ else
126
+ %Q{
127
+ IF (NEW.#{column_name}_type = '#{child_table.to_s.singularize.camelize}') THEN
128
+ INSERT INTO #{parent_table}_#{child_table} VALUES (NEW.*);
129
+ }
130
+ end
131
+ end
132
+
133
+ def create_trigger_sql(parent_table, trigger_name, fun_name, when_to_call)
134
+ %Q{
135
+ DROP TRIGGER IF EXISTS #{trigger_name} ON #{parent_table};
136
+ CREATE TRIGGER #{trigger_name}
137
+ #{when_to_call} ON #{parent_table}
138
+ FOR EACH ROW EXECUTE PROCEDURE #{fun_name}();
139
+ }
140
+ end
141
+
142
+ def drop_trigger_and_fun_sql(trigger_name, parent_table, fun_name)
143
+ %Q{
144
+ DROP TRIGGER #{trigger_name} ON #{parent_table};
145
+ DROP FUNCTION #{fun_name}();
146
+ }
147
+ end
148
+
149
+ def get_function(fun_name)
150
+ run("SELECT prosrc FROM pg_proc WHERE proname = '#{fun_name}'")
151
+ end
152
+
153
+ def run(query)
154
+ ActiveRecord::Base.connection.select_value(query)
155
+ end
156
+ end
157
+
158
+ end
@@ -0,0 +1,11 @@
1
+ module PgMorph
2
+ class Railtie < Rails::Railtie
3
+ initializer 'PgMorph.active_record' do
4
+ ActiveSupport.on_load :active_record do
5
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
6
+ include PgMorph::Adapter
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module PgMorph
2
+ VERSION = "0.3.0"
3
+ end
data/lib/pg_morph.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'bundler/setup'
2
+ Bundler.require :default, :test
3
+
4
+ require 'active_support'
5
+ require 'active_record'
6
+
7
+ require File.join(File.dirname(__FILE__), %w{ pg_morph naming })
8
+ require File.join(File.dirname(__FILE__), %w{ pg_morph polymorphic })
9
+ require File.join(File.dirname(__FILE__), %w{ pg_morph adapter })
10
+
11
+ require File.join(File.dirname(__FILE__), %w{ pg_morph railtie }) if defined?(Rails)
12
+
13
+ module PgMorph
14
+ class Exception < RuntimeError
15
+ end
16
+ end
data/pg_morph.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pg_morph/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pg_morph"
8
+ spec.version = PgMorph::VERSION
9
+ spec.authors = ["Hanka Seweryn"]
10
+ spec.email = ["hanka@lunarlogic.io"]
11
+ spec.summary = %q{Takes care of postgres DB consistency for ActiveRecord polymorphic associations}
12
+ spec.description = %q{Takes care of postgres DB consistency for ActiveRecord polymorphic associations via partitioning and inheritance}
13
+ spec.homepage = "https://github.com/LunarLogic/pg_morph"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activerecord", "~> 3"
22
+
23
+ spec.add_development_dependency "rails", "~> 3"
24
+ spec.add_development_dependency "pg", "~> 0.17"
25
+
26
+ spec.add_development_dependency 'rspec-rails'
27
+
28
+ spec.add_development_dependency "rake", "~> 10.3"
29
+ spec.add_development_dependency "pry", "~> 0.10"
30
+ spec.add_development_dependency "awesome_print", "~> 1.2"
31
+ end
data/spec/dummy/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Dummy::Application.load_tasks
File without changes
@@ -0,0 +1,3 @@
1
+ class Comment < ActiveRecord::Base
2
+ has_many :likes, as: :likeable
3
+ end
@@ -0,0 +1,3 @@
1
+ class Like < ActiveRecord::Base
2
+ belongs_to :likeable, polymorphic: true
3
+ end
@@ -0,0 +1,3 @@
1
+ class Post < ActiveRecord::Base
2
+ has_many :likes, as: :likeable
3
+ end
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
+ load Gem.bin_path('bundler', 'bundle')
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
3
+ require_relative '../config/boot'
4
+ require 'rails/commands'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../config/boot'
3
+ require 'rake'
4
+ Rake.application.run
@@ -0,0 +1,24 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ require 'rails/all'
4
+
5
+ # Require the gems listed in Gemfile, including any gems
6
+ # you've limited to :test, :development, or :production.
7
+ Bundler.require(*Rails.groups)
8
+ require 'pg_morph'
9
+
10
+ module Dummy
11
+ class Application < Rails::Application
12
+ # Settings in config/environments/* take precedence over those specified here.
13
+ # Application configuration should go into files in config/initializers
14
+ # -- all .rb files in that directory are automatically loaded.
15
+
16
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
17
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
18
+ # config.time_zone = 'Central Time (US & Canada)'
19
+
20
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
21
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
22
+ # config.i18n.default_locale = :de
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ # Set up gems listed in the Gemfile.
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
+
4
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
@@ -0,0 +1,8 @@
1
+ test:
2
+ adapter: postgresql
3
+ encoding: unicode
4
+ database: pg_morph_test
5
+ pool: 5
6
+ username: postgres
7
+ password: test123
8
+ host: localhost
@@ -0,0 +1,8 @@
1
+ test:
2
+ adapter: postgresql
3
+ encoding: unicode
4
+ database: pg_morph_test
5
+ pool: 5
6
+ username: postgres
7
+ password: test123
8
+ host: localhost
@@ -0,0 +1,5 @@
1
+ # Load the Rails application.
2
+ require File.expand_path('../application', __FILE__)
3
+
4
+ # Initialize the Rails application.
5
+ Dummy::Application.initialize!
@@ -0,0 +1,36 @@
1
+ Dummy::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb.
3
+
4
+ # The test environment is used exclusively to run your application's
5
+ # test suite. You never need to work with it otherwise. Remember that
6
+ # your test database is "scratch space" for the test suite and is wiped
7
+ # and recreated between test runs. Don't rely on the data there!
8
+ config.cache_classes = true
9
+
10
+ # Do not eager load code on boot. This avoids loading your whole application
11
+ # just for the purpose of running a single test. If you are using a tool that
12
+ # preloads Rails for running tests, you may have to set it to true.
13
+ config.eager_load = false
14
+
15
+ # Configure static asset server for tests with Cache-Control for performance.
16
+ config.serve_static_assets = true
17
+ config.static_cache_control = "public, max-age=3600"
18
+
19
+ # Show full error reports and disable caching.
20
+ config.consider_all_requests_local = true
21
+ config.action_controller.perform_caching = false
22
+
23
+ # Raise exceptions instead of rendering exception templates.
24
+ config.action_dispatch.show_exceptions = false
25
+
26
+ # Disable request forgery protection in test environment.
27
+ config.action_controller.allow_forgery_protection = false
28
+
29
+ # Tell Action Mailer not to deliver emails to the real world.
30
+ # The :test delivery method accumulates sent emails in the
31
+ # ActionMailer::Base.deliveries array.
32
+ config.action_mailer.delivery_method = :test
33
+
34
+ # Print deprecation notices to the stderr.
35
+ config.active_support.deprecation = :stderr
36
+ end