secondbase 0.6.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +16 -0
  4. data/.yardopts +1 -0
  5. data/Appraisals +12 -0
  6. data/CHANGELOG.md +18 -6
  7. data/Gemfile +3 -12
  8. data/Guardfile +16 -0
  9. data/LICENSE.txt +2 -1
  10. data/README.md +144 -0
  11. data/Rakefile +18 -41
  12. data/VERSION +1 -1
  13. data/gemfiles/rails40.gemfile +8 -0
  14. data/gemfiles/rails41.gemfile +8 -0
  15. data/gemfiles/rails42.gemfile +8 -0
  16. data/lib/rails/second_base/generators/migration_generator.rb +25 -0
  17. data/lib/second_base.rb +20 -0
  18. data/lib/second_base/base.rb +8 -0
  19. data/lib/second_base/databases.rake +127 -0
  20. data/lib/second_base/forced.rb +21 -0
  21. data/lib/second_base/on_base.rb +36 -0
  22. data/lib/second_base/railtie.rb +40 -0
  23. data/lib/second_base/test_help.rb +11 -0
  24. data/lib/second_base/version.rb +3 -0
  25. data/lib/secondbase.rb +1 -46
  26. data/secondbase.gemspec +25 -75
  27. data/test/cases/dbtask_test.rb +225 -0
  28. data/test/cases/forced_test.rb +49 -0
  29. data/test/cases/generator_test.rb +41 -0
  30. data/test/cases/on_base_test.rb +35 -0
  31. data/test/cases/railtie_test.rb +31 -0
  32. data/test/dummy_app/Rakefile +2 -0
  33. data/test/dummy_app/app/controllers/application_controller.rb +7 -0
  34. data/test/dummy_app/app/helpers/application_helper.rb +3 -0
  35. data/test/dummy_app/app/models/comment.rb +6 -0
  36. data/test/dummy_app/app/models/comment_forced.rb +6 -0
  37. data/test/dummy_app/app/models/post.rb +7 -0
  38. data/test/dummy_app/app/models/user.rb +6 -0
  39. data/test/dummy_app/bin/rails +5 -0
  40. data/test/dummy_app/config/database.yml +14 -0
  41. data/test/dummy_app/config/routes.rb +3 -0
  42. data/test/dummy_app/db/migrate/20141209165002_create_users.rb +11 -0
  43. data/test/dummy_app/db/migrate/20141214142700_create_posts.rb +12 -0
  44. data/test/dummy_app/db/secondbase/migrate/20151202075826_create_comments.rb +11 -0
  45. data/test/dummy_app/init.rb +40 -0
  46. data/test/dummy_app/log/.keep +0 -0
  47. data/test/dummy_app/tmp/.keep +0 -0
  48. data/test/test_helper.rb +36 -0
  49. data/test/test_helpers/dummy_app_helpers.rb +90 -0
  50. data/test/test_helpers/rails_version_helpers.rb +29 -0
  51. data/test/test_helpers/stream_helpers.rb +40 -0
  52. metadata +220 -139
  53. data/.document +0 -5
  54. data/Gemfile.lock +0 -38
  55. data/README.rdoc +0 -143
  56. data/lib/generators/secondbase/USAGE +0 -19
  57. data/lib/generators/secondbase/migration_generator.rb +0 -36
  58. data/lib/generators/secondbase/templates/migration.rb +0 -13
  59. data/lib/secondbase/active_record/associations/has_and_belongs_to_many_association.rb +0 -66
  60. data/lib/secondbase/active_record/base.rb +0 -13
  61. data/lib/secondbase/active_record/fixtures.rb +0 -66
  62. data/lib/secondbase/active_record/patches.rb +0 -13
  63. data/lib/secondbase/active_record/test_fixtures.rb +0 -32
  64. data/lib/secondbase/model.rb +0 -11
  65. data/lib/secondbase/railtie.rb +0 -10
  66. data/lib/secondbase/rake_method_chain.rb +0 -22
  67. data/lib/secondbase/tasks.rb +0 -219
  68. data/rails_generators/secondbase/USAGE +0 -29
  69. data/rails_generators/secondbase/secondbase_migration_generator.rb +0 -20
  70. data/rails_generators/secondbase/templates/migration.rb +0 -15
  71. data/test/helper.rb +0 -18
  72. data/test/test_secondbase.rb +0 -7
