activerecord-multi-tenant 1.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/active-record-multi-tenant-tests.yml +83 -0
  3. data/.gitignore +6 -0
  4. data/.readthedocs.yaml +15 -0
  5. data/.rspec +0 -0
  6. data/.rubocop.yml +51 -0
  7. data/Appraisals +6 -22
  8. data/CHANGELOG.md +51 -0
  9. data/Gemfile +3 -1
  10. data/LICENSE +0 -0
  11. data/README.md +3 -2
  12. data/Rakefile +1 -1
  13. data/activerecord-multi-tenant.gemspec +28 -23
  14. data/docker-compose.yml +24 -18
  15. data/docs/.gitignore +3 -0
  16. data/docs/Makefile +28 -0
  17. data/docs/api-reference.sh +10 -0
  18. data/docs/requirements.in +4 -0
  19. data/docs/requirements.txt +62 -0
  20. data/docs/source/_static/api-reference/ActiveRecord/Associations/Association.html +285 -0
  21. data/docs/source/_static/api-reference/ActiveRecord/Associations/ClassMethods.html +255 -0
  22. data/docs/source/_static/api-reference/ActiveRecord/Associations.html +117 -0
  23. data/docs/source/_static/api-reference/ActiveRecord/ConnectionAdapters/SchemaStatements.html +232 -0
  24. data/docs/source/_static/api-reference/ActiveRecord/ConnectionAdapters.html +126 -0
  25. data/docs/source/_static/api-reference/ActiveRecord/QueryMethods.html +336 -0
  26. data/docs/source/_static/api-reference/ActiveRecord/SchemaDumper.html +121 -0
  27. data/docs/source/_static/api-reference/ActiveRecord.html +130 -0
  28. data/docs/source/_static/api-reference/MultiTenant/ArelTenantVisitor.html +755 -0
  29. data/docs/source/_static/api-reference/MultiTenant/ArelVisitorsDepthFirst.html +208 -0
  30. data/docs/source/_static/api-reference/MultiTenant/BaseTenantEnforcementClause.html +462 -0
  31. data/docs/source/_static/api-reference/MultiTenant/Context.html +659 -0
  32. data/docs/source/_static/api-reference/MultiTenant/ControllerExtensions.html +202 -0
  33. data/docs/source/_static/api-reference/MultiTenant/CopyFromClient.html +186 -0
  34. data/docs/source/_static/api-reference/MultiTenant/CopyFromClientHelper.html +362 -0
  35. data/docs/source/_static/api-reference/MultiTenant/Current.html +124 -0
  36. data/docs/source/_static/api-reference/MultiTenant/DatabaseStatements.html +366 -0
  37. data/docs/source/_static/api-reference/MultiTenant/FastTruncate.html +226 -0
  38. data/docs/source/_static/api-reference/MultiTenant/MigrationExtensions.html +554 -0
  39. data/docs/source/_static/api-reference/MultiTenant/MissingTenantError.html +124 -0
  40. data/docs/source/_static/api-reference/MultiTenant/ModelExtensionsClassMethods.html +492 -0
  41. data/docs/source/_static/api-reference/MultiTenant/QueryMonitor.html +257 -0
  42. data/docs/source/_static/api-reference/MultiTenant/Table.html +419 -0
  43. data/docs/source/_static/api-reference/MultiTenant/TenantEnforcementClause.html +148 -0
  44. data/docs/source/_static/api-reference/MultiTenant/TenantIsImmutable.html +135 -0
  45. data/docs/source/_static/api-reference/MultiTenant/TenantJoinEnforcementClause.html +310 -0
  46. data/docs/source/_static/api-reference/MultiTenant/TenantValueVisitor.html +239 -0
  47. data/docs/source/_static/api-reference/MultiTenant.html +1454 -0
  48. data/docs/source/_static/api-reference/MultiTenantFindBy.html +180 -0
  49. data/docs/source/_static/api-reference/Sidekiq/Client.html +302 -0
  50. data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant/Client.html +217 -0
  51. data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant/Server.html +219 -0
  52. data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant.html +126 -0
  53. data/docs/source/_static/api-reference/Sidekiq.html +126 -0
  54. data/docs/source/_static/api-reference/_index.html +399 -0
  55. data/docs/source/_static/api-reference/class_list.html +51 -0
  56. data/docs/source/_static/api-reference/css/common.css +1 -0
  57. data/docs/source/_static/api-reference/css/full_list.css +58 -0
  58. data/docs/source/_static/api-reference/css/style.css +497 -0
  59. data/docs/source/_static/api-reference/file.README.html +167 -0
  60. data/docs/source/_static/api-reference/file_list.html +56 -0
  61. data/docs/source/_static/api-reference/frames.html +17 -0
  62. data/docs/source/_static/api-reference/index.html +167 -0
  63. data/docs/source/_static/api-reference/js/app.js +314 -0
  64. data/docs/source/_static/api-reference/js/full_list.js +216 -0
  65. data/docs/source/_static/api-reference/js/jquery.js +4 -0
  66. data/docs/source/_static/api-reference/method_list.html +715 -0
  67. data/docs/source/_static/api-reference/top-level-namespace.html +126 -0
  68. data/docs/source/_templates/.gitignore +4 -0
  69. data/docs/source/api-reference.rst +8 -0
  70. data/docs/source/appendix.rst +26 -0
  71. data/docs/source/changelog.rst +8 -0
  72. data/docs/source/community-and-support.rst +26 -0
  73. data/docs/source/conf.py +30 -0
  74. data/docs/source/contributing.rst +70 -0
  75. data/docs/source/getting-started.rst +37 -0
  76. data/docs/source/guides-and-tutorials.rst +129 -0
  77. data/docs/source/index.rst +54 -0
  78. data/docs/source/introduction.rst +33 -0
  79. data/docs/source/license.rst +22 -0
  80. data/docs/source/troubleshooting.rst +41 -0
  81. data/docs/source/usage-guide.rst +59 -0
  82. data/lib/activerecord-multi-tenant/arel_visitors_depth_first.rb +183 -174
  83. data/lib/activerecord-multi-tenant/controller_extensions.rb +15 -4
  84. data/lib/activerecord-multi-tenant/copy_from_client.rb +4 -0
  85. data/lib/activerecord-multi-tenant/fast_truncate.rb +4 -2
  86. data/lib/activerecord-multi-tenant/habtm.rb +50 -0
  87. data/lib/activerecord-multi-tenant/migrations.rb +87 -10
  88. data/lib/activerecord-multi-tenant/model_extensions.rb +98 -34
  89. data/lib/activerecord-multi-tenant/multi_tenant.rb +102 -29
  90. data/lib/activerecord-multi-tenant/query_monitor.rb +21 -5
  91. data/lib/activerecord-multi-tenant/query_rewriter.rb +122 -91
  92. data/lib/activerecord-multi-tenant/sidekiq.rb +46 -19
  93. data/lib/activerecord-multi-tenant/table_node.rb +13 -0
  94. data/lib/activerecord-multi-tenant/version.rb +1 -1
  95. data/lib/activerecord-multi-tenant.rb +3 -13
  96. data/lib/activerecord_multi_tenant.rb +13 -0
  97. data/spec/activerecord-multi-tenant/associations_spec.rb +42 -0
  98. data/spec/activerecord-multi-tenant/controller_extensions_spec.rb +3 -2
  99. data/spec/activerecord-multi-tenant/fast_truncate_spec.rb +8 -6
  100. data/spec/activerecord-multi-tenant/model_extensions_spec.rb +347 -143
  101. data/spec/activerecord-multi-tenant/multi_tenant_spec.rb +69 -13
  102. data/spec/activerecord-multi-tenant/query_rewriter_spec.rb +60 -59
  103. data/spec/activerecord-multi-tenant/record_callback_spec.rb +0 -0
  104. data/spec/activerecord-multi-tenant/record_finding_spec.rb +11 -11
  105. data/spec/activerecord-multi-tenant/record_modifications_spec.rb +23 -4
  106. data/spec/activerecord-multi-tenant/sidekiq_spec.rb +10 -10
  107. data/spec/database.yml +0 -0
  108. data/spec/schema.rb +43 -2
  109. data/spec/spec_helper.rb +52 -16
  110. data/spec/support/format_sql.rb +20 -0
  111. metadata +126 -36
  112. data/.github/workflows/CI.yml +0 -63
  113. data/gemfiles/.bundle/config +0 -2
  114. data/gemfiles/active_record_5.2.gemfile +0 -16
  115. data/gemfiles/active_record_6.0.gemfile +0 -8
  116. data/gemfiles/active_record_6.1.gemfile +0 -8
  117. data/gemfiles/active_record_7.0.gemfile +0 -8
  118. data/gemfiles/rails_5.2.gemfile +0 -16
  119. data/gemfiles/rails_6.0.gemfile +0 -8
  120. data/gemfiles/rails_6.1.gemfile +0 -8
  121. data/gemfiles/rails_7.0.gemfile +0 -8
  122. data/lib/activerecord-multi-tenant/persistence_extension.rb +0 -13
  123. data/lib/activerecord-multi-tenant/with_lock.rb +0 -15
  124. data/spec/activerecord-multi-tenant/schema_dumper_tester.rb +0 -0
