single point of failure for all tenants. Shared development and maintenance costs, but customer-specific features are burdensome. Single codebase, but with configuration overhead.
single point of failure for all tenants. Shared development and maintenance costs, but customer-specific features are burdensome. Single codebase, but with configuration overhead. However, after configuration, development is the same as for a classic, single- tenant application.
more named schemas, which in turn contain tables. Schemas also contain other kinds of named objects, including data types, functions, and operators. The same object name can be used in different schemas without conflict http://www.postgresql.org/docs/9.5/static/ddl-schemas.html There is a default p u b l i c schema, but you can create your own.
meant without specifying it explicitly. # s h o w s e a r c h _ p a t h ; s e a r c h _ p a t h - - - - - - - - - - - - - - - - " $ u s e r " , p u b l i c # s e t s e a r c h _ p a t h t o m y s c h e m a , p u b l i c ; # s h o w s e a r c h _ p a t h ; s e a r c h _ p a t h - - - - - - - - - - - - - - - - - - m y s c h e m a , p u b l i c
r e q u i r e ' a c t i v e _ r e c o r d / f i x t u r e s ' r e q u i r e R a i l s . r o o t . j o i n ( ' l i b / f i x t u r e _ l o a d e r ' ) f i x t u r e s _ d i r e c t o r y = R a i l s . r o o t . j o i n ( " d b / s e e d s / # { A p a r t m e n t : : T e n a n t . c u r r e n t } " ) F i x t u r e L o a d e r : : l o a d _ d i r e c t o r y ( f i x t u r e s _ d i r e c t o r y ) # l i b / f i x t u r e _ l o a d e r . r b c l a s s F i x t u r e L o a d e r d e f s e l f . l o a d _ d i r e c t o r y ( d i r e c t o r y ) u n l e s s D i r . e x i s t ? ( d i r e c t o r y ) r a i s e A r g u m e n t E r r o r . n e w ( " # { d i r e c t o r y } d o e s n o t e x i s t ! " ) e n d A c t i v e R e c o r d : : F i x t u r e S e t . r e s e t _ c a c h e D i r . g l o b ( d i r e c t o r y . j o i n ( " * * / * " ) ) . e a c h d o | f i l e n a m e | f i x t u r e = P a t h n a m e . n e w ( f i l e n a m e . c h o m p ( ' . y m l ' ) ) . b a s e n a m e A c t i v e R e c o r d : : F i x t u r e S e t . c r e a t e _ f i x t u r e s ( d i r e c t o r y , f i x t u r e ) e n d e n d e n d
m e n t gem documentation: When using extensions, keep in mind: Extensions can only be installed into one schema per database, so we will want to install it into a schema that is always available in the schema_search_path The schema and extension need to be created in the database before they are referenced in migrations, database.yml or apartment. There does not seem to be a way to create the schema and extension using standard rails migrations. Rails db:test:prepare deletes and recreates the database, so it needs to be easy for the extension schema to be recreated here.
o s t g r e s - c " C R E A T E D A T A B A S E t e m p l a t e _ a p p ; p s q l - U p o s t g r e s - d t e m p l a t e _ a p p - c \ " C R E A T E S C H E M A I F N O T E X I S T S s h a r e d _ e x t e n s i o n s ; \ C R E A T E E X T E N S I O N I F N O T E X I S T S h s t o r e S C H E M A s h a r e d _ e x t e n s i o n s ; " # d a t a b a s e . y m l d e f a u l t : & d e f a u l t a d a p t e r : p o s t g r e s q l e n c o d i n g : u n i c o d e t e m p l a t e : t e m p l a t e _ a p p Also had to use s t r u c t u r e . s q l instead of s c h e m a . r b .
p : : A t t a c h m e n t . d e f a u l t _ o p t i o n s [ : u r l ] = " / : c l a s s / : t e n a n t / : a t t a c h m e n t / : i d / : s t y l e / : u p d a t e d _ a t " P a p e r c l i p : : A t t a c h m e n t . d e f a u l t _ o p t i o n s [ : p a t h ] = R a i l s . r o o t . j o i n ( ' f i l e s ' ) . j o i n ( " : c l a s s / : t e n a n t / : a t t a c h m e n t / : i d / : s t y l e / : u p d a t e d _ a t " ) . t o _ s P a p e r c l i p . i n t e r p o l a t e s : t e n a n t d o | a t t a c h m e n t , s t y l e | A p a r t m e n t : : T e n a n t . c u r r e n t e n d # o t h e r i n t e r p o l a t i o n s . . .
name and a creation timestamp. c l a s s F e a t u r e < A c t i v e R e c o r d : : B a s e d e f s e l f . e n a b l e d ? ( n a m e ) w h e r e ( n a m e : n a m e ) . e x i s t s ? e n d e n d Remember: the contents of this table can be different for every tenant thanks to PosgreSQL schemas.
& d e f a u l t c l a s s : ' T a s k W o r k e r ' q u e u e : ' c r o n ' r e t r i e s : 0 s e n d _ r e c a p _ e m a i l s : < < : * d e f a u l t c r o n : " 0 2 * * * " # 2 : 0 0 e v e r y d a y a r g s : [ ' s e n d _ r e c a p _ e m a i l s ' ] Results in something like w o r k e r = T a s k W o r k e r . n e w ( o p t i o n s ) w o r k e r . p e r f o r m ( ' s e n d _ r e c a p _ e m a i l s ' )
k W o r k e r i n c l u d e S i d e k i q : : W o r k e r s i d e k i q _ o p t i o n s r e t r y : f a l s e d e f p e r f o r m ( t a s k _ n a m e ) u n l e s s r e s p o n d _ t o ? ( t a s k _ n a m e ) r a i s e ( A r g u m e n t E r r o r , " m i s s i n g t a s k # { t a s k _ n a m e } " ) e n d A p a r t m e n t . t e n a n t _ n a m e s . e a c h d o | t e n a n t | A p a r t m e n t : : T e n a n t . s w i t c h ( t e n a n t ) d o i f F e a t u r e . e n a b l e d ? ( " t a s k : # { t a s k _ n a m e } " ) s e n d ( t a s k _ n a m e ) e n d e n d e n d e n d d e f s e n d _ r e c a p _ e m a i l s ; e n d # . . . e n d
all tenants (and optionally different environments for each tenant) t e n a n t 1 : < < : * d e f a u l t d o m a i n : e x a m p l e . c o m s m t p : d o m a i n : e x a m p l e . c o m Loaded in an initializer: r e q u i r e R a i l s . r o o t . j o i n ( ' l i b / c o n f i g ' ) R a i l s . a p p l i c a t i o n . c o n f i g . x . a p p = A p p : : C o n f i g . n e w
p l i c a t i o n M a i l e r < A c t i o n M a i l e r : : B a s e d e f a u l t f r o m : - > ( m a i l e r ) d o R a i l s . c o n f i g u r a t i o n . x . a p p . s e n d e r _ e m a i l e n d l a y o u t ' m a i l e r ' d e f s e l f . s m t p _ s e t t i n g s s u p e r . m e r g e ( R a i l s . c o n f i g u r a t i o n . x . a p p . s m t p . s y m b o l i z e _ k e y s ) e n d d e f u r l _ o p t i o n s s u p e r . m e r g e ( h o s t : R a i l s . c o n f i g u r a t i o n . x . a p p . d o m a i n ) e n d e n d
g d e f m e t h o d _ m i s s i n g ( m e t h o d _ n a m e , * a r g s , & b l o c k ) g e t ( m e t h o d _ n a m e ) e n d p r i v a t e d e f g e t ( k e y , t e n a n t = n i l ) c o n f i g = t e n a n t s _ c o n f i g [ t e n a n t | | A p a r t m e n t : : T e n a n t . c u r r e n t ] | | { } c o n f i g = c o n f i g . m e r g e ( c o n f i g . f e t c h ( R a i l s . e n v , { } ) ) c o n f i g . f e t c h ( k e y . t o _ s ) r e s c u e K e y E r r o r r a i s e K e y E r r o r , e x c e p t i o n _ m e s s a g e ( k e y ) e n d d e f t e n a n t s _ c o n f i g @ t e n a n t s _ c o n f i g | | = Y A M L : : l o a d ( E R B . n e w ( R a i l s . r o o t . j o i n ( " f o o . y m l " ) . r e a d ) . r e s u l t ) e n d d e f e x c e p t i o n _ m e s s a g e ( k e y ) # . . . e n d e n d
d u l e U r l m o d u l e U s e r P i c t u r e d e f s e l f . p a t h ( u s e r , s t y l e ) # . . . e n d d e f s e l f . u r l ( u s e r , s t y l e ) h o s t = R a i l s . c o n f i g u r a t i o n . x . a p p . h o s t U R I . j o i n ( h o s t , p a t h ( u s e r , s t y l e ) ) . t o _ s e n d e n d e n d
c e p t i o n N o t i f i e r c l a s s M u l t i t e n a n t E m a i l N o t i f i e r < E m a i l N o t i f i e r d e f o p t i o n s s u p e r . t a p d o | o p t s | e n v = R a i l s . e n v . s t a g i n g ? ? ' S T A G I N G ' : n i l t e n a n t _ w i t h _ e n v i r o n m e n t = [ c u r r e n t _ t e n a n t , e n v ] . c o m p a c t . j o i n ( ' ' ) o p t s [ : e m a i l _ p r e f i x ] = " [ # { t e n a n t _ w i t h _ e n v i r o n m e n t } ] [ E R R O R ] " e n d e n d p r i v a t e d e f c u r r e n t _ t e n a n t : : A p a r t m e n t : : T e n a n t . c u r r e n t . u p c a s e e n d e n d e n d Examples: [ T E N A N T O N E ] [ E R R O R ] E r r o r m e s s a g e [ T E N A N T O N E S T A G I N G ] [ E R R O R ] E r r o r m e s s a g e [ T E N A N T T W O ] [ E R R O R ] O t h e r e r r o r
I 1 8 n m o d u l e B a c k e n d c l a s s M u l t i t e n a n t < S i m p l e d e f l o o k u p ( l o c a l e , k e y , s c o p e , o p t i o n s ) t e n a n t _ s c o p e = [ c u r r e n t _ t e n a n t ] + [ s c o p e ] . f l a t t e n s u p e r ( l o c a l e , k e y , t e n a n t _ s c o p e , o p t i o n s ) e n d p r i v a t e d e f c u r r e n t _ t e n a n t : : A p a r t m e n t : : T e n a n t . c u r r e n t e n d e n d e n d e n d
e ' i 1 8 n / b a c k e n d / m u l t i t e n a n t ' I 1 8 n . b a c k e n d = I 1 8 n : : B a c k e n d : : C h a i n . n e w ( I 1 8 n : : B a c k e n d : : M u l t i t e n a n t . n e w , I 1 8 n . b a c k e n d )
l i c a t i o n C o n t r o l l e r b e f o r e _ a c t i o n : p r e p e n d _ t e n a n t _ v i e w _ p a t h p r i v a t e d e f p r e p e n d _ t e n a n t _ v i e w _ p a t h t e n a n t = A p a r t m e n t : : T e n a n t . c u r r e n t p r e p e n d _ v i e w _ p a t h R a i l s . r o o t . j o i n ( ' a p p ' , ' v i e w s ' , t e n a n t ) e n d e n d
h o m e / l e g a l ' $ l s a p p / v i e w s / * / h o m e a p p / v i e w s / t e n a n t 1 / h o m e : l e g a l . h t m l . e r b a p p / v i e w s / t e n a n t 2 / h o m e : l e g a l . h t m l . e r b
e s h e e t _ l i n k _ t a g A p a r t m e n t : : T e n a n t . c u r r e n t % > t e n a n t 1 . c s s : / * * = r e q u i r e . / t e n a n t 1 / m a i n * / t e n a n t 1 / m a i n . s c s s : @ i m p o r t " . . / s h a r e d / f o n t - l a t o " ; @ i m p o r t " v a r i a b l e s " ; @ i m p o r t " . . / a p p l i c a t i o n / m a i n " ; @ i m p o r t " i m a g e s " ;
s s e t s / s t y l e s h e e t s a p p / a s s e t s / s t y l e s h e e t s ├── a p p l i c a t i o n │ ├── a d m i n │ │ └── . . . │ ├── m a i n . s c s s │ └── . . . ├── e m b e d │ └── m a i n . s c s s ├── m o b i l e │ ├── m a i n . s c s s │ └── . . . ├── t e n a n t 1 │ ├── e m b e d . s c s s │ ├── f o n t - l a t o . s c s s │ ├── i m a g e s . s c s s │ ├── m a i n . s c s s │ ├── m o b i l e . s c s s │ └── _ v a r i a b l e s . s c s s ├── t e n a n t 1 . c s s ├── t e n a n t 1 _ e m b e d . c s s ├── t e n a n t 1 _ m o b i l e . c s s ├── s h a r e d │ └── f o n t - l a t o . s c s s └── . . .
h e ( n a m e = { } , o p t i o n s = n i l , & b l o c k ) k e y = [ A p a r t m e n t : : T e n a n t . c u r r e n t , I 1 8 n . l o c a l e , n a m e ] c a c h e ( k e y , o p t i o n s , & b l o c k ) e n d
c h e H e l p e r d e f a p p _ c a c h e ( n a m e = { } , o p t i o n s = n i l , & b l o c k ) c a c h e ( [ A p a r t m e n t : : T e n a n t . c u r r e n t , I 1 8 n . l o c a l e , d e v i c e _ c a c h e _ k e y , c u r r e n t _ a c c o u n t . t r y ( : r o l e s _ c a c h e _ k e y ) , n a m e ] . f l a t t e n ( 1 ) , o p t i o n s , & b l o c k ) e n d d e f d e v i c e _ c a c h e _ k e y [ d e v i c e _ r e q u i r e s _ u s e r _ a c t i o n _ t o _ p l a y ? ] . i n d e x { | v e r s i o n | v e r s i o n } e n d d e f m o d e l _ i s _ o r _ b e l o n g s _ t o _ c u r r e n t _ u s e r ? ( m o d e l ) i f c u r r e n t _ u s e r m o d e l = = c u r r e n t _ u s e r | | m o d e l . t r y ( : u s e r ) = = c u r r e n t _ u s e r e n d e n d e n d