data/.document DELETED
@@ -1,5 +0,0 @@
1
- lib/**/*.rb
2
- bin/*
3
- -
4
- features/**/*.feature
5
- LICENSE.txt
data/Gemfile.lock DELETED
@@ -1,38 +0,0 @@
1
- GEM
2
- remote: http://rubygems.org/
3
- specs:
4
- activemodel (3.0.0)
5
- activesupport (= 3.0.0)
6
- builder (~> 2.1.2)
7
- i18n (~> 0.4.1)
8
- activerecord (3.0.0)
9
- activemodel (= 3.0.0)
10
- activesupport (= 3.0.0)
11
- arel (~> 1.0.0)
12
- tzinfo (~> 0.3.23)
13
- activesupport (3.0.0)
14
- arel (1.0.1)
15
- activesupport (~> 3.0.0)
16
- builder (2.1.2)
17
- git (1.2.5)
18
- i18n (0.4.1)
19
- jeweler (1.8.4)
20
- bundler (~> 1.0)
21
- git (>= 1.2.5)
22
- rake
23
- rdoc
24
- json (1.7.5)
25
- rake (0.9.2.2)
26
- rdoc (3.12)
27
- json (~> 1.4)
28
- shoulda (2.11.1)
29
- tzinfo (0.3.23)
30
-
31
- PLATFORMS
32
- ruby
33
-
34
- DEPENDENCIES
35
- activerecord (~> 3.0.0)
36
- bundler (>= 1.0.0)
37
- jeweler (~> 1.8.2)
38
- shoulda
data/README.rdoc DELETED
@@ -1,143 +0,0 @@
1
- = secondbase
2
-
3
- SecondBase adds a second database to your application. While Rails enables you to establish connections to as many external databases as you like, Rails can only manage a single database with it's migration and testing tasks.
4
-
5
- SecondBase enables Rails to work with, and manage, a second database (almost) transparently. As a developer, you should not even realize a second database is in play. Core rake tasks such as rake db:create, rake db:migrate, and rake test will continue to work seamlessly with both databases without you, the developer, having to run any extra rake tasks.
6
-
7
- SecondBase works with Rails 2.3.x and 3.0.x. I've not tried to use SecondBase with Rails 3.1 yet, although someone has submitted a pull request fixing a 3.1 issue, so I assume it's working OK?
8
-
9
- == Contributing to SecondBase
10
-
11
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
12
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
13
- * Fork the project
14
- * Start a feature/bugfix branch
15
- * Commit and push until you are happy with your contribution
16
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
17
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
18
-
19
- == System Requirements
20
- SecondBase now supports Rails 2.x and Rails 3.x.
21
-
22
- == Installation
23
- Modify your Gemfile to include SecondBase:
24
-
25
- gem 'secondbase', '0.5.0'
26
-
27
- Run `bundle install`. You thought it would be harder? If you're using Rails 2.x, then yes, a little bit harder. You must also add this to your Rakefile:
28
-
29
- require 'secondbase/tasks' if defined?(SecondBase)
30
-
31
- PLEASE NOTE that if you are using bundler with Rails 2.x, then you simply need to add this to your Rakefile:
32
-
33
- require 'secondbase/tasks'
34
-
35
- == Usage
36
- === Database
37
- Configure your database.yml to define your SecondBase:
38
-
39
- # Your normal rails definitions...
40
- development:
41
- adapter: mysql #postgres, oracle, etc
42
- encoding: utf8
43
- database: development
44
-
45
- test:
46
- adapter: mysql #postgres, oracle, etc
47
- encoding: utf8
48
- database: test
49
-
50
- # Your secondbase database configurations...
51
- secondbase:
52
- development:
53
- adapter: mysql
54
- encoding: utf8
55
- database: secondbase_development
56
-
57
- test:
58
- adapter: mysql
59
- encoding: utf8
60
- database: secondbase_test
61
-
62
-
63
- === Migrations
64
- SecondBase comes with a generator to assist in managing your migrations
65
-
66
- Rails 3.x:
67
- rails generator secondbase:migration CreateWidgetsTable
68
-
69
- Rails 2.x:
70
- script/generate secondbase_migration CreateWidgetsTable
71
-
72
- The generator will organize your second (data)base migrations alongside of your primary database. The above command will generate the file:
73
-
74
- db/migrate/secondbase/20101203211338_create_widgets_table.rb
75
-
76
- To run your migrations, simply run:
77
-
78
- rake db:migrate
79
-
80
- This will migrate your first and second (data)bases. If, for some reason, you only want to migrate your second (data)base, run:
81
-
82
- rake db:migrate:secondbase
83
-
84
- Please note that migrating up and migrating down must be done specifically on your first or second (data)base. As usual, to migrate your first (data)base up or down to version 20101203211338, you could run:
85
-
86
- rake db:migrate:up VERSION=20101005311335
87
- rake db:migrate:down VERSION=20101005311335
88
-
89
- To migrate your second (data)base up or down to version 20101203211338, you would run:
90
-
91
- rake db:migrate:up:secondbase VERSION=20101203211338
92
- rake db:migrate:down:secondbase VERSION=20101203211338
93
-
94
-
95
- === Models
96
- Every model in your project that extends ActiveRecord::Base will point to the database defined by Rails.env. This is the default Rails behavior and should be of no surprise to you. So how do we point our models to the second (data)base?
97
-
98
- SecondBase offers a base model that you can simply extend:
99
-
100
- require 'secondbase/model'
101
-
102
- class Widget < SecondBase::Base
103
- # you're Widget model is now pointing to your second (data)base table 'widgets'
104
- end
105
-
106
- ActiveRecord associations will still work between your Firstbase and SecondBase models!
107
-
108
- # Notice how normal this all looks...
109
- class User < ActiveRecord::Base
110
- has_many :widgets
111
- end
112
-
113
- === Rake Tasks & Custom Classes
114
- If you need to write rake tasks, or some other code that does not extend ActiveRecord, you can simply establish a connection to your second (data)base:
115
-
116
- SecondBase::has_runner(Rails.env)
117
-
118
- Please note that this is equivalent to using ActiveRecord::Base.establish_connection(config) and will reset the base connection of your ENTIRE application. No worries, to move the runner back to first you can use:
119
-
120
- FirstBase::has_runner(Rails.env)
121
-
122
- === Testing
123
- Tests can still be run using `rake test` or `rake test:units`, etc. However, if you are using fixtures, you will need to update your TestHelper class to include:
124
-
125
- require 'secondbase/fixtures'
126
-
127
- This is patch to fixtures that will identify the fixtures which belong to models that extend SecondBase::Base. The patch will then ensure that the table descendants of SecondBase::Base get loaded into your second test (data)base.
128
-
129
- At this time, I can verify that SecondBase works with Fixtures, Machinist and FactoryGirl. Conceivably, other test factories should work, but there is currently no support for this. If you have the time to update this gem to be test object compatible, by all means...
130
-
131
- == TODO
132
- - Migration generator in Rails 3.x needs support for attribute generation (similar to rails generate migration). For example:
133
- `rails generate secondbase:migration AddTitleBodyToPost title:string body:text published:boolean`
134
-
135
- - rake db:fixtures:load is currently broken. Like many other things I have fixed, it assumes you only one a single database and attempts to load all fixtures into it. I don't believe we can get away with alias chaining this one, I think (like the Fixtures class), we'll have to freedom patch it.
136
-
137
- - TESTS!! Not 100% sure how to test the rake tasks, but I can definitely write tests for the classes and generators. I did this in my spare time, sorry...
138
-
139
- == Copyright
140
-
141
- Copyright (c) 2010 karledurante. See LICENSE.txt for
142
- further details.
143
-
@@ -1,19 +0,0 @@
1
- Description:
2
- Stubs out a new (second)database migration. Pass the migration name, either
3
- CamelCased or under_scored, and an optional list of attribute pairs as arguments.
4
-
5
- For your organizational convenience, a migration class is generated in
6
- db/migrate/secondbase prefixed by a timestamp of the current date and time.
7
-
8
- You can name your migration in either of these formats to generate add/remove
9
- column lines from supplied attributes: AddColumnsToTable or RemoveColumnsFromTable
10
-
11
- Example:
12
- `rails generate secondbase:migration AddSslFlag`
13
-
14
- If the current date is May 14, 2008 and the current time 09:09:12, this creates the AddSslFlag migration
15
- db/migrate/secondbase/20080514090912_add_ssl_flag.rb
16
-
17
- You can continue to use `rake db:migrate` to migrate your first and second databases, or you can
18
- target secondbase by using `rake db:migrate:secondbase`. Remember, your migrations are timestamped,
19
- so regardless of the database they target, they will be unique and you will not have collision issues.
@@ -1,36 +0,0 @@
1
- require 'rails/generators'
2
- require 'rails/generators/migration'
3
- require 'active_record'
4
-
5
- module Secondbase
6
- class MigrationGenerator < Rails::Generators::NamedBase
7
- include Rails::Generators::Migration
8
-
9
- def self.source_root
10
- File.join(File.dirname(__FILE__), 'templates')
11
- end
12
-
13
- # Implement the required interface for Rails::Generators::Migration.
14
- # taken from http://github.com/rails/rails/blob/master/activerecord/lib/generators/active_record.rb
15
- def self.next_migration_number(dirname) #:nodoc:
16
- if ActiveRecord::Base.timestamped_migrations
17
- Time.now.utc.strftime("%Y%m%d%H%M%S")
18
- else
19
- "%.3d" % (current_migration_number(dirname) + 1)
20
- end
21
- end
22
-
23
- def create_migration_file
24
- migration_template 'migration.rb',
25
- "db/migrate/#{SecondBase::CONNECTION_PREFIX}/#{class_name.underscore}.rb",
26
- :assigns => get_local_assigns
27
- end
28
-
29
- private
30
- # TODO: We need to add support for name/value pairs like title:string dob:date etc..
31
- def get_local_assigns
32
- { :class_name => class_name }
33
- end
34
-
35
- end
36
- end
@@ -1,13 +0,0 @@
1
- class <%= class_name.underscore.camelize %> < ActiveRecord::Migration
2
- ############################################################
3
- # Database migration targeting the SecondBase!
4
- # Generated using: rails generator secondbase:migration [MigrationName]
5
-
6
- def self.up
7
-
8
- end
9
-
10
- def self.down
11
-
12
- end
13
- end
@@ -1,66 +0,0 @@
1
- module ActiveRecord
2
- # = Active Record Has And Belongs To Many Association
3
- module Associations
4
- class HasAndBelongsToManyAssociation < AssociationCollection
5
-
6
- # determines the appropriate engine for the join table's parent klass
7
- def arel_engine
8
- Arel::Sql::Engine.new(@reflection.klass.engine)
9
- end
10
-
11
- # This method is entirely replicated, except for line 25. We simply
12
- # need to pass in the appropriate engine to Arel.
13
- def insert_record(record, force = true, validate = true)
14
- if record.new_record?
15
- if force
16
- record.save!
17
- else
18
- return false unless record.save(:validate => validate)
19
- end
20
- end
21
-
22
- if @reflection.options[:insert_sql]
23
- @owner.connection.insert(interpolate_and_sanitize_sql(@reflection.options[:insert_sql], record))
24
- else
25
- relation = Arel::Table.new(@reflection.options[:join_table], arel_engine)
26
- timestamps = record_timestamp_columns(record)
27
- timezone = record.send(:current_time_from_proper_timezone) if timestamps.any?
28
-
29
- attributes = Hash[columns.map do |column|
30
- name = column.name
31
- value = case name.to_s
32
- when @reflection.primary_key_name.to_s
33
- @owner.id
34
- when @reflection.association_foreign_key.to_s
35
- record.id
36
- when *timestamps
37
- timezone
38
- else
39
- @owner.send(:quote_value, record[name], column) if record.has_attribute?(name)
40
- end
41
- [relation[name], value] unless value.nil?
42
- end]
43
-
44
- relation.insert(attributes)
45
- end
46
-
47
- return true
48
- end
49
-
50
- # This method is entirely replicated, except for line 57. We simply
51
- # need to pass in the appropriate engine to Arel.
52
- def delete_records(records)
53
- if sql = @reflection.options[:delete_sql]
54
- records.each { |record| @owner.connection.delete(interpolate_and_sanitize_sql(sql, record)) }
55
- else
56
-
57
- relation = Arel::Table.new(@reflection.options[:join_table], arel_engine)
58
-
59
- relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
60
- and(relation[@reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
61
- ).delete
62
- end
63
- end
64
- end
65
- end
66
- end
@@ -1,13 +0,0 @@
1
- module ActiveRecord
2
- class Base
3
-
4
- # Arel is concerned with "engines". Normally the engine defaults to the primary
5
- # connection (ActiveRecord::Base). This will let us easily override the engine
6
- # when dealing with Seoncdbase models (deep in ActiveRecord code).
7
- # Since SecondBase::Base inherits from ActiveRecord::Base, this will pass the
8
- # right engine around.
9
- def self.engine
10
- self
11
- end
12
- end
13
- end
@@ -1,66 +0,0 @@
1
- ###########################
2
- # Monkey patch Fixtures
3
- # Fixtures needs to load fixtures into the database defined by the parent class!
4
- #
5
- # I feel like the concepts here could be incorporated directly into Fixtures.
6
- # I mean, they shouldn't be so presumptions to think that every model lives in the
7
- # same database....
8
- class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
9
- def self.create_fixtures(fixtures_directory, table_names, class_names = {})
10
- table_names = [table_names].flatten.map { |n| n.to_s }
11
- table_names.each { |n| class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/') }
12
- connection = block_given? ? yield : ActiveRecord::Base.connection
13
-
14
- # make sure we only load secondbase tables that have fixtures defined...
15
- sb_table_names = SecondBase::Base.send(:descendants).map(&:table_name)
16
- sb_table_names = sb_table_names & table_names
17
- sb_connection = SecondBase::Base.connection
18
-
19
- # filter out the secondbase tables from firstbase, otherwise we'll get SQL errors...
20
- table_names_to_fetch = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) || sb_table_names.include?(table_name) }
21
- fixtures = process_fixture_table_names(table_names_to_fetch, class_names, connection, fixtures_directory)
22
- fixtures = [fixtures] if !fixtures.instance_of?(Array)
23
-
24
- sb_table_names_to_fetch = sb_table_names.reject { |table_name| fixture_is_cached?(sb_connection, table_name)}
25
- sb_fixtures = process_fixture_table_names(sb_table_names_to_fetch, class_names, sb_connection, fixtures_directory)
26
- sb_fixtures = [sb_fixtures] if !sb_fixtures.instance_of?(Array)
27
-
28
- (fixtures + sb_fixtures).compact
29
- end
30
-
31
- def self.process_fixture_table_names(table_names_to_fetch, class_names, connection, fixtures_directory)
32
- fixtures_map = {}
33
- unless table_names_to_fetch.empty?
34
- ActiveRecord::Base.silence do
35
- connection.disable_referential_integrity do
36
- # fixtures_map = {}
37
-
38
- fixtures = table_names_to_fetch.map do |table_name|
39
- obj = Fixtures.new(connection, table_name.tr('/', '_'), class_names[table_name.tr('/', '_').to_sym], File.join(fixtures_directory, table_name))
40
- fixtures_map[table_name] = obj
41
- end
42
-
43
- all_loaded_fixtures.update(fixtures_map)
44
-
45
- connection.transaction(:requires_new => true) do
46
- fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
47
- fixtures.each { |fixture| fixture.insert_fixtures }
48
-
49
- # Cap primary key sequences to max(pk).
50
- if connection.respond_to?(:reset_pk_sequence!)
51
- table_names_to_fetch.each do |table_name|
52
- connection.reset_pk_sequence!(table_name.tr('/', '_'))
53
- end
54
- end
55
- end
56
-
57
- cache_fixtures(connection, fixtures_map)
58
- end
59
- end
60
- end
61
-
62
- table_names_to_fetch = nil if table_names_to_fetch.blank?
63
- cached_fixtures(connection, table_names_to_fetch)
64
- end
65
-
66
- end
@@ -1,13 +0,0 @@
1
- ####################
2
- ## ActiveRecord patches for all versions of rails
3
- require 'secondbase/active_record/base'
4
-
5
-
6
- ####################
7
- ## ActiveRecord patches for specific versions of rails
8
- if Rails.env.test?
9
- require 'secondbase/active_record/fixtures'
10
- require 'secondbase/active_record/test_fixtures'
11
- end
12
-
13
- require 'secondbase/active_record/associations/has_and_belongs_to_many_association'
@@ -1,32 +0,0 @@
1
- ## ActiveRecord::TestFixtures
2
- ## Monkey patch active record's test_fixtures module to manage
3
- ## transactions for the SecondBase
4
- module ActiveRecord
5
- module TestFixtures
6
- alias_method :original_setup_fixtures, :setup_fixtures
7
- alias_method :original_teardown_fixtures, :teardown_fixtures
8
-
9
- def setup_fixtures
10
- original_setup_fixtures
11
- # start tx for secondbase, if required
12
- # Load fixtures once and begin transaction.
13
- if run_in_transaction?
14
- SecondBase::Base.connection.increment_open_transactions
15
- SecondBase::Base.connection.transaction_joinable = false
16
- SecondBase::Base.connection.begin_db_transaction
17
- end
18
- end
19
-
20
- def teardown_fixtures
21
- original_teardown_fixtures
22
-
23
- # Rollback secondbase changes if a transaction is active.
24
- if run_in_transaction? && SecondBase::Base.connection.open_transactions != 0
25
- SecondBase::Base.connection.rollback_db_transaction
26
- SecondBase::Base.connection.decrement_open_transactions
27
- end
28
-
29
- SecondBase::Base.clear_active_connections!
30
- end
31
- end
32
- end