@@ -0,0 +1,59 @@
1
+ .. _usage-guide:
2
+
3
+ Usage Guide
4
+ ===========
5
+
6
+ This section provides a comprehensive guide on how to use ``activerecord-multi-tenant`` in your Rails application.
7
+
8
+ Basic Usage
9
+ -----------
10
+
11
+ To use ``activerecord-multi-tenant``, you need to declare the tenant model in your ActiveRecord models. Here's an example:
12
+
13
+ .. code-block:: ruby
14
+
15
+ class PageView < ActiveRecord::Base
16
+ multi_tenant :customer
17
+ belongs_to :site
18
+
19
+ # ...
20
+ end
21
+
22
+ class Site < ActiveRecord::Base
23
+ multi_tenant :customer
24
+ has_many :page_views
25
+
26
+ # ...
27
+ end
28
+
29
+ In this example, the ``PageView`` and ``Site`` models are scoped to the ``Customer`` model. This means that each user belongs to a specific customer.
30
+
31
+
32
+ Then wrap all code that runs queries/modifications in blocks like this:
33
+
34
+ .. code-block:: ruby
35
+
36
+ customer = Customer.find(session[:current_customer_id])
37
+ # ...
38
+ MultiTenant.with(customer) do
39
+ site = Site.find(params[:site_id])
40
+ site.update! last_accessed_at: Time.now
41
+ site.page_views.count
42
+ end
43
+
44
+ Alternatively, if you don't want to use a block, you can set the current tenant explicitly:
45
+
46
+ .. code-block:: ruby
47
+
48
+ customer = Customer.find(session[:current_customer_id])
49
+ MultiTenant.current_tenant = customer
50
+
51
+
52
+ Multi-tenancy Concepts and Terminology
53
+ --------------------------------------
54
+
55
+ Multi-tenancy is a software architecture in which a single instance of software serves multiple tenants. A tenant is a group of users who share a common access with specific privileges to the software instance.
56
+
57
+ In the context of ``activerecord-multi-tenant``, a tenant is typically represented by a model in your Rails application (e.g., ``Customer``), and other models (e.g., ``PageView``) are scoped to this tenant model.
58
+
59
+
@@ -1,200 +1,209 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module MultiTenant
2
4
  class ArelVisitorsDepthFirst < Arel::Visitors::Visitor
