roomer 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/.autotest +17 -0
  2. data/.gitignore +9 -0
  3. data/.rvmrc +2 -0
  4. data/.travis.yml +2 -0
  5. data/Gemfile +31 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +116 -0
  8. data/Rakefile +16 -0
  9. data/TODO.md +10 -0
  10. data/doc/ActiveRecord/Base.html +328 -0
  11. data/doc/ApplicationController.html +116 -0
  12. data/doc/Roomer/Engine.html +116 -0
  13. data/doc/Roomer/Generators/InstallGenerator.html +419 -0
  14. data/doc/Roomer/Generators/SetupGenerator.html +209 -0
  15. data/doc/Roomer/Generators.html +108 -0
  16. data/doc/Roomer/Tools.html +702 -0
  17. data/doc/Roomer/VERSION.html +149 -0
  18. data/doc/Roomer.html +533 -0
  19. data/doc/RoomerCreate.html +121 -0
  20. data/doc/_index.html +232 -0
  21. data/doc/class_list.html +47 -0
  22. data/doc/css/common.css +1 -0
  23. data/doc/css/full_list.css +53 -0
  24. data/doc/css/style.css +320 -0
  25. data/doc/file.README.html +146 -0
  26. data/doc/file_list.html +49 -0
  27. data/doc/frames.html +13 -0
  28. data/doc/index.html +146 -0
  29. data/doc/js/app.js +205 -0
  30. data/doc/js/full_list.js +150 -0
  31. data/doc/js/jquery.js +16 -0
  32. data/doc/method_list.html +182 -0
  33. data/doc/top-level-namespace.html +105 -0
  34. data/lib/generators/roomer/install/install_generator.rb +39 -0
  35. data/lib/generators/roomer/install/templates/README +15 -0
  36. data/lib/generators/roomer/install/templates/roomer.rb +68 -0
  37. data/lib/generators/roomer/migration/migration_generator.rb +35 -0
  38. data/lib/generators/roomer/migration/templates/migration.rb +17 -0
  39. data/lib/generators/roomer/model/model_generator.rb +45 -0
  40. data/lib/generators/roomer/model/templates/migration.rb +16 -0
  41. data/lib/generators/roomer/setup/setup_generator.rb +38 -0
  42. data/lib/generators/roomer/setup/templates/global_migration.rb +20 -0
  43. data/lib/roomer/extensions/controller.rb +36 -0
  44. data/lib/roomer/extensions/model.rb +72 -0
  45. data/lib/roomer/helpers/generator_helper.rb +30 -0
  46. data/lib/roomer/helpers/migration_helper.rb +7 -0
  47. data/lib/roomer/helpers/postgres_helper.rb +75 -0
  48. data/lib/roomer/rails.rb +24 -0
  49. data/lib/roomer/schema.rb +63 -0
  50. data/lib/roomer/schema_dumper.rb +36 -0
  51. data/lib/roomer/tasks/migrate.rake +85 -0
  52. data/lib/roomer/utils.rb +68 -0
  53. data/lib/roomer/version.rb +14 -0
  54. data/lib/roomer.rb +125 -0
  55. data/roomer.gemspec +22 -0
  56. data/test/config.rb +3 -0
  57. data/test/config.yml +7 -0
  58. data/test/generators/install_generator_test.rb +13 -0
  59. data/test/generators/migration_generator_test.rb +33 -0
  60. data/test/generators/model_generator_test.rb +25 -0
  61. data/test/generators/setup_generator_test.rb +25 -0
  62. data/test/rails_app/app/controllers/application_controller.rb +3 -0
  63. data/test/rails_app/app/helpers/application_helper.rb +2 -0
  64. data/test/rails_app/app/models/tenant.rb +12 -0
  65. data/test/rails_app/app/views/layouts/application.html.erb +14 -0
  66. data/test/rails_app/config/application.rb +50 -0
  67. data/test/rails_app/config/boot.rb +6 -0
  68. data/test/rails_app/config/database.yml +2 -0
  69. data/test/rails_app/config/environment.rb +5 -0
  70. data/test/rails_app/config/environments/development.rb +26 -0
  71. data/test/rails_app/config/environments/production.rb +49 -0
  72. data/test/rails_app/config/environments/test.rb +35 -0
  73. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  74. data/test/rails_app/config/initializers/inflections.rb +10 -0
  75. data/test/rails_app/config/initializers/mime_types.rb +5 -0
  76. data/test/rails_app/config/initializers/roomer.rb +68 -0
  77. data/test/rails_app/config/initializers/secret_token.rb +7 -0
  78. data/test/rails_app/config/initializers/session_store.rb +8 -0
  79. data/test/rails_app/config/locales/en.yml +5 -0
  80. data/test/rails_app/config/routes.rb +58 -0
  81. data/test/rails_app/config.ru +4 -0
  82. data/test/rails_app/db/migrate/global/20110825231409_roomer_create_tenants.rb +20 -0
  83. data/test/rails_app/db/seeds.rb +7 -0
  84. data/test/rails_app/lib/tasks/.gitkeep +0 -0
  85. data/test/rails_app/public/404.html +26 -0
  86. data/test/rails_app/public/422.html +26 -0
  87. data/test/rails_app/public/500.html +26 -0
  88. data/test/rails_app/public/favicon.ico +0 -0
  89. data/test/rails_app/public/images/rails.png +0 -0
  90. data/test/rails_app/public/index.html +239 -0
  91. data/test/rails_app/public/javascripts/application.js +2 -0
  92. data/test/rails_app/public/javascripts/controls.js +965 -0
  93. data/test/rails_app/public/javascripts/dragdrop.js +974 -0
  94. data/test/rails_app/public/javascripts/effects.js +1123 -0
  95. data/test/rails_app/public/javascripts/prototype.js +6001 -0
  96. data/test/rails_app/public/javascripts/rails.js +191 -0
  97. data/test/rails_app/public/robots.txt +5 -0
  98. data/test/rails_app/public/stylesheets/.gitkeep +0 -0
  99. data/test/roomer/helpers/postgres_helper_test.rb +62 -0
  100. data/test/roomer/roomer_test.rb +37 -0
  101. data/test/test_helper.rb +23 -0
  102. metadata +228 -0
