innkeeper 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.pryrc +3 -0
- data/.travis.yml +15 -0
- data/Appraisals +20 -0
- data/Gemfile +10 -0
- data/Guardfile +24 -0
- data/HISTORY.md +337 -0
- data/README.md +485 -0
- data/Rakefile +92 -0
- data/TODO.md +51 -0
- data/gemfiles/rails_5_1.gemfile +12 -0
- data/innkeeper.gemspec +42 -0
- data/lib/generators/innkeeper/install/USAGE +5 -0
- data/lib/generators/innkeeper/install/install_generator.rb +10 -0
- data/lib/generators/innkeeper/install/templates/innkeeper.rb +76 -0
- data/lib/innkeeper.rb +110 -0
- data/lib/innkeeper/adapters/abstract_adapter.rb +172 -0
- data/lib/innkeeper/adapters/mysql2_adapter.rb +47 -0
- data/lib/innkeeper/adapters/postgresql_adapter.rb +112 -0
- data/lib/innkeeper/console.rb +12 -0
- data/lib/innkeeper/deprecation.rb +10 -0
- data/lib/innkeeper/elevators/domain.rb +20 -0
- data/lib/innkeeper/elevators/generic.rb +32 -0
- data/lib/innkeeper/elevators/host_hash.rb +22 -0
- data/lib/innkeeper/elevators/subdomain.rb +62 -0
- data/lib/innkeeper/migrator.rb +33 -0
- data/lib/innkeeper/railtie.rb +56 -0
- data/lib/innkeeper/resolvers/abstract.rb +15 -0
- data/lib/innkeeper/resolvers/database.rb +11 -0
- data/lib/innkeeper/resolvers/schema.rb +14 -0
- data/lib/innkeeper/tasks/enhancements.rb +36 -0
- data/lib/innkeeper/tenant.rb +47 -0
- data/lib/innkeeper/version.rb +3 -0
- data/lib/tasks/innkeeper.rake +128 -0
- data/notes.md +31 -0
- data/test/config_test.rb +52 -0
- data/test/databases.yml.sample +37 -0
- data/test/decorator_test.rb +21 -0
- data/test/domain_elevator_test.rb +38 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/controllers/application_controller.rb +6 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/company.rb +3 -0
- data/test/dummy/app/models/user.rb +3 -0
- data/test/dummy/app/views/application/index.html.erb +1 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +49 -0
- data/test/dummy/config/boot.rb +11 -0
- data/test/dummy/config/database.yml.sample +38 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +27 -0
- data/test/dummy/config/environments/production.rb +51 -0
- data/test/dummy/config/environments/test.rb +34 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/innkeeper.rb +4 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/db/schema.rb +19 -0
- data/test/dummy/db/seeds.rb +5 -0
- data/test/dummy/db/seeds/import.rb +5 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/stylesheets/.gitkeep +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/excluded_models_test.rb +32 -0
- data/test/generic_elevator_test.rb +63 -0
- data/test/host_hash_elevator_test.rb +42 -0
- data/test/innkeeper_test.rb +96 -0
- data/test/mocks/adapter_mock.rb +11 -0
- data/test/multithreading_test.rb +37 -0
- data/test/mysql2_adapter_test.rb +17 -0
- data/test/postgresql_adapter_test.rb +39 -0
- data/test/railtie_test.rb +31 -0
- data/test/rake_task_test.rb +57 -0
- data/test/resolver_test.rb +21 -0
- data/test/shared/shared_adapter_tests.rb +95 -0
- data/test/subdomain_elevator_test.rb +75 -0
- data/test/test_helper.rb +24 -0
- metadata +325 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 560d0c28790b3fe5ca5be5aaf652504314fc25c3678ceb57759ac443592b11de
|
4
|
+
data.tar.gz: 37cfd45f70289bbda7bfb73ee868d3b379eaf108ac51581f7a5e41e486bbccc0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5bfd05b70e590c273e78249f6c74d859038d1711f4e551e775dc8cd5b176c6020596e0362f12708dd5216d61e630bb14df8e1fa2cd1a3719dbbd6797272eec78
|
7
|
+
data.tar.gz: 4256631c4d73af87b7f38f27493cb81ec4b2837faf8a47955067736bd4d97fe290eadcc08990304dc163da1809eef021e16f7aeb9b97b7f98ae3a62829fcaee8
|
data/.gitignore
ADDED
data/.pryrc
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.2.4
|
4
|
+
- 2.3.1
|
5
|
+
- jruby-9.0.5.0
|
6
|
+
gemfile:
|
7
|
+
- gemfiles/rails_5_1.gemfile
|
8
|
+
bundler_args: --without local
|
9
|
+
before_install:
|
10
|
+
- gem install bundler -v '> 1.5.0'
|
11
|
+
env:
|
12
|
+
RUBY_GC_MALLOC_LIMIT: 90000000
|
13
|
+
RUBY_FREE_MIN: 200000
|
14
|
+
matrix:
|
15
|
+
fast_finish: true
|
data/Appraisals
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
appraise "rails-3-2" do
|
2
|
+
gem "rails", "~> 3.2"
|
3
|
+
gem "test-unit", "~> 3.0"
|
4
|
+
end
|
5
|
+
|
6
|
+
appraise "rails-4-0" do
|
7
|
+
gem "rails", "~> 4.0"
|
8
|
+
end
|
9
|
+
|
10
|
+
appraise "rails-4-1" do
|
11
|
+
gem "rails", "~> 4.1"
|
12
|
+
end
|
13
|
+
|
14
|
+
appraise "rails-4-2" do
|
15
|
+
gem "rails", "~> 4.2"
|
16
|
+
end
|
17
|
+
|
18
|
+
appraise "rails-5-0" do
|
19
|
+
gem "rails", "~> 5.0"
|
20
|
+
end
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :rspec do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/innkeeper/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
7
|
+
watch(%r{^lib/innkeeper/(.+)\.rb$}) { |m| "spec/integration/#{m[1]}_spec.rb" }
|
8
|
+
watch('spec/spec_helper.rb') { "spec" }
|
9
|
+
|
10
|
+
# # Rails example
|
11
|
+
# watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
12
|
+
# watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
13
|
+
# watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
14
|
+
# watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
15
|
+
# watch('config/routes.rb') { "spec/routing" }
|
16
|
+
# watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
17
|
+
|
18
|
+
# # Capybara features specs
|
19
|
+
# watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
20
|
+
|
21
|
+
# # Turnip features and steps
|
22
|
+
# watch(%r{^spec/acceptance/(.+)\.feature$})
|
23
|
+
# watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
24
|
+
end
|
data/HISTORY.md
ADDED
@@ -0,0 +1,337 @@
|
|
1
|
+
# 1.2.0
|
2
|
+
* July 28, 2016
|
3
|
+
|
4
|
+
- Official Rails 5 support
|
5
|
+
|
6
|
+
# 1.1.0
|
7
|
+
* May 26, 2016
|
8
|
+
|
9
|
+
- Reset tenant after each request
|
10
|
+
- [Support callbacks](https://github.com/influitive/apartment/commit/ff9c9d092a781026502f5997c0bbedcb5748bc83) on switch [cbeer]
|
11
|
+
- Preliminary support for [separate database hosts](https://github.com/influitive/apartment/commit/abdffbf8cd9fba87243f16c86390da13e318ee1f) [apneadiving]
|
12
|
+
|
13
|
+
# 1.0.2
|
14
|
+
* July 2, 2015
|
15
|
+
|
16
|
+
- Fix pg_dump env vars - pull/208 [MitinPavel]
|
17
|
+
- Allow custom seed data file - pull/234 [typeoneerror]
|
18
|
+
|
19
|
+
# 1.0.1
|
20
|
+
* April 28, 2015
|
21
|
+
|
22
|
+
- Fix `Apartment::Deprecation` which was rescuing all exceptions
|
23
|
+
|
24
|
+
# 1.0.0
|
25
|
+
* Feb 3, 2015
|
26
|
+
|
27
|
+
- [BREAKING CHANGE] `Apartment::Tenant.process` is deprecated in favour of `Apartment::Tenant.switch`
|
28
|
+
- [BREAKING CHANGE] `Apartment::Tenant.switch` without a block is deprecated in favour of `Apartment::Tenant.switch!`
|
29
|
+
- Raise proper `TenantNotFound`, `TenantExists` exceptions
|
30
|
+
- Deprecate old `SchemaNotFound`, `DatabaseNotFound` exceptions
|
31
|
+
|
32
|
+
# 0.26.1
|
33
|
+
* Jan 13, 2015
|
34
|
+
|
35
|
+
- Fixed [schema quoting bug](https://github.com/influitive/apartment/issues/198#issuecomment-69782651) [jonsgreen]
|
36
|
+
|
37
|
+
# 0.26.0
|
38
|
+
* Jan 5, 2015
|
39
|
+
|
40
|
+
- Rails 4.2 support
|
41
|
+
|
42
|
+
# 0.25.2
|
43
|
+
* Sept 8, 2014
|
44
|
+
|
45
|
+
- Heroku fix on `assets:precompile` - pull/169 [rabbitt]
|
46
|
+
|
47
|
+
# 0.25.1
|
48
|
+
* July 17, 2014
|
49
|
+
|
50
|
+
- Fixed a few vestiges of Apartment::Database
|
51
|
+
|
52
|
+
# 0.25.0
|
53
|
+
* July 3, 2014
|
54
|
+
|
55
|
+
- [BREAKING CHANGE] - `Apartment::Database` is not deprecated in favour of
|
56
|
+
`Apartment::Tenant`
|
57
|
+
- ActiveRecord (and Rails) 4.1 now supported
|
58
|
+
- A new sql based adapter that dumps the schema using sql
|
59
|
+
|
60
|
+
# 0.24.3
|
61
|
+
* March 5, 2014
|
62
|
+
|
63
|
+
- Rake enhancements weren't removed from the generator template
|
64
|
+
|
65
|
+
# 0.24.2
|
66
|
+
* February 24, 2014
|
67
|
+
|
68
|
+
- Better warnings if `apartment:migrate` is run
|
69
|
+
|
70
|
+
# 0.24.1
|
71
|
+
* February 21, 2014
|
72
|
+
|
73
|
+
- requiring `apartment/tasks/enhancements` in an initializer doesn't work
|
74
|
+
- One can disable tenant migrations using `Apartment.db_migrate_tenants = false` in the Rakefile
|
75
|
+
|
76
|
+
# 0.24
|
77
|
+
* February 21, 2014 (In honour of the Women's Gold Medal in Hockey at Sochi)
|
78
|
+
|
79
|
+
- [BREAKING CHANGE] `apartment:migrate` task no longer depends on `db:migrate`
|
80
|
+
- Instead, you can `require 'apartment/tasks/enhancements'` in your Apartment initializer
|
81
|
+
- This will enhance `rake db:migrate` to also run `apartment:migrate`
|
82
|
+
- You can now forget about ever running `apartment:migrate` again
|
83
|
+
- Numerous deprecations for things referencing the word 'database'
|
84
|
+
- This is an ongoing effort to completely replace 'database' with 'tenant' as a better abstraction
|
85
|
+
- Note the obvious `Apartment::Database` still exists but will hopefully become `Apartment::Tenant` soon
|
86
|
+
|
87
|
+
# 0.23.2
|
88
|
+
* January 9, 2014
|
89
|
+
|
90
|
+
- Increased visibility of #parse_database_name warning
|
91
|
+
|
92
|
+
# 0.23.1
|
93
|
+
* January 8, 2014
|
94
|
+
|
95
|
+
- Schema adapters now initialize with default and persistent schemas
|
96
|
+
- Deprecated Apartment::Elevators#parse_database_name
|
97
|
+
|
98
|
+
# 0.23.0
|
99
|
+
* August 21, 2013
|
100
|
+
|
101
|
+
- Subdomain Elevator now allows for exclusions
|
102
|
+
- Delayed::Job has been completely removed
|
103
|
+
|
104
|
+
# 0.22.1
|
105
|
+
* August 21, 2013
|
106
|
+
|
107
|
+
- Fix bug where if your ruby process importing the database schema is run
|
108
|
+
from a directory other than the app root, Apartment wouldn't know what
|
109
|
+
schema_migrations to insert into the database (Rails only)
|
110
|
+
|
111
|
+
# 0.22.0
|
112
|
+
* June 9, 2013
|
113
|
+
|
114
|
+
- Numerous bug fixes:
|
115
|
+
- Mysql reset could connect to wrong database [eric88]
|
116
|
+
- Postgresql schema names weren't quoted properly [gdott9]
|
117
|
+
- Fixed error message on SchemaNotFound in `process`
|
118
|
+
- HostHash elevator allows mapping host based on hash contents [gdott9]
|
119
|
+
- Official Sidekiq support with the [apartment-sidekiq gem](https://github.com/influitive/apartment-sidekiq)
|
120
|
+
|
121
|
+
|
122
|
+
# 0.21.1
|
123
|
+
* May 31, 2013
|
124
|
+
|
125
|
+
- Clearing the AR::QueryCache after switching databases.
|
126
|
+
- Fixes issue with stale model being loaded for schema adapters
|
127
|
+
|
128
|
+
# 0.21.0
|
129
|
+
* April 24, 2013
|
130
|
+
|
131
|
+
- JDBC support!! [PetrolMan]
|
132
|
+
|
133
|
+
# 0.20.0
|
134
|
+
* Feb 6, 2013
|
135
|
+
|
136
|
+
- Mysql now has a 'schema like' option to perform like Postgresql (default)
|
137
|
+
- This should be significantly more performant than using connections
|
138
|
+
- Psych is now supported for Delayed::Job yaml parsing
|
139
|
+
|
140
|
+
# 0.19.2
|
141
|
+
* Jan 30, 2013
|
142
|
+
|
143
|
+
- Database schema file can now be set manually or skipped altogether
|
144
|
+
|
145
|
+
# 0.19.1
|
146
|
+
* Jan 30, 2013
|
147
|
+
|
148
|
+
- Allow schema.rb import file to be specified in config or skip schema.rb import altogether
|
149
|
+
|
150
|
+
# 0.19.0
|
151
|
+
* Dec 29, 2012
|
152
|
+
|
153
|
+
- Apartment is now threadsafe
|
154
|
+
- New postgis adapter [zonpantli]
|
155
|
+
- Removed ActionDispatch dependency for use with Rack apps (regression)
|
156
|
+
|
157
|
+
# 0.18.0
|
158
|
+
* Nov 27, 2012
|
159
|
+
|
160
|
+
- Added `append_environment` config option [virtualstaticvoid]
|
161
|
+
- Cleaned up the readme and generator documentation
|
162
|
+
- Added `connection_class` config option [smashtank]
|
163
|
+
- Fixed a [bug](https://github.com/influitive/apartment/issues/17#issuecomment-10758327) in pg adapter when missing schema
|
164
|
+
|
165
|
+
# 0.17.1
|
166
|
+
* Oct 30, 2012
|
167
|
+
|
168
|
+
- Fixed a bug where switching to an unknown db in mysql2 would crash the app [Frodotus]
|
169
|
+
|
170
|
+
# 0.17.0
|
171
|
+
* Sept 26, 2012
|
172
|
+
|
173
|
+
- Apartment has [a new home!](https://github.com/influitive/apartment)
|
174
|
+
- Support Sidekiq hooks to switch dbs [maedhr]
|
175
|
+
- Allow VERSION to be used on apartment:migrate [Bhavin Kamani]
|
176
|
+
|
177
|
+
# 0.16.0
|
178
|
+
* June 1, 2012
|
179
|
+
|
180
|
+
- Apartment now supports a default_schema to be set, rather than relying on ActiveRecord's default schema_search_path
|
181
|
+
- Additional schemas can always be maintained in the schema_search_path by configuring persistent_schemas [ryanbrunner]
|
182
|
+
- This means Hstore is officially supported!!
|
183
|
+
- There is now a full domain based elevator to switch dbs based on the whole domain [lcowell]
|
184
|
+
- There is now a generic elevator that takes a Proc to switch dbs based on the return value of that proc.
|
185
|
+
|
186
|
+
# 0.15.0
|
187
|
+
* March 18, 2012
|
188
|
+
|
189
|
+
- Remove Rails dependency, Apartment can now be used with any Rack based framework using ActiveRecord
|
190
|
+
|
191
|
+
# 0.14.4
|
192
|
+
* March 8, 2012
|
193
|
+
|
194
|
+
- Delayed::Job Hooks now return to the previous database, rather than resetting
|
195
|
+
|
196
|
+
# 0.14.3
|
197
|
+
* Feb 21, 2012
|
198
|
+
|
199
|
+
- Fix yaml serialization of non DJ models
|
200
|
+
|
201
|
+
# 0.14.2
|
202
|
+
* Feb 21, 2012
|
203
|
+
|
204
|
+
- Fix Delayed::Job yaml encoding with Rails > 3.0.x
|
205
|
+
|
206
|
+
# 0.14.1
|
207
|
+
* Dec 13, 2011
|
208
|
+
|
209
|
+
- Fix ActionDispatch::Callbacks deprecation warnings
|
210
|
+
|
211
|
+
# 0.14.0
|
212
|
+
* Dec 13, 2011
|
213
|
+
|
214
|
+
- Rails 3.1 Support
|
215
|
+
|
216
|
+
# 0.13.1
|
217
|
+
* Nov 8, 2011
|
218
|
+
|
219
|
+
- Reset prepared statement cache for rails 3.1.1 before switching dbs when using postgresql schemas
|
220
|
+
- Only necessary until the next release which will be more schema aware
|
221
|
+
|
222
|
+
# 0.13.0
|
223
|
+
* Oct 25, 2011
|
224
|
+
|
225
|
+
- `process` will now rescue with reset if the previous schema/db is no longer available
|
226
|
+
- `create` now takes an optional block which allows you to process within the newly created db
|
227
|
+
- Fixed Rails version >= 3.0.10 and < 3.1 because there have been significant testing problems with 3.1, next version will hopefully fix this
|
228
|
+
|
229
|
+
# 0.12.0
|
230
|
+
* Oct 4, 2011
|
231
|
+
|
232
|
+
- Added a `drop` method for removing databases/schemas
|
233
|
+
- Refactored abstract adapter to further remove duplication in concrete implementations
|
234
|
+
- Excluded models now take string references so they are properly reloaded in development
|
235
|
+
- Better silencing of `schema.rb` loading using `verbose` flag
|
236
|
+
|
237
|
+
# 0.11.1
|
238
|
+
* Sep 22, 2011
|
239
|
+
|
240
|
+
- Better use of Railties for initializing apartment
|
241
|
+
- The following changes were necessary as I haven't figured out how to properly hook into Rails reloading
|
242
|
+
- Added reloader middleware in development to init Apartment on each request
|
243
|
+
- Override `reload!` in console to also init Apartment
|
244
|
+
|
245
|
+
# 0.11.0
|
246
|
+
* Sep 20, 2011
|
247
|
+
|
248
|
+
- Excluded models no longer use a different connection when using postgresql schemas. Instead their table_name is prefixed with `public.`
|
249
|
+
|
250
|
+
# 0.10.3
|
251
|
+
* Sep 20, 2011
|
252
|
+
|
253
|
+
- Fix improper raising of exceptions on create and reset
|
254
|
+
|
255
|
+
# 0.10.2
|
256
|
+
* Sep 15, 2011
|
257
|
+
|
258
|
+
- Remove all the annoying logging for loading db schema and seeding on create
|
259
|
+
|
260
|
+
# 0.10.1
|
261
|
+
* Aug 11, 2011
|
262
|
+
|
263
|
+
- Fixed bug in DJ where new objects (that hadn't been pulled from the db) didn't have the proper database assigned
|
264
|
+
|
265
|
+
# 0.10.0
|
266
|
+
* July 29, 2011
|
267
|
+
|
268
|
+
- Added better support for Delayed Job
|
269
|
+
- New config option that enables Delayed Job wrappers
|
270
|
+
- Note that DJ support uses a work-around in order to get queues stored in the public schema, not sure why it doesn't work out of the box, will look into it, until then, see documentation on queue'ng jobs
|
271
|
+
|
272
|
+
# 0.9.2
|
273
|
+
* July 4, 2011
|
274
|
+
|
275
|
+
- Migrations now run associated rails migration fully, fixes schema.rb not being reloaded after migrations
|
276
|
+
|
277
|
+
# 0.9.1
|
278
|
+
* June 24, 2011
|
279
|
+
|
280
|
+
- Hooks now take the payload object as an argument to fetch the proper db for DJ hooks
|
281
|
+
|
282
|
+
# 0.9.0
|
283
|
+
* June 23, 2011
|
284
|
+
|
285
|
+
- Added module to provide delayed job hooks
|
286
|
+
|
287
|
+
# 0.8.0
|
288
|
+
* June 23, 2011
|
289
|
+
|
290
|
+
- Added #current_database which will return the current database (or schema) name
|
291
|
+
|
292
|
+
# 0.7.0
|
293
|
+
* June 22, 2011
|
294
|
+
|
295
|
+
- Added apartment:seed rake task for seeding all dbs
|
296
|
+
|
297
|
+
# 0.6.0
|
298
|
+
* June 21, 2011
|
299
|
+
|
300
|
+
- Added #process to connect to new db, perform operations, then ensure a reset
|
301
|
+
|
302
|
+
# 0.5.1
|
303
|
+
* June 21, 2011
|
304
|
+
|
305
|
+
- Fixed db migrate up/down/rollback
|
306
|
+
- added db:redo
|
307
|
+
|
308
|
+
# 0.5.0
|
309
|
+
* June 20, 2011
|
310
|
+
|
311
|
+
- Added the concept of an "Elevator", a rack based strategy for db switching
|
312
|
+
- Added the Subdomain Elevator middleware to enabled db switching based on subdomain
|
313
|
+
|
314
|
+
# 0.4.0
|
315
|
+
* June 14, 2011
|
316
|
+
|
317
|
+
- Added `configure` method on Apartment instead of using yml file, allows for dynamic setting of db names to migrate for rake task
|
318
|
+
- Added `seed_after_create` config option to import seed data to new db on create
|
319
|
+
|
320
|
+
# 0.3.0
|
321
|
+
* June 10, 2011
|
322
|
+
|
323
|
+
- Added full support for database migration
|
324
|
+
- Added in method to establish new connection for excluded models on startup rather than on each switch
|
325
|
+
|
326
|
+
# 0.2.0
|
327
|
+
* June 6, 2011 *
|
328
|
+
|
329
|
+
- Refactor to use more rails/active_support functionality
|
330
|
+
- Refactor config to lazily load apartment.yml if exists
|
331
|
+
- Remove OStruct and just use hashes for fetching methods
|
332
|
+
- Added schema load on create instead of migrating from scratch
|
333
|
+
|
334
|
+
# 0.1.3
|
335
|
+
* March 30, 2011 *
|
336
|
+
|
337
|
+
- Original pass from Ryan
|
data/README.md
ADDED
@@ -0,0 +1,485 @@
|
|
1
|
+
# Innkeeper
|
2
|
+
|
3
|
+
[![Code Climate](https://codeclimate.com/github/cpoms/innkeeper.png)](https://codeclimate.com/github/cpoms/innkeeper)
|
4
|
+
[![Build Status](https://secure.travis-ci.org/cpoms/innkeeper.png?branch=development)](http://travis-ci.org/cpoms/innkeeper)
|
5
|
+
|
6
|
+
*Multitenancy for Rails and ActiveRecord*
|
7
|
+
|
8
|
+
Innkeeper provides tools to help you deal with multiple tenants in your Rails
|
9
|
+
application. If you need to have certain data sequestered based on account or
|
10
|
+
company, but still allow some data to exist in a common tenant, Innkeeper can
|
11
|
+
help.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
### Rails
|
16
|
+
|
17
|
+
Add the following to your Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'innkeeper'
|
21
|
+
```
|
22
|
+
|
23
|
+
Then generate your `Innkeeper` config file using
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
bundle exec rails generate innkeeper:install
|
27
|
+
```
|
28
|
+
|
29
|
+
This will create a `config/initializers/innkeeper.rb` initializer file.
|
30
|
+
Configure as needed using the docs below.
|
31
|
+
|
32
|
+
That's all you need to set up the Innkeeper libraries. If you want to switch
|
33
|
+
tenants on a per-user basis, look under "Usage - Switching tenants per request",
|
34
|
+
below.
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
### Creating new Tenants
|
39
|
+
|
40
|
+
Before you can switch to a new innkeeper tenant, you will need to create it.
|
41
|
+
Whenever you need to create a new tenant, you can run the following command:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
Innkeeper::Tenant.create('tenant_name')
|
45
|
+
```
|
46
|
+
|
47
|
+
If you're using PostgreSQL, this will create the database and schema from the
|
48
|
+
derived tenant configuration. For example, if you're using the Schema resolver,
|
49
|
+
this will create a schema named 'tenant_name', assuming you're not using a
|
50
|
+
decorator. If you're customising the tenant name with a decorator, it is the
|
51
|
+
decorated name that will be used.
|
52
|
+
|
53
|
+
When you create a new tenant, the schema is loaded on to that tenant, so it will
|
54
|
+
be up to date when create returns.
|
55
|
+
|
56
|
+
### Switching Tenants
|
57
|
+
|
58
|
+
To switch tenants using Innkeeper, use the following command:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
Innkeeper::Tenant.switch!('tenant_name')
|
62
|
+
```
|
63
|
+
|
64
|
+
When switch is called, all requests coming to ActiveRecord will be routed to the
|
65
|
+
tenant you specify (with the exception of excluded models, see below). To return
|
66
|
+
to the 'root' tenant, call switch with no arguments.
|
67
|
+
|
68
|
+
### Switching Tenants per request
|
69
|
+
|
70
|
+
You can have Innkeeper route to the appropriate tenant by adding some Rack
|
71
|
+
middleware. Innkeeper can support many different "Elevators" that can take care
|
72
|
+
of this routing to your data.
|
73
|
+
|
74
|
+
**NOTE: when switching tenants per-request, keep in mind that the order of your
|
75
|
+
Rack middleware is important.** See the
|
76
|
+
[Middleware Considerations](#middleware-considerations) section for more.
|
77
|
+
|
78
|
+
The initializer above will generate the appropriate code for the Subdomain
|
79
|
+
elevator by default. You can see this in `config/initializers/innkeeper.rb`
|
80
|
+
after running that generator. If you're *not* using the generator, you can
|
81
|
+
specify your elevator below. Note that in this case you will **need** to require
|
82
|
+
the elevator manually in your `application.rb` like so:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# config/application.rb
|
86
|
+
require 'innkeeper/elevators/subdomain' # or 'domain' or 'generic'
|
87
|
+
```
|
88
|
+
|
89
|
+
#### Switch on subdomain
|
90
|
+
|
91
|
+
In house, we use the subdomain elevator, which analyzes the subdomain of the
|
92
|
+
request and switches to a tenant schema of the same name. It can be used like
|
93
|
+
so:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
# application.rb
|
97
|
+
module MyApplication
|
98
|
+
class Application < Rails::Application
|
99
|
+
config.middleware.use 'Innkeeper::Elevators::Subdomain'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
If you want to exclude a domain, for example if you don't want your application
|
105
|
+
to treat www like a subdomain, in an initializer in your application, you can
|
106
|
+
set the following:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
# config/initializers/innkeeper/subdomain_exclusions.rb
|
110
|
+
Innkeeper::Elevators::Subdomain.excluded_subdomains = ['www']
|
111
|
+
```
|
112
|
+
|
113
|
+
This functions much in the same way as Innkeeper.excluded_models. This example
|
114
|
+
will prevent switching your tenant when the subdomain is www. Handy for
|
115
|
+
subdomains like: "public", "www", and "admin" :)
|
116
|
+
|
117
|
+
#### Switch on domain
|
118
|
+
|
119
|
+
To switch based on full domain (excluding subdomains *ie 'www'* and top level
|
120
|
+
domains *ie '.com'* ) use the following:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
# application.rb
|
124
|
+
module MyApplication
|
125
|
+
class Application < Rails::Application
|
126
|
+
config.middleware.use 'Innkeeper::Elevators::Domain'
|
127
|
+
end
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
#### Switch on full host using a hash
|
132
|
+
|
133
|
+
To switch based on full host with a hash to find corresponding tenant name use
|
134
|
+
the following:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
# application.rb
|
138
|
+
module MyApplication
|
139
|
+
class Application < Rails::Application
|
140
|
+
config.middleware.use 'Innkeeper::Elevators::HostHash', {'example.com' => 'example_tenant'}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
#### Custom Elevator
|
146
|
+
|
147
|
+
A Generic Elevator exists that allows you to pass a `Proc` (or anything that
|
148
|
+
responds to `call`) to the middleware. This Object will be passed in an
|
149
|
+
`ActionDispatch::Request` object when called for you to do your magic. Innkeeper
|
150
|
+
will use the return value of this proc to switch to the appropriate tenant. Use
|
151
|
+
like so:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
# application.rb
|
155
|
+
module MyApplication
|
156
|
+
class Application < Rails::Application
|
157
|
+
# Obviously not a contrived example
|
158
|
+
config.middleware.use 'Innkeeper::Elevators::Generic', Proc.new { |request| request.host.reverse }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
Your other option is to subclass the Generic elevator and implement your own
|
164
|
+
switching mechanism. This is exactly how the other elevators work. Look at the
|
165
|
+
`subdomain.rb` elevator to get an idea of how this should work. Basically all
|
166
|
+
you need to do is subclass the generic elevator and implement your own
|
167
|
+
`parse_tenant_name` method that will ultimately return the name of the tenant
|
168
|
+
based on the request being made. It *could* look something like this:
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
# app/middleware/my_custom_elevator.rb
|
172
|
+
class MyCustomElevator < Innkeeper::Elevators::Generic
|
173
|
+
|
174
|
+
# @return {String} - The tenant to switch to
|
175
|
+
def parse_tenant_name(request)
|
176
|
+
# request is an instance of Rack::Request
|
177
|
+
|
178
|
+
# example: look up some tenant from the db based on this request
|
179
|
+
tenant_name = SomeModel.from_request(request)
|
180
|
+
|
181
|
+
return tenant_name
|
182
|
+
end
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
#### Middleware Considerations
|
187
|
+
|
188
|
+
In the examples above, we show the Innkeeper middleware being appended to the
|
189
|
+
Rack stack with
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
Rails.application.config.middleware.use 'Innkeeper::Elevators::Subdomain'
|
193
|
+
```
|
194
|
+
|
195
|
+
By default, the Subdomain middleware switches into a Tenant based on the
|
196
|
+
subdomain at the beginning of the request, and when the request is finished, it
|
197
|
+
switches back to the "public" Tenant. This happens in the
|
198
|
+
[Generic](https://github.com/cpoms/innkeeper/blob/development/lib/innkeeper/elevators/generic.rb#L22)
|
199
|
+
elevator, so all elevators that inherit from this elevator will operate as such.
|
200
|
+
|
201
|
+
It's also good to note that Innkeeper switches back to the "public" tenant any
|
202
|
+
time an error is raised in your application.
|
203
|
+
|
204
|
+
This works okay for simple applications, but it's important to consider that you
|
205
|
+
may want to maintain the "selected" tenant through different parts of the Rack
|
206
|
+
application stack. For example, the
|
207
|
+
[Devise](https://github.com/plataformatec/devise) gem adds the `Warden::Manager`
|
208
|
+
middleware at the end of the stack in the examples above, our
|
209
|
+
`Innkeeper::Elevators::Subdomain` middleware would come after it. Trouble is,
|
210
|
+
Innkeeper resets the selected tenant after the request is finish, so some
|
211
|
+
edirects (e.g. authentication) in Devise will be run in the context of the
|
212
|
+
"public" tenant. The same issue would also effect a gem such as the
|
213
|
+
[better_errors](https://github.com/charliesome/better_errors) gem which inserts
|
214
|
+
a middleware quite early in the Rails middleware stack.
|
215
|
+
|
216
|
+
To resolve this issue, consider adding the Innkeeper middleware at a location
|
217
|
+
in the Rack stack that makes sense for your needs, e.g.:
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
Rails.application.config.middleware.insert_before 'Warden::Manager', 'Innkeeper::Elevators::Subdomain'
|
221
|
+
```
|
222
|
+
|
223
|
+
Now work done in the Warden middleware is wrapped in the
|
224
|
+
`Innkeeper::Tenant.switch` context started in the Generic elevator.
|
225
|
+
|
226
|
+
### Dropping Tenants
|
227
|
+
|
228
|
+
To drop tenants using Innkeeper, use the following command:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
Innkeeper::Tenant.drop('tenant_name')
|
232
|
+
```
|
233
|
+
|
234
|
+
When method is called, the schema is dropped and all data from itself will be
|
235
|
+
lost. Be careful with this method.
|
236
|
+
|
237
|
+
## Config
|
238
|
+
|
239
|
+
The following config options should be set up in a Rails initializer such as:
|
240
|
+
|
241
|
+
config/initializers/innkeeper.rb
|
242
|
+
|
243
|
+
To set config options, add this to your initializer:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
Innkeeper.configure do |config|
|
247
|
+
# set your options (described below) here
|
248
|
+
end
|
249
|
+
```
|
250
|
+
|
251
|
+
### Excluding models
|
252
|
+
|
253
|
+
If you have some models that should always access the 'public' tenant, you can
|
254
|
+
specify this by configuring Innkeeper using `Innkeeper.configure`. This will
|
255
|
+
yield a config object for you. You can set excluded models like so:
|
256
|
+
|
257
|
+
```ruby
|
258
|
+
config.excluded_models = ["User", "Company"] # these models will not be multi-tenanted, but remain in the global (public) namespace
|
259
|
+
```
|
260
|
+
|
261
|
+
Note that a string representation of the model name is now the standard so that
|
262
|
+
models are properly constantized when reloaded in development.
|
263
|
+
|
264
|
+
Rails will always access the 'public' tenant when accessing these models, but
|
265
|
+
note that tables will be created in all schemas. This may not be ideal, but its
|
266
|
+
done this way because otherwise rails wouldn't be able to properly generate the
|
267
|
+
schema.rb file.
|
268
|
+
|
269
|
+
> **NOTE - Many-To-Many Excluded Models:**
|
270
|
+
> Since model exclusions must come from referencing a real ActiveRecord model,
|
271
|
+
`has_and_belongs_to_many` is NOT supported. In order to achieve a many-to-many
|
272
|
+
relationship for excluded models, you MUST use `has_many :through`. This way you
|
273
|
+
can reference the join model in the excluded models configuration.
|
274
|
+
|
275
|
+
### Postgresql Schemas
|
276
|
+
|
277
|
+
## Providing a Different default_schema
|
278
|
+
|
279
|
+
By default, ActiveRecord will use `"$user", public` as the default
|
280
|
+
`schema_search_path`. This can be modified if you wish to use a different
|
281
|
+
default schema be setting:
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
config.default_schema = "some_other_schema"
|
285
|
+
```
|
286
|
+
|
287
|
+
With that set, all excluded models will use this schema as the table name prefix
|
288
|
+
instead of `public` and `reset` on `Innkeeper::Tenant` will return to this
|
289
|
+
schema as well.
|
290
|
+
|
291
|
+
## Persistent Schemas
|
292
|
+
|
293
|
+
Innkeeper will normally just switch the `schema_search_path` whole hog to the
|
294
|
+
one passed in. This can lead to problems if you want other schemas to always be
|
295
|
+
searched as well. Enter `persistent_schemas`. You can configure a list of other
|
296
|
+
schemas that will always remain in the search path, while the default gets
|
297
|
+
swapped out:
|
298
|
+
|
299
|
+
```ruby
|
300
|
+
config.persistent_schemas = ['some', 'other', 'schemas']
|
301
|
+
```
|
302
|
+
|
303
|
+
### Installing Extensions into Persistent Schemas
|
304
|
+
|
305
|
+
Persistent Schemas have numerous useful applications.
|
306
|
+
[Hstore](http://www.postgresql.org/docs/9.1/static/hstore.html), for instance,
|
307
|
+
is a popular storage engine for Postgresql. In order to use extensions such as
|
308
|
+
Hstore, you have to install it to a specific schema and have that always in the
|
309
|
+
`schema_search_path`.
|
310
|
+
|
311
|
+
When using extensions, keep in mind:
|
312
|
+
|
313
|
+
* Extensions can only be installed into one schema per database, so we will want
|
314
|
+
to install it into a schema that is always available in the
|
315
|
+
`schema_search_path`
|
316
|
+
* The schema and extension need to be created in the database *before* they are
|
317
|
+
referenced in migrations, database.yml or innkeeper.
|
318
|
+
* There does not seem to be a way to create the schema and extension using
|
319
|
+
standard rails migrations.
|
320
|
+
* Rails db:test:prepare deletes and recreates the database, so it needs to be
|
321
|
+
easy for the extension schema to be recreated here.
|
322
|
+
|
323
|
+
#### 1. Ensure the extensions schema is created when the database is created
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
# lib/tasks/db_enhancements.rake
|
327
|
+
|
328
|
+
####### Important information ####################
|
329
|
+
# This file is used to setup a shared extensions #
|
330
|
+
# within a dedicated schema. This gives us the #
|
331
|
+
# advantage of only needing to enable extensions #
|
332
|
+
# in one place. #
|
333
|
+
# #
|
334
|
+
# This task should be run AFTER db:create but #
|
335
|
+
# BEFORE db:migrate. #
|
336
|
+
##################################################
|
337
|
+
|
338
|
+
namespace :db do
|
339
|
+
desc 'Also create shared_extensions Schema'
|
340
|
+
task :extensions => :environment do
|
341
|
+
# Create Schema
|
342
|
+
ActiveRecord::Base.connection.execute 'CREATE SCHEMA IF NOT EXISTS shared_extensions;'
|
343
|
+
# Enable Hstore
|
344
|
+
ActiveRecord::Base.connection.execute 'CREATE EXTENSION IF NOT EXISTS HSTORE SCHEMA shared_extensions;'
|
345
|
+
# Enable UUID-OSSP
|
346
|
+
ActiveRecord::Base.connection.execute 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp" SCHEMA shared_extensions;'
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
Rake::Task["db:create"].enhance do
|
351
|
+
Rake::Task["db:extensions"].invoke
|
352
|
+
end
|
353
|
+
|
354
|
+
Rake::Task["db:test:purge"].enhance do
|
355
|
+
Rake::Task["db:extensions"].invoke
|
356
|
+
end
|
357
|
+
```
|
358
|
+
|
359
|
+
#### 2. Ensure the schema is in Rails' default connection
|
360
|
+
|
361
|
+
Next, your `database.yml` file must mimic what you've set for your default and
|
362
|
+
persistent schemas in Innkeeper. When you run migrations with Rails, it won't
|
363
|
+
know about the extensions schema because Innkeeper isn't injected into the
|
364
|
+
default connection, it's done on a per-request basis, therefore Rails doesn't
|
365
|
+
know about `hstore` or `uuid-ossp` during migrations. To do so, add the
|
366
|
+
following to your `database.yml` for all environments
|
367
|
+
|
368
|
+
```yaml
|
369
|
+
# database.yml
|
370
|
+
...
|
371
|
+
adapter: postgresql
|
372
|
+
schema_search_path: "public,shared_extensions"
|
373
|
+
...
|
374
|
+
```
|
375
|
+
|
376
|
+
This would be for a config with `default_schema` set to `public` and
|
377
|
+
`persistent_schemas` set to `['shared_extensions']`.
|
378
|
+
|
379
|
+
#### 3. Ensure the schema is in the innkeeper config
|
380
|
+
|
381
|
+
```ruby
|
382
|
+
# config/initializers/innkeeper.rb
|
383
|
+
...
|
384
|
+
config.persistent_schemas = ['shared_extensions']
|
385
|
+
...
|
386
|
+
```
|
387
|
+
|
388
|
+
#### Alternative: Creating schema by default
|
389
|
+
|
390
|
+
Another way that we've successfully configured hstore for our applications is to
|
391
|
+
add it into the postgresql template1 database so that every tenant that gets
|
392
|
+
created has it by default.
|
393
|
+
|
394
|
+
One caveat with this approach is that it can interfere with other projects in
|
395
|
+
development using the same extensions and template, but not using innkeeper with
|
396
|
+
this approach.
|
397
|
+
|
398
|
+
You can do so using a command like so:
|
399
|
+
|
400
|
+
```bash
|
401
|
+
psql -U postgres -d template1 -c "CREATE SCHEMA shared_extensions AUTHORIZATION some_username;"
|
402
|
+
psql -U postgres -d template1 -c "CREATE EXTENSION IF NOT EXISTS hstore SCHEMA shared_extensions;"
|
403
|
+
```
|
404
|
+
|
405
|
+
The *ideal* setup would actually be to install `hstore` into the `public` schema
|
406
|
+
and leave the public schema in the `search_path` at all times. We won't be able
|
407
|
+
to do this though until public doesn't also contain the tenanted tables, which
|
408
|
+
is an open issue with no real milestone to be completed. Happy to accept PR's on
|
409
|
+
the matter.
|
410
|
+
|
411
|
+
### Managing Migrations
|
412
|
+
|
413
|
+
In order to migrate all of your tenants (or postgresql schemas) you need to
|
414
|
+
provide a list of dbs to Innkeeper. You can make this dynamic by providing a
|
415
|
+
Proc object to be called on migrations. This object should yield an array of
|
416
|
+
string representing each tenant name. Example:
|
417
|
+
|
418
|
+
```ruby
|
419
|
+
# Dynamically get tenant names to migrate
|
420
|
+
config.tenant_names = lambda{ Customer.pluck(:tenant_name) }
|
421
|
+
|
422
|
+
# Use a static list of tenant names for migrate
|
423
|
+
config.tenant_names = ['tenant1', 'tenant2']
|
424
|
+
```
|
425
|
+
|
426
|
+
You can then migrate your tenants using the normal rake task:
|
427
|
+
|
428
|
+
```ruby
|
429
|
+
rake db:migrate
|
430
|
+
```
|
431
|
+
|
432
|
+
This just invokes `Innkeeper::Tenant.migrate(#{tenant_name})` for each tenant
|
433
|
+
name supplied from `Innkeeper.tenant_names`.
|
434
|
+
|
435
|
+
#### Parallel Migrations
|
436
|
+
|
437
|
+
Innkeeper supports parallelizing migrations into multiple threads when
|
438
|
+
you have a large number of tenants. By default, parallel migrations is
|
439
|
+
turned off. You can enable this by setting `parallel_migration_threads` to
|
440
|
+
the number of threads you want to use in your initializer.
|
441
|
+
|
442
|
+
Keep in mind that because migrations are going to access the database,
|
443
|
+
the number of threads indicated here should be less than the pool size
|
444
|
+
that Rails will use to connect to your database.
|
445
|
+
|
446
|
+
## Tenants on different servers
|
447
|
+
|
448
|
+
Innkeeper supports tenant-based sharding at the application level. The `switch`,
|
449
|
+
`create`, and `drop` methods all support full database configurations (as a
|
450
|
+
hash) as well as tenant names. In fact, even when you pass a tenant name, it
|
451
|
+
gets resolved to a full configuration using the configured `tenant_resolver`. If
|
452
|
+
you wish to switch to a tenant on a different host, you can pass the full config
|
453
|
+
with the host key.
|
454
|
+
|
455
|
+
Innkeeper will compare the config to it's current one and work out whether it
|
456
|
+
needs to switch host, database, schema, etc, and only do the minimal switch. For
|
457
|
+
tenants (databases for mysql, schemas for pg) on the same host, the switch will
|
458
|
+
be a lightweight 'local' switch, which is one that occurs as a SQL query only,
|
459
|
+
rather than a re-establishment of the database connection.
|
460
|
+
|
461
|
+
You could make use of a custom resolver to do multi-host tenant switching by
|
462
|
+
name. You could map tenant names to a host (shard) IP via the hash of the host
|
463
|
+
name, or something similar, and divide the hash space across available hosts.
|
464
|
+
|
465
|
+
## Sidekiq
|
466
|
+
|
467
|
+
See [innkeeper-sidekiq](https://github.com/cpoms/innkeeper-sidekiq)
|
468
|
+
|
469
|
+
## Contributing
|
470
|
+
|
471
|
+
* In `test/`, you will see `databases.yml.sample` files
|
472
|
+
* Copy them into the same directory but with the name `databases.yml`
|
473
|
+
* Edit them to fit your own settings
|
474
|
+
* Rake tasks (see the Rakefile) will help you setup your dbs necessary to run
|
475
|
+
tests
|
476
|
+
* Please issue pull requests to the `development` branch. All development
|
477
|
+
happens here, master is used for releases.
|
478
|
+
* Ensure that your code is accompanied with tests. No code will be merged
|
479
|
+
without tests
|
480
|
+
|
481
|
+
## License
|
482
|
+
|
483
|
+
Innkeeper is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
484
|
+
|
485
|
+
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/cpoms/innkeeper/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|