3
5
  def initialize(block = nil)
4
- @block = block || Proc.new
6
+ @block = block || proc
5
7
  super()
6
8
  end
7
9
 
8
10
  private
9
11
 
10
- def visit(o, _ = nil)
11
- super
12
- @block.call o
13
- end
12
+ def visit(obj, _ = nil)
13
+ super
14
+ @block.call obj
15
+ end
14
16
 
15
- def unary(o)
16
- visit o.expr
17
- end
18
- alias :visit_Arel_Nodes_Else :unary
19
- alias :visit_Arel_Nodes_Group :unary
20
- alias :visit_Arel_Nodes_Cube :unary
21
- alias :visit_Arel_Nodes_RollUp :unary
22
- alias :visit_Arel_Nodes_GroupingSet :unary
23
- alias :visit_Arel_Nodes_GroupingElement :unary
24
- alias :visit_Arel_Nodes_Grouping :unary
25
- alias :visit_Arel_Nodes_Having :unary
26
- alias :visit_Arel_Nodes_Lateral :unary
27
- alias :visit_Arel_Nodes_Limit :unary
28
- alias :visit_Arel_Nodes_Not :unary
29
- alias :visit_Arel_Nodes_Offset :unary
30
- alias :visit_Arel_Nodes_On :unary
31
- alias :visit_Arel_Nodes_Ordering :unary
32
- alias :visit_Arel_Nodes_Ascending :unary
33
- alias :visit_Arel_Nodes_Descending :unary
34
- alias :visit_Arel_Nodes_UnqualifiedColumn :unary
35
- alias :visit_Arel_Nodes_OptimizerHints :unary
36
- alias :visit_Arel_Nodes_ValuesList :unary
37
-
38
- def function(o)
39
- visit o.expressions
40
- visit o.alias
41
- visit o.distinct
42
- end
43
- alias :visit_Arel_Nodes_Avg :function
44
- alias :visit_Arel_Nodes_Exists :function
45
- alias :visit_Arel_Nodes_Max :function
46
- alias :visit_Arel_Nodes_Min :function
47
- alias :visit_Arel_Nodes_Sum :function
48
-
49
- def visit_Arel_Nodes_NamedFunction(o)
50
- visit o.name
51
- visit o.expressions
52
- visit o.distinct
53
- visit o.alias
54
- end
17
+ def unary(obj)
18
+ visit obj.expr
19
+ end
20
+ alias visit_Arel_Nodes_Else unary
21
+ alias visit_Arel_Nodes_Group unary
22
+ alias visit_Arel_Nodes_Cube unary
23
+ alias visit_Arel_Nodes_RollUp unary
24
+ alias visit_Arel_Nodes_GroupingSet unary
25
+ alias visit_Arel_Nodes_GroupingElement unary
26
+ alias visit_Arel_Nodes_Grouping unary
27
+ alias visit_Arel_Nodes_Having unary
28
+ alias visit_Arel_Nodes_Lateral unary
29
+ alias visit_Arel_Nodes_Limit unary
30
+ alias visit_Arel_Nodes_Not unary
31
+ alias visit_Arel_Nodes_Offset unary
32
+ alias visit_Arel_Nodes_On unary
33
+ alias visit_Arel_Nodes_Ordering unary
34
+ alias visit_Arel_Nodes_Ascending unary
35
+ alias visit_Arel_Nodes_Descending unary
36
+ alias visit_Arel_Nodes_UnqualifiedColumn unary
37
+ alias visit_Arel_Nodes_OptimizerHints unary
38
+ alias visit_Arel_Nodes_ValuesList unary
39
+
40
+ def function(obj)
41
+ visit obj.expressions
42
+ visit obj.alias
43
+ visit obj.distinct
44
+ end
45
+ alias visit_Arel_Nodes_Avg function
46
+ alias visit_Arel_Nodes_Exists function
47
+ alias visit_Arel_Nodes_Max function
48
+ alias visit_Arel_Nodes_Min function
49
+ alias visit_Arel_Nodes_Sum function
50
+
51
+ # rubocop:disable Naming/MethodName
52
+
53
+ def visit_Arel_Nodes_NamedFunction(obj)
54
+ visit obj.name
55
+ visit obj.expressions
56
+ visit obj.distinct
57
+ visit obj.alias
58
+ end
55
59
 