@@ -0,0 +1,72 @@
1
+ module Roomer
2
+ module Extensions
3
+ module Model
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ # Sets the roomer scope for the model and changes the model's table_name_prefix
10
+ # Sets the table name prefix (schema name) to current_tenant's
11
+ # If :shared is passed, the global schema will be used as the table name prefix
12
+ # if :tenanted is pased, the current tenant's schema will be used as the table name prefix
13
+ # @return [Symbol] :shared or :tenanted
14
+ def roomer(scope)
15
+ case scope
16
+ when :shared
17
+ @roomer_scope = :shared
18
+ when :tenanted
19
+ @roomer_scope = :tenanted
20
+ else
21
+ raise "Invalid roomer model scope. Choose :shared or :tenanted"
22
+ end
23
+ roomer_set_table_name_prefix
24
+ end
25
+
26
+ # Confirms if model is shared
27
+ # @return [True,False]
28
+ def shared?
29
+ @roomer_scope == :shared
30
+ end
31
+
32
+ # Confirms if model is scoped by tenant
33
+ # @return [True,False]
34
+ def tenanted?
35
+ @roomer_scope == :tenanted
36
+ end
37
+
38
+ # Resets model's cached table_name and
39
+ # column information.
40
+ # This method needs to get called whenever
41
+ # the current tenant changes
42
+ def roomer_reset
43
+ roomer_set_table_name_prefix
44
+ reset_table_name
45
+ reset_column_information
46
+ end
47
+
48
+ protected
49
+ # Resolves the full table name prefix
50
+ def roomer_full_table_name_prefix(schema_name)
51
+ "#{schema_name.to_s}#{Roomer.schema_seperator}"
52
+ end
53
+
54
+ # Sets the model's table name prefix to the current tenant's schema name
55
+ # Defaults to public if model is marked as tenanted but tenant table
56
+ # hasn't been populated
57
+ def roomer_set_table_name_prefix
58
+ self.table_name_prefix = begin
59
+ case @roomer_scope
60
+ when :shared
61
+ roomer_full_table_name_prefix(Roomer.shared_schema_name)
62
+ when :tenanted
63
+ roomer_full_table_name_prefix(Roomer.current_tenant.try(Roomer.tenant_schema_name_column))
64
+ else
65
+ ""
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,30 @@
1
+ module Roomer
2
+ module Helpers
3
+ module GeneratorHelper
4
+
5
+ # Check to see if the model file exists, should be used in a Generator
6
+ # @return [True,False]
7
+ def model_exists?
8
+ File.exists?(File.join(destination_root, model_path))
9
+ end
10
+
11
+ # Returns the path of the model
12
+ # @return [String] model_path string representing the model location
13
+ def model_path
14
+ @model_path ||= File.join("app", "models", "#{file_path}.rb")
15
+ end
16
+
17
+ # Reads the --shared option specified when running "rails generate roomer:model"
18
+ # @return [True,False]
19
+ def shared?
20
+ @shared ||= options[:shared]
21
+ end
22
+
23
+ # Fetchs the migration directory for the migrations
24
+ def migration_dir
25
+ return Roomer.shared_migrations_directory if shared?
26
+ return Roomer.tenanted_migrations_directory
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ module Roomer
2
+ module Helpers
3
+ module MigrationHelper
4
+ # Migration Helper Methods go here
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,75 @@
1
+ module Roomer
2
+ module Helpers
3
+ module PostgresHelper
4
+
5
+ # Creates a schema in PostgreSQL
6
+ # @param [#to_s] schema_name declaring the name of the schema
7
+ def create_schema(schema_name)
8
+ ActiveRecord::Base.connection.execute "CREATE SCHEMA \"#{schema_name.to_s}\""
9
+ end
10
+
11
+ # Drops a schema and all it's objects (Cascades)
12
+ # @param [#to_s] schema_name declaring the name of the schema to drop
13
+ def drop_schema(schema_name)
14
+ ActiveRecord::Base.connection.execute("DROP SCHEMA IF EXISTS \"#{schema_name.to_s}\" CASCADE")
15
+ end
16
+
17
+ # lists the schemas available
18
+ # @return [Array] list of schemas
19
+ def schemas
20
+ ActiveRecord::Base.connection.query("SELECT nspname FROM pg_namespace WHERE nspname !~ '^pg_.*'").flatten.map(&:to_s)
21
+ end
22
+
23
+ # Ensures the schema and schema_migrations exist(creates them otherwise)
24
+ # and executes the code block
25
+ # @param [#to_s] schema_name declaring name to ensure
26
+ # @param [#call] &block code to execute
27
+ #
28
+ # Example:
29
+ #
30
+ # ensuring_schema(:global) do
31
+ # ActiveRecord::Migrator.migrate('/db/migrate', '20110812012536')
32
+ # end
33
+ def ensuring_schema(schema_name, &block)
34
+ raise ArgumentError.new("schema_name not present") unless schema_name
35
+ ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
36
+ create_schema(schema_name) unless schemas.include?(schema_name.to_s)
37
+ ensure_prefix(schema_name) do
38
+ ensure_schema_migrations
39
+ yield
40
+ end
41
+ end
42
+
43
+ # Ensures the same ActiveRecord::Base#table_name_prefix for all the
44
+ # models executed in the block
45
+ # @param [#to_s] A Symbol declaring the table name prefix
46
+ # @param [#call] code to execute
47
+ # @note All the Models will have the same prefix, caution is advised
48
+ #
49
+ # Example:
50
+ #
51
+ # ensure_prefix(:global) do
52
+ # Person.find(1) # => will execute "SELECT id FROM 'global.person' where 'id' = 1"
53
+ # end
54
+ def ensure_prefix(prefix, &block)
55
+ @old_prefix = ActiveRecord::Base.table_name_prefix
56
+ ActiveRecord::Base.table_name_prefix = "#{prefix.to_s}#{Roomer.schema_seperator.to_s}"
57
+ yield
58
+ ActiveRecord::Base.table_name_prefix = @old_prefix
59
+ end
60
+
61
+ # Ensures schema_migrations table exists and creates otherwise
62
+ # @see ActiveRecord::Base.connection#initialize_schema_migrations_table
63
+ def ensure_schema_migrations
64
+ ActiveRecord::Base.connection.initialize_schema_migrations_table
65
+ end
66
+
67
+ # Determine if there are any pending migrations in the shared migrations directory
68
+ # @returns true if migrations are pending
69
+ def shared_migrations_pending?
70
+ ActiveRecord::Migrator.new(:up,Roomer.shared_migrations_directory)
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,24 @@
1
+ module Roomer
2
+ class RoomerEngine < ::Rails::Engine
3
+
4
+ initializer 'roomer.extensions' do |app|
5
+ # load model extensions
6
+ ActiveSupport.on_load(:active_record) do
7
+ include Roomer::Extensions::Model
8
+ end
9
+
10
+ # load controller extensions
11
+ ActiveSupport.on_load(:action_controller) do
12
+ include Roomer::Extensions::Controller
13
+ end
14
+ end
15
+
16
+ # load all the rake tasks
17
+ rake_tasks do
18
+ Dir[File.expand_path("../tasks/*.rake", __FILE__)].each do |file|
19
+ load file
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,63 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
3
+ module Roomer
4
+ # Roomer::Schema extends ActiveRecord::Schema
5
+ # Roomer::Schema is currently only supported by the Postgres database adapter
6
+
7
+ class Schema < ActiveRecord::Migration
8
+ extend Roomer::Helpers::PostgresHelper
9
+
10
+ def self.migrations_path(scope=:tenanted)
11
+ case scope
12
+ when :shared
13
+ return Roomer.shared_migrations_directory
14
+ when :tenanted
15
+ return Roomer.tenanted_migrations_directory
16
+ else
17
+ raise 'Invalid scope parameter'
18
+ end
19
+ end
20
+
21
+ def self.dump(scope=:tenanted)
22
+ case scope
23
+ when :shared
24
+ schema_name = Roomer.shared_schema_name.to_s
25
+ filename = Roomer.shared_schema_filename
26
+ when :tenanted
27
+ tenant = Tenant.first
28
+ return if tenant.blank?
29
+
30
+ schema_name = Tenant.first.schema_name.to_s
31
+ filename = Roomer.tenanted_schema_filename
32
+ end
33
+ FileUtils.mkdir_p(Roomer.schemas_directory) unless File.exists?(Roomer.schemas_directory)
34
+ filepath = File.expand_path(File.join(Roomer.schemas_directory, filename))
35
+ ActiveRecord::Base.connection.schema_search_path = schema_name
36
+ ActiveRecord::Base.table_name_prefix = "#{schema_name}."
37
+ Roomer::SchemaDumper.dump(ActiveRecord::Base.connection, File.new(filepath, "w"))
38
+ end
39
+
40
+ def self.load(schema_name, scope=:tenanted)
41
+ filename = begin
42
+ if scope == :shared
43
+ Roomer.shared_schema_filename
44
+ elsif scope == :tenanted
45
+ Roomer.tenanted_schema_filename
46
+ end
47
+ end
48
+ filepath = File.expand_path(File.join(Roomer.schemas_directory, filename))
49
+ return unless File.exists?(filepath)
50
+
51
+ ensuring_schema(schema_name) do
52
+ Object.load(filepath)
53
+ end
54
+ end
55
+
56
+ def self.define(info={}, &block)
57
+ instance_eval(&block)
58
+ unless info[:version].blank?
59
+ ActiveRecord::Base.connection.assume_migrated_upto_version(info[:version], migrations_path)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ module Roomer
2
+ class SchemaDumper < ActiveRecord::SchemaDumper
3
+
4
+ protected
5
+ def header(stream)
6
+ define_params = @version ? ":version => #{@version}" : ""
7
+ stream.puts <<HEADER
8
+ # It's strongly recommended to check this file into your version control system.
9
+
10
+ Roomer::Schema.define(#{define_params}) do
11
+
12
+ HEADER
13
+ end
14
+
15
+ def indexes(table, stream)
16
+ if (indexes = @connection.indexes(table)).any?
17
+ add_index_statements = indexes.map do |index|
18
+ statement_parts = [ ('add_index ' + index.table.inspect) ]
19
+ statement_parts << index.columns.inspect
20
+ statement_parts << (':name => "' + index.columns.first.to_s.split('.').last + '"')
21
+ statement_parts << ':unique => true' if index.unique
22
+
23
+ index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
24
+ if index_lengths.present?
25
+ statement_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect)
26
+ end
27
+
28
+ ' ' + statement_parts.join(', ')
29
+ end
30
+
31
+ stream.puts add_index_statements.sort.join("\n")
32
+ stream.puts
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,85 @@
1
+ include Roomer::Helpers::PostgresHelper
2
+ include Roomer::Helpers::GeneratorHelper
3
+
4
+ namespace :roomer do
5
+ namespace :shared do
6
+ desc "Migrates the shared tables. Target specific version with VERSION=x"
7
+ task :migrate => :environment do
8
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
9
+ ensuring_schema(Roomer.shared_schema_name) do
10
+ ActiveRecord::Migrator.migrate(Roomer.shared_migrations_directory, version)
11
+ end
12
+ Roomer::Schema.dump(:shared)
13
+ end
14
+
15
+ desc "Rolls back shared tables. Target specific version with STEP=x"
16
+ task :rollback => :environment do
17
+ step = ENV['STEP'] ? ENV['STEP'].to_i : 1
18
+ ensuring_schema(Roomer.shared_schema_name) do
19
+ ActiveRecord::Migrator.rollback(Roomer.shared_migrations_directory, step)
20
+ end
21
+ Roomer::Schema.dump(:shared)
22
+ end
23
+
24
+ namespace :schema do
25
+ desc "Load shared schema into database"
26
+ task :load => :environment do
27
+ Roomer::Schema.load(Roomer.shared_schema_name, :shared)
28
+ end
29
+
30
+ desc "Dumps the shared schema to shared/schema.rb. This file can be portably used against any DB supported by AR"
31
+ task :dump => :environment do
32
+ Roomer::Schema.dump(:shared)
33
+ end
34
+ end
35
+ end
36
+
37
+ namespace :tenanted do
38
+ desc "Migrates the tenanted tables. Target specific version with VERSION=x"
39
+ task :migrate => :environment do
40
+ version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
41
+ Roomer.tenant_model.find(:all).each do |tenant|
42
+ ensuring_schema(tenant.try(Roomer.tenant_schema_name_column)) do
43
+ ActiveRecord::Migrator.migrate(Roomer.tenanted_migrations_directory, version)
44
+ end
45
+ end
46
+ Roomer::Schema.dump(:tenanted)
47
+ end
48
+
49
+ desc "Rolls back tenanted tables. Target specific version with STEP=x"
50
+ task :rollback => :environment do
51
+ step = ENV['STEP'] ? ENV['STEP'].to_i : 1
52
+ Roomer.tenant_model.find(:all).each do |tenant|
53
+ ensuring_schema(tenant.try(Roomer.tenant_schema_name_column)) do
54
+ ActiveRecord::Migrator.rollback(Roomer.tenanted_migrations_directory, step)
55
+ end
56
+ end
57
+ Roomer::Schema.dump(:tenanted)
58
+ end
59
+
60
+ namespace :schema do
61
+ desc "Load tenanted schema into database"
62
+ task :load => :environment do
63
+ schema_name = ENV['SCHEMA_NAME']
64
+ raise "No schema name provided. Try: env SCHEMA_NAME=" if schema_name.blank?
65
+ Roomer::Schema.load(schema_name)
66
+ end
67
+
68
+ desc "Dumps tenanted schema file to migrate/schema.rb. This file can be portably used against any DB supported by AR"
69
+ task :dump => :environment do
70
+ Roomer::Schema.dump
71
+ end
72
+ end
73
+ end
74
+
75
+ desc "Runs shared and tenanted migrations"
76
+ task :migrate do
77
+ if ENV["VERSION"].blank?
78
+ Rake::Task["roomer:shared:migrate"].invoke
79
+ Rake::Task["roomer:tenanted:migrate"].invoke
80
+ else
81
+ Rake::Task["roomer:tenanted:migrate"].invoke
82
+ Rake::Task["roomer:shared:migrate"].invoke
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,68 @@
1
+ module Roomer
2
+ module Utils
3
+
4
+ # Rails DB Migrations Directory
5
+ # @return [String] full path to the current migrations directory
6
+ def migrations_directory
7
+ ActiveRecord::Migrator.migrations_path
8
+ end
9
+
10
+ # Directory where the models are stored
11
+ # @return [String] path of the directory where the models are stored
12
+ def model_directory
13
+ File.join("app","models")
14
+ end
15
+
16
+ # Consutructs the full name for the tenants table with schema
17
+ # Example: 'global.tenant'
18
+ # @return [String] full name of the tenant table
19
+ def full_tenants_table_name
20
+ "#{shared_schema_name}#{schema_seperator}#{tenants_table}"
21
+ end
22
+
23
+ # Constructs the full path to the shared schema directory
24
+ # Example: /Users/Greg/Projects/roomer/db/migrate/global
25
+ # @return [String] full path to the shared schema directory
26
+ def full_shared_schema_migration_path
27
+ "#{Rails.root}/#{shared_migrations_directory}"
28
+ end
29
+
30
+ # Returns tenant model as a constant
31
+ # Example: Tenant
32
+ # @return Object
33
+ def tenant_model
34
+ Roomer.tenants_table.to_s.classify.constantize
35
+ end
36
+
37
+ # Sets current tenant from ApplicationController into a Thread
38
+ # local variable. Works only with thread-safe Rails as long as
39
+ # it gets set on every request
40
+ # @return [Symbol] the current tenant key in the thread
41
+ def current_tenant=(val)
42
+ key = :"roomer_current_tenant"
43
+ Thread.current[key] = val
44
+ ensure_tenant_model_reset
45
+ end
46
+
47
+ # Fetches the current tenant
48
+ # @return [Symbol] the current tenant key in the thread
49
+ def current_tenant
50
+ key = :"roomer_current_tenant"
51
+ Thread.current[key]
52
+ end
53
+
54
+ # Reset current tenant
55
+ # @return [Nil]
56
+ def reset_current_tenant
57
+ key = :"roomer_current_tenant"
58
+ Thread.current[key] = nil
59
+ end
60
+
61
+ # Reset cached data in tenanted models
62
+ def ensure_tenant_model_reset
63
+ ActiveRecord::Base.descendants.each do |model|
64
+ model.roomer_reset if model.tenanted?
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,14 @@
1
+ module Roomer
2
+ module VERSION # :nodoc:
3
+ unless defined? MAJOR
4
+ MAJOR = 0
5
+ MINOR = 0
6
+ TINY = 8
7
+ PRE = nil
8
+
9
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
10
+
11
+ SUMMARY = "roomer #{STRING}"
12
+ end
13
+ end
14
+ end
data/lib/roomer.rb ADDED
@@ -0,0 +1,125 @@
1
+ require 'rails'
2
+ require 'active_support'
3
+ require 'active_support/dependencies'
4
+ require 'active_record'
5
+ require 'roomer/version'
6
+
7
+ module ActiveRecord
8
+ autoload :Migration, 'active_record/migration'
9
+ end
10
+
11
+ module Roomer
12
+ autoload :Utils, 'roomer/utils'
13
+
14
+ module Helpers
15
+ autoload :GeneratorHelper, 'roomer/helpers/generator_helper'
16
+ autoload :PostgresHelper, 'roomer/helpers/postgres_helper'
17
+ autoload :MigrationHelper, 'roomer/helpers/migration_helper'
18
+ end
19
+
20
+ module Extensions
21
+ autoload :Model, 'roomer/extensions/model'
22
+ autoload :Controller, 'roomer/extensions/controller'
23
+ end
24
+
25
+ autoload :SchemaDumper, 'roomer/schema_dumper'
26
+ autoload :Schema, 'roomer/schema'
27
+
28
+ extend Utils
29
+
30
+ # The URL routing strategy. Roomer currently supports two routing strategies (:domain and :path)
31
+ # * :domain - Using domain name to identify the tenant. This could include a subdomain
32
+ # * :path - identifying the tenant by the path
33
+ # Example:
34
+ # Domain
35
+ # ------
36
+ # http://mytenant.myapp.com - If you tenant has a subdomain under your domain
37
+ # http://mytenant.com - If the tenant choose to use their own top level domain name
38
+ # http://myapp.mytenant.com If the tenant chooses to use their own subdomain under thier TLD
39
+ # Path
40
+ # ----
41
+ # http://yourapp.com/tenant
42
+ mattr_accessor :url_routing_strategy
43
+ @@url_routing_strategy = :domain
44
+
45
+ # name of the shared schema where all the shared tables are be present
46
+ mattr_accessor :shared_schema_name
47
+ @@shared_schema_name = :global
48
+
49
+ # The name of the table where the tenants are stored, this table must
50
+ # contain two required columns configured under tenant_url_identifier_column and
51
+ # tenant_schema_name_column
52
+ mattr_accessor :tenants_table
53
+ @@tenants_table = :tenants
54
+
55
+ # The column name that stores the url identfier in the tenants tables.
56
+ # A url idenfier is a unique value that identifies the tenant from the URL.
57
+ # For e.g: if you use domains and the url is http://mytenant.myapp.com,
58
+ # the url identifier would "mytenant"
59
+ mattr_accessor :tenant_url_identifier_column
60
+ @@tenant_url_identifier_column = :url_identifier
61
+
62
+ # The column name that strores the schema name in the tenants tables.
63
+ mattr_accessor :tenant_schema_name_column
64
+ @@tenant_schema_name_column = :schema_name
65
+
66
+ # The schema seperator. This is used when generating the table name.
67
+ # The default is set to "."
68
+ # Example: tenant's table by default will be global.tenants
69
+ mattr_accessor :schema_seperator
70
+ @@schema_seperator = '.'
71
+
72
+ # Directory where schema files will be stored.
73
+ mattr_accessor :schemas_directory
74
+ @@schemas_directory = File.expand_path(File.join("db", "schemas"))
75
+
76
+ # Tenanted schema filename.
77
+ mattr_accessor :tenanted_schema_filename
78
+ @@tenanted_schema_filename = "tenanted_schema.rb"
79
+
80
+ # Shared schema filename.
81
+ mattr_accessor :shared_schema_filename
82
+ @@shared_schema_filename = "shared_schema.rb"
83
+
84
+ # Use Tentant migrations directory?
85
+ # Default is set to false
86
+ mattr_accessor :use_tenanted_migrations_directory
87
+ alias_method :use_tenanted_migrations_directory?,
88
+ :use_tenanted_migrations_directory
89
+ @@use_tenanted_migrations_directory = false
90
+
91
+ # Directory where the tenanted migrations are stored.
92
+ # This will be used only if use_tenanted_migration_directory is set to
93
+ # true if not usual rails migraiton directory db/migrate will be used
94
+ mattr_writer :tenanted_migrations_directory
95
+ @@tenanted_migrations_directory = File.join(migrations_directory,tenants_table.to_s)
96
+
97
+
98
+ # Directory where shared migrations are stored.
99
+ mattr_accessor :shared_migrations_directory
100
+ @@shared_migrations_directory = File.join(migrations_directory,shared_schema_name.to_s)
101
+
102
+ # Fetches the migrations directory for Tenanted migrations.
103
+ # returns the standard rails migration directory "db/migrate" is the
104
+ # use_tenanted_migrations_directory is set to false
105
+ # @return [String] String representing the tenanted migrations
106
+ def self.tenanted_migrations_directory
107
+ if self.use_tenanted_migrations_directory
108
+ return @@tenanted_migrations_directory
109
+ end
110
+ return migrations_directory
111
+ end
112
+
113
+ # Default way to setup Roomer. Run rails generate roomer:install to create
114
+ # a fresh initializer with all configuration values.
115
+ # @example
116
+ # Roomer.setup do |config|
117
+ # config.url_routing_strategy = :domain
118
+ # end
119
+ def self.setup
120
+ yield self
121
+ end
122
+
123
+ end
124
+
125
+ require 'roomer/rails'
data/roomer.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "roomer/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "roomer"
7
+ s.version = Roomer::VERSION::STRING
8
+ s.platform = Gem::Platform::RUBY
9
+ s.homepage = "https://github.com/gosuri/roomer"
10
+ s.summary = "Roomer is a multitenant framework for Rails using PostgreSQL"
11
+ s.description = Roomer::VERSION::SUMMARY
12
+
13
+ s.authors = ["Greg Osuri","Daniel Ceballos"]
14
+ s.email = ["gosuri@gmail.com","dceballos@gmail.com"]
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'rails','~> 3.0.9'
22
+ end
data/test/config.rb ADDED
@@ -0,0 +1,3 @@
1
+ require
2
+ TEST_ROOT = File.expand_path(File.dirname(__FILE__))
3
+
data/test/config.yml ADDED
@@ -0,0 +1,7 @@
1
+ postgresql: &postgresql
2
+ adapter: postgresql
3
+ encoding: unicode
4
+ database: roomer_test
5
+ username: postgres
6
+
7
+
@@ -0,0 +1,13 @@
1
+ require "test_helper"
2
+ require "generators/roomer/install/install_generator"
3
+
4
+ class InstallGeneratorTest < Rails::Generators::TestCase
5
+ tests Roomer::Generators::InstallGenerator
6
+ destination File.expand_path("../../tmp", __FILE__)
7
+ setup :prepare_destination
8
+
9
+ test "assert initializer file created" do
10
+ run_generator
11
+ assert_file "config/initializers/roomer.rb"
12
+ end
13
+ end