56
- def visit_Arel_Nodes_Count(o)
57
- visit o.expressions
58
- visit o.alias
59
- visit o.distinct
60
- end
60
+ def visit_Arel_Nodes_Count(obj)
61
+ visit obj.expressions
62
+ visit obj.alias
63
+ visit obj.distinct
64
+ end
61
65
 
62
- def visit_Arel_Nodes_Case(o)
63
- visit o.case
64
- visit o.conditions
65
- visit o.default
66
- end
66
+ def visit_Arel_Nodes_Case(obj)
67
+ visit obj.case
68
+ visit obj.conditions
69
+ visit obj.default
70
+ end
67
71
 
68
- def nary(o)
69
- o.children.each { |child| visit child }
70
- end
71
- alias :visit_Arel_Nodes_And :nary
72
+ def nary(obj)
73
+ obj.children.each { |child| visit child }
74
+ end
75
+ alias visit_Arel_Nodes_And nary
72
76
 
73
- def binary(o)
74
- visit o.left
75
- visit o.right
76
- end
77
- alias :visit_Arel_Nodes_As :binary
78
- alias :visit_Arel_Nodes_Assignment :binary
79
- alias :visit_Arel_Nodes_Between :binary
80
- alias :visit_Arel_Nodes_Concat :binary
81
- alias :visit_Arel_Nodes_DeleteStatement :binary
82
- alias :visit_Arel_Nodes_DoesNotMatch :binary
83
- alias :visit_Arel_Nodes_Equality :binary
84
- alias :visit_Arel_Nodes_FullOuterJoin :binary
85
- alias :visit_Arel_Nodes_GreaterThan :binary
86
- alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
87
- alias :visit_Arel_Nodes_In :binary
88
- alias :visit_Arel_Nodes_InfixOperation :binary
89
- alias :visit_Arel_Nodes_JoinSource :binary
90
- alias :visit_Arel_Nodes_InnerJoin :binary
91
- alias :visit_Arel_Nodes_LessThan :binary
92
- alias :visit_Arel_Nodes_LessThanOrEqual :binary
93
- alias :visit_Arel_Nodes_Matches :binary
94
- alias :visit_Arel_Nodes_NotEqual :binary
95
- alias :visit_Arel_Nodes_NotIn :binary
96
- alias :visit_Arel_Nodes_NotRegexp :binary
97
- alias :visit_Arel_Nodes_IsNotDistinctFrom :binary
98
- alias :visit_Arel_Nodes_IsDistinctFrom :binary
99
- alias :visit_Arel_Nodes_Or :binary
100
- alias :visit_Arel_Nodes_OuterJoin :binary
101
- alias :visit_Arel_Nodes_Regexp :binary
102
- alias :visit_Arel_Nodes_RightOuterJoin :binary
103
- alias :visit_Arel_Nodes_TableAlias :binary
104
- alias :visit_Arel_Nodes_When :binary
105
-
106
- def visit_Arel_Nodes_StringJoin(o)
107
- visit o.left
108
- end
77
+ def binary(obj)
78
+ visit obj.left
79
+ visit obj.right
80
+ end
81
+ alias visit_Arel_Nodes_As binary
82
+ alias visit_Arel_Nodes_Assignment binary
83
+ alias visit_Arel_Nodes_Between binary
84
+ alias visit_Arel_Nodes_Concat binary
85
+ alias visit_Arel_Nodes_DeleteStatement binary
86
+ alias visit_Arel_Nodes_DoesNotMatch binary
87
+ alias visit_Arel_Nodes_Equality binary
88
+ alias visit_Arel_Nodes_FullOuterJoin binary
89
+ alias visit_Arel_Nodes_GreaterThan binary
90
+ alias visit_Arel_Nodes_GreaterThanOrEqual binary
91
+ alias visit_Arel_Nodes_In binary
92
+ alias visit_Arel_Nodes_InfixOperation binary
93
+ alias visit_Arel_Nodes_JoinSource binary
94
+ alias visit_Arel_Nodes_InnerJoin binary
95
+ alias visit_Arel_Nodes_LessThan binary
96
+ alias visit_Arel_Nodes_LessThanOrEqual binary
97
+ alias visit_Arel_Nodes_Matches binary
98
+ alias visit_Arel_Nodes_NotEqual binary
99
+ alias visit_Arel_Nodes_NotIn binary
100
+ alias visit_Arel_Nodes_NotRegexp binary
101
+ alias visit_Arel_Nodes_IsNotDistinctFrom binary
102
+ alias visit_Arel_Nodes_IsDistinctFrom binary
103
+ alias visit_Arel_Nodes_Or binary
104
+ alias visit_Arel_Nodes_OuterJoin binary
105
+ alias visit_Arel_Nodes_Regexp binary
106
+ alias visit_Arel_Nodes_RightOuterJoin binary
107
+ alias visit_Arel_Nodes_TableAlias binary
108
+ alias visit_Arel_Nodes_When binary
109
+
110
+ def visit_Arel_Nodes_StringJoin(obj)
111
+ visit obj.left
112
+ end
109
113
 
110
- def visit_Arel_Attribute(o)
111
- visit o.relation
112
- visit o.name
113
- end
114
- alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
115
- alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
116
- alias :visit_Arel_Attributes_String :visit_Arel_Attribute
117
- alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
118
- alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
119
- alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
120
- alias :visit_Arel_Attributes_Decimal :visit_Arel_Attribute
121
-
122
- def visit_Arel_Table(o)
123
- visit o.name
124
- end
114
+ def visit_Arel_Attribute(obj)
115
+ visit obj.relation
116
+ visit obj.name
117
+ end
118
+ alias visit_Arel_Attributes_Integer visit_Arel_Attribute
119
+ alias visit_Arel_Attributes_Float visit_Arel_Attribute
120
+ alias visit_Arel_Attributes_String visit_Arel_Attribute
121
+ alias visit_Arel_Attributes_Time visit_Arel_Attribute
122
+ alias visit_Arel_Attributes_Boolean visit_Arel_Attribute
123
+ alias visit_Arel_Attributes_Attribute visit_Arel_Attribute
124
+ alias visit_Arel_Attributes_Decimal visit_Arel_Attribute
125
+
126
+ def visit_Arel_Table(obj)
127
+ visit obj.name
128
+ end
125
129
 
126
- def terminal(o)
127
- end
128
- alias :visit_ActiveSupport_Multibyte_Chars :terminal
129
- alias :visit_ActiveSupport_StringInquirer :terminal
130
- alias :visit_Arel_Nodes_Lock :terminal
131
- alias :visit_Arel_Nodes_Node :terminal
132
- alias :visit_Arel_Nodes_SqlLiteral :terminal
133
- alias :visit_Arel_Nodes_BindParam :terminal
134
- alias :visit_Arel_Nodes_Window :terminal
135
- alias :visit_Arel_Nodes_True :terminal
136
- alias :visit_Arel_Nodes_False :terminal
137
- alias :visit_BigDecimal :terminal
138
- alias :visit_Class :terminal
139
- alias :visit_Date :terminal
140
- alias :visit_DateTime :terminal
141
- alias :visit_FalseClass :terminal
142
- alias :visit_Float :terminal
143
- alias :visit_Integer :terminal
144
- alias :visit_NilClass :terminal
145
- alias :visit_String :terminal
146
- alias :visit_Symbol :terminal
147
- alias :visit_Time :terminal
148
- alias :visit_TrueClass :terminal
149
-
150
- def visit_Arel_Nodes_InsertStatement(o)
151
- visit o.relation
152
- visit o.columns
153
- visit o.values
154
- end
130
+ def terminal(obj); end
131
+ alias visit_ActiveSupport_Multibyte_Chars terminal
132
+ alias visit_ActiveSupport_StringInquirer terminal
133
+ alias visit_Arel_Nodes_Lock terminal
134
+ alias visit_Arel_Nodes_Node terminal
135
+ alias visit_Arel_Nodes_SqlLiteral terminal
136
+ alias visit_Arel_Nodes_BindParam terminal
137
+ alias visit_Arel_Nodes_Window terminal
138
+ alias visit_Arel_Nodes_True terminal
139
+ alias visit_Arel_Nodes_False terminal
140
+ alias visit_BigDecimal terminal
141
+ alias visit_Class terminal
142
+ alias visit_Date terminal
143
+ alias visit_DateTime terminal
144
+ alias visit_FalseClass terminal
145
+ alias visit_Float terminal
146
+ alias visit_Integer terminal
147
+ alias visit_NilClass terminal
148
+ alias visit_String terminal
149
+ alias visit_Symbol terminal
150
+ alias visit_Time terminal
151
+ alias visit_TrueClass terminal
152
+
153
+ def visit_Arel_Nodes_InsertStatement(obj)
154
+ visit obj.relation
155
+ visit obj.columns
156
+ visit obj.values
157
+ end
155
158
 
156
- def visit_Arel_Nodes_SelectCore(o)
157
- visit o.projections
158
- visit o.source
159
- visit o.wheres
160
- visit o.groups
161
- visit o.windows
162
- visit o.havings
163
- end
159
+ def visit_Arel_Nodes_SelectCore(obj)
160
+ visit obj.projections
161
+ visit obj.source
162
+ visit obj.wheres
163
+ visit obj.groups
164
+ visit obj.windows
165
+ visit obj.havings
166
+ end
164
167
 
165
- def visit_Arel_Nodes_SelectStatement(o)
166
- visit o.cores
167
- visit o.orders
168
- visit o.limit
169
- visit o.lock
170
- visit o.offset
171
- end
168
+ def visit_Arel_Nodes_SelectStatement(obj)
169
+ visit obj.cores
170
+ visit obj.orders
171
+ visit obj.limit
172
+ visit obj.lock
173
+ visit obj.offset
174
+ end
172
175
 
173
- def visit_Arel_Nodes_UpdateStatement(o)
174
- visit o.relation
175
- visit o.values
176
- visit o.wheres
177
- visit o.orders
178
- visit o.limit
179
- end
176
+ def visit_Arel_Nodes_UpdateStatement(obj)
177
+ visit obj.relation
178
+ visit obj.values
179
+ visit obj.wheres
180
+ visit obj.orders
181
+ visit obj.limit
182
+ end
180
183
 
181
- def visit_Arel_Nodes_Comment(o)
182
- visit o.values
183
- end
184
+ def visit_Arel_Nodes_Comment(obj)
185
+ visit obj.values
186
+ end
184
187
 
185
- def visit_Array(o)
186
- o.each { |i| visit i }
187
- end
188
- alias :visit_Set :visit_Array
188
+ def visit_Array(obj)
189
+ obj.each { |i| visit i }
190
+ end
191
+ alias visit_Set visit_Array
189
192
 
190
- def visit_Hash(o)
191
- o.each { |k, v| visit(k); visit(v) }
193
+ def visit_Hash(obj)
194
+ obj.each do |k, v|
195
+ visit(k)
196
+ visit(v)
192
197
  end
198
+ end
193
199
 
194
- DISPATCH = dispatch_cache
200
+ DISPATCH = dispatch_cache
195
201
 
196
- def get_dispatch_cache
197
- DISPATCH
198
- end
202
+ # rubocop:disable Naming/AccessorMethodName
203
+ def get_dispatch_cache
204
+ DISPATCH
205
+ end
206
+ # rubocop:enable Naming/AccessorMethodName
207
+ # rubocop:enable Naming/MethodName
199
208
  end
200
209
  end
@@ -1,16 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Extension to the controller to allow setting the current tenant
4
+ # set_current_tenant and current_tenant methods are introduced
5
+ # to set and get the current tenant in the controllers that uses
6
+ # the MultiTenant module.
1
7
  module MultiTenant
2
8
  module ControllerExtensions
3
9
  def set_current_tenant_through_filter
4
- self.class_eval do
5
- if respond_to?(:helper_method)
6
- helper_method :current_tenant
7
- end
10
+ class_eval do
11
+ helper_method :current_tenant if respond_to?(:helper_method)
8
12
 
9
13
  private
10
14
 
15
+ # rubocop:disable Naming/AccessorMethodName
11
16
  def set_current_tenant(current_tenant_object)
12
17
  MultiTenant.current_tenant = current_tenant_object
13
18
  end
19
+ # rubocop:enable Naming/AccessorMethodName
14
20
 
15
21
  def current_tenant
16
22
  MultiTenant.current_tenant
@@ -20,6 +26,11 @@ module MultiTenant
20
26
  end
21
27
  end
22
28
 
29
+ # This block is executed when the file is loaded and
30
+ # makes the base class; ActionController::Base to
31
+ # extend the ControllerExtensions module.
32
+ # This will add the set_current_tenant and current_tenant
33
+ # in all the controllers that inherit from ActionController::Base
23
34
  ActiveSupport.on_load(:action_controller) do |base|
24
35
  base.extend MultiTenant::ControllerExtensions
25
36
  end
@@ -1,4 +1,7 @@
1
1
  module MultiTenant
2
+ # Designed to be mixed into an ActiveRecord model to provide
3
+ # a copy_from_client method that allows for efficient bulk insertion of
4
+ # data into a PostgreSQL database using the COPY command
2
5
  class CopyFromClientHelper
3
6
  attr_reader :count
4
7
 
@@ -28,6 +31,7 @@ module MultiTenant
28
31
  end
29
32
  end
30
33
 
34
+ # Add copy_from_client to ActiveRecord::Base
31
35
  ActiveSupport.on_load(:active_record) do |base|
32
36
  base.extend(MultiTenant::CopyFromClient)
33
37
  end
@@ -1,5 +1,6 @@
1
1
  # Truncates only the tables that have been modified, according to sequence
2
2
  # values
3
+ # Faster alternative to DatabaseCleaner.clean_with(:truncation, pre_count: true)
3
4
  module MultiTenant
4
5
  module FastTruncate
5
6
  def self.run(exclude: ['schema_migrations'])
@@ -13,7 +14,8 @@ module MultiTenant
13
14
  needs_truncate boolean;
14
15
  BEGIN
15
16
  FOR t IN SELECT schemaname, tablename FROM pg_tables WHERE schemaname = 'public' AND tablename NOT IN (%s) LOOP
16
- EXECUTE 'SELECT EXISTS (SELECT * from pg_class c WHERE c.relkind = ''S'' AND c.relname=''' || t.tablename || '_id_seq'')' into seq_exists;
17
+ EXECUTE 'SELECT EXISTS (SELECT * from pg_class c WHERE c.relkind = ''S''
18
+ AND c.relname=''' || t.tablename || '_id_seq'')' into seq_exists;
17
19
  IF seq_exists THEN
18
20
  EXECUTE 'SELECT is_called FROM ' || t.tablename || '_id_seq' INTO needs_truncate;
19
21
  ELSE
@@ -28,7 +30,7 @@ module MultiTenant
28
30
  IF array_length(tables, 1) > 0 THEN
29
31
  EXECUTE 'TRUNCATE TABLE ' || array_to_string(tables, ', ') || ' RESTART IDENTITY CASCADE';
30
32
  END IF;
31
- END$$;), exclude.map { |t| "'" + t + "'" }.join('\n'))
33
+ END$$;), exclude.map { |t| "'#{t}'" }.join('\n'))
32
34
  end
33
35
  end
34
36
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module extension is a monkey patch to the ActiveRecord::Associations::ClassMethods module.
4
+ # It overrides the has_and_belongs_to_many method to add the tenant_id to the join table if the
5
+ # tenant_enabled option is set to true.
6
+
7
+ module ActiveRecord
8
+ module Associations
9
+ module ClassMethods
10
+ # rubocop:disable Naming/PredicateName
11
+ def has_and_belongs_to_many_with_tenant(name, scope = nil, **options, &extension)
12
+ # rubocop:enable Naming/PredicateName
13
+ has_and_belongs_to_many_without_tenant(name, scope, **options, &extension)
14
+
15
+ middle_reflection = _reflections[name.to_s].through_reflection
16
+ join_model = middle_reflection.klass
17
+
18
+ # get tenant_enabled from options and if it is not set, set it to false
19
+ tenant_enabled = options[:tenant_enabled] || false
20
+
21
+ return unless tenant_enabled
22
+
23
+ tenant_class_name = options[:tenant_class_name]
24
+ tenant_column = options[:tenant_column]
25
+
26
+ match = tenant_column.match(/(\w+)_id/)
27
+ tenant_field_name = match ? match[1] : 'tenant'
28
+
29
+ join_model.class_eval do
30
+ belongs_to tenant_field_name.to_sym, class_name: tenant_class_name
31
+ before_create :tenant_set
32
+
33
+ private
34
+
35
+ # This method sets the tenant_id on the join table and executes before creation of the join table record.
36
+ define_method :tenant_set do
37
+ if tenant_enabled
38
+ raise MultiTenant::MissingTenantError, 'Tenant Id is not set' unless MultiTenant.current_tenant_id
39
+
40
+ send("#{tenant_column}=", MultiTenant.current_tenant_id)
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ alias has_and_belongs_to_many_without_tenant has_and_belongs_to_many
47
+ alias has_and_belongs_to_many has_and_belongs_to_many_with_tenant
48
+ end
49
+ end
50
+ end