bp3-core 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '02341017029035ce15928115c68d1b142a43d46474e8b7ea989c3c67d38ae323'
4
+ data.tar.gz: 4e54a3d0c7357386472cf811707c9c990cbbd20541dad265b73d5f24224bfbaa
5
+ SHA512:
6
+ metadata.gz: 88c7855cd29ba6a7d33f404d8947d75df4ea27d11bfed33ea34a242e1fe3e31624c2619935d8f5018f2df684c5973f95954e016d125d009915923b87be44247d
7
+ data.tar.gz: bf68641f064961b9de03cc3cdf9a70b63d9c2e4a362d4b8b7dec70cee5564ceb550042826c480ed3d4eec68680d8bb95d0731a26f50df61352dce3e202a23606
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,43 @@
1
+ require:
2
+ - rubocop-rake
3
+ - rubocop-rspec
4
+ - rubocop-capybara
5
+ - rubocop-factory_bot
6
+
7
+ AllCops:
8
+ TargetRubyVersion: 3.2.2
9
+ NewCops: enable
10
+
11
+ Gemspec/DevelopmentDependencies:
12
+ EnforcedStyle: gemspec
13
+
14
+ Metrics/AbcSize:
15
+ Max: 26
16
+
17
+ Metrics/BlockLength:
18
+ Max: 66
19
+
20
+ Metrics/CyclomaticComplexity:
21
+ Max: 10
22
+
23
+ Metrics/MethodLength:
24
+ Max: 15
25
+
26
+ Metrics/ModuleLength:
27
+ Max: 150
28
+
29
+ Metrics/PerceivedComplexity:
30
+ Max: 10
31
+
32
+ Naming/FileName:
33
+ Exclude:
34
+ - lib/bp3-*.rb
35
+
36
+ RSpec/ExampleLength:
37
+ Max: 10
38
+
39
+ RSpec/MultipleExpectations:
40
+ Max: 4
41
+
42
+ Style/Documentation:
43
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1,2 @@
1
+ ruby-3.2.2
2
+
data/.yardopts ADDED
@@ -0,0 +1,8 @@
1
+ --charset utf-8
2
+ --readme README.md
3
+ --markup markdown
4
+ --title "bp3-core"
5
+ --private
6
+ -
7
+ CHANGELOG.md
8
+ LICENSE.txt
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.2] - 2024-05-30
4
+
5
+ - Explicit require active_support/parameter_filter
6
+
7
+ ## [0.1.1] - 2024-05-30
8
+
9
+ - Tweaks and documentation
10
+
11
+ ## [0.1.0] - 2023-12-19
12
+
13
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in bp3-core.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,120 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bp3-core (0.1.2)
5
+ actionview (>= 7.1.2, < 8)
6
+ activesupport (>= 7.1.2, < 8)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actionview (7.1.3)
12
+ activesupport (= 7.1.3)
13
+ builder (~> 3.1)
14
+ erubi (~> 1.11)
15
+ rails-dom-testing (~> 2.2)
16
+ rails-html-sanitizer (~> 1.6)
17
+ activesupport (7.1.3)
18
+ base64
19
+ bigdecimal
20
+ concurrent-ruby (~> 1.0, >= 1.0.2)
21
+ connection_pool (>= 2.2.5)
22
+ drb
23
+ i18n (>= 1.6, < 2)
24
+ minitest (>= 5.1)
25
+ mutex_m
26
+ tzinfo (~> 2.0)
27
+ ast (2.4.2)
28
+ base64 (0.2.0)
29
+ bigdecimal (3.1.6)
30
+ builder (3.2.4)
31
+ concurrent-ruby (1.2.3)
32
+ connection_pool (2.4.1)
33
+ crass (1.0.6)
34
+ diff-lcs (1.5.0)
35
+ drb (2.2.0)
36
+ ruby2_keywords
37
+ erubi (1.12.0)
38
+ i18n (1.14.1)
39
+ concurrent-ruby (~> 1.0)
40
+ json (2.7.1)
41
+ language_server-protocol (3.17.0.3)
42
+ loofah (2.22.0)
43
+ crass (~> 1.0.2)
44
+ nokogiri (>= 1.12.0)
45
+ minitest (5.21.2)
46
+ mutex_m (0.2.0)
47
+ nokogiri (1.16.5-x86_64-darwin)
48
+ racc (~> 1.4)
49
+ parallel (1.24.0)
50
+ parser (3.3.0.4)
51
+ ast (~> 2.4.1)
52
+ racc
53
+ racc (1.7.3)
54
+ rails-dom-testing (2.2.0)
55
+ activesupport (>= 5.0.0)
56
+ minitest
57
+ nokogiri (>= 1.6)
58
+ rails-html-sanitizer (1.6.0)
59
+ loofah (~> 2.21)
60
+ nokogiri (~> 1.14)
61
+ rainbow (3.1.1)
62
+ rake (13.1.0)
63
+ regexp_parser (2.9.0)
64
+ rexml (3.2.6)
65
+ rspec (3.12.0)
66
+ rspec-core (~> 3.12.0)
67
+ rspec-expectations (~> 3.12.0)
68
+ rspec-mocks (~> 3.12.0)
69
+ rspec-core (3.12.2)
70
+ rspec-support (~> 3.12.0)
71
+ rspec-expectations (3.12.3)
72
+ diff-lcs (>= 1.2.0, < 2.0)
73
+ rspec-support (~> 3.12.0)
74
+ rspec-mocks (3.12.6)
75
+ diff-lcs (>= 1.2.0, < 2.0)
76
+ rspec-support (~> 3.12.0)
77
+ rspec-support (3.12.1)
78
+ rubocop (1.60.1)
79
+ json (~> 2.3)
80
+ language_server-protocol (>= 3.17.0)
81
+ parallel (~> 1.10)
82
+ parser (>= 3.3.0.2)
83
+ rainbow (>= 2.2.2, < 4.0)
84
+ regexp_parser (>= 1.8, < 3.0)
85
+ rexml (>= 3.2.5, < 4.0)
86
+ rubocop-ast (>= 1.30.0, < 2.0)
87
+ ruby-progressbar (~> 1.7)
88
+ unicode-display_width (>= 2.4.0, < 3.0)
89
+ rubocop-ast (1.30.0)
90
+ parser (>= 3.2.1.0)
91
+ rubocop-capybara (2.20.0)
92
+ rubocop (~> 1.41)
93
+ rubocop-factory_bot (2.25.1)
94
+ rubocop (~> 1.41)
95
+ rubocop-rake (0.6.0)
96
+ rubocop (~> 1.0)
97
+ rubocop-rspec (2.26.1)
98
+ rubocop (~> 1.40)
99
+ rubocop-capybara (~> 2.17)
100
+ rubocop-factory_bot (~> 2.22)
101
+ ruby-progressbar (1.13.0)
102
+ ruby2_keywords (0.0.5)
103
+ tzinfo (2.0.6)
104
+ concurrent-ruby (~> 1.0)
105
+ unicode-display_width (2.5.0)
106
+
107
+ PLATFORMS
108
+ x86_64-darwin-21
109
+ x86_64-darwin-22
110
+
111
+ DEPENDENCIES
112
+ bp3-core!
113
+ rake (~> 13.0)
114
+ rspec (~> 3.0)
115
+ rubocop (~> 1.21)
116
+ rubocop-rake (~> 0.6)
117
+ rubocop-rspec (~> 2.25)
118
+
119
+ BUNDLED WITH
120
+ 2.5.11
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023-2024 Wim den Braven
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # Bp3::Core
2
+
3
+ bp3-core provides core concerns for the persuavis/black_phoebe_3 multi-site multi-tenant rails application.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'bp3-core'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install bp3-core
20
+
21
+ ## Usage
22
+
23
+ In all controllers (or their base class), add
24
+ ```ruby
25
+ include Bp3::Core::Actions
26
+ include Bp3::Core::Cookies
27
+ ```
28
+ In ActiveRecord models(or their base class) that are displayed to users, add:
29
+ ```ruby
30
+ include Bp3::Core::Displayable
31
+ ```
32
+ In models, controllers, services and helpers that use feature flags, add:
33
+ ```ruby
34
+ include Bp3::Core::FeatureFlags
35
+ include Bp3::Core::Settings
36
+ ```
37
+ In ActiveRecord models (or their base class) that are filtered/sorted with ransack, add:
38
+ ```ruby
39
+ include Bp3::Core::Ransackable
40
+ ```
41
+ In all ActiveRecord models (or their base class) with a request-id attribute :rqid, add:
42
+ ```ruby
43
+ include Bp3::Core::Rqid
44
+ ```
45
+ In all ActiveRecord models (or their base class) with a sequence number attribute :sqnr, add:
46
+ ```ruby
47
+ include Bp3::Core::Sqnr
48
+ ```
49
+ To use :sqnr for record ordering for a particular model, use the class macro:
50
+ ```ruby
51
+ use_sqnr_for_ordering
52
+ ```
53
+ In all ActiveRecord models (or their base class) that use site, tenant and/or workspace attributes that need
54
+ to be populated from global state, add:
55
+ ```ruby
56
+ include Bp3::Core::Tenantable
57
+ ```
58
+ The specific columns expected by `Tenantable` are:
59
+ - site: `sites_site_id`
60
+ - tenant: `tenant_id`
61
+ - workspace: `workspaces_workspace_id`
62
+ Tenantable will use reflection to determine which one(s) exist, and will create associations and callbacks accordingly.
63
+
64
+ ## Development
65
+
66
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
67
+
68
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
69
+
70
+ ## Testing
71
+ The `Bp3::Core::Test` class is for testing purposes.
72
+
73
+ ## Documentation
74
+ A `.yardopts` file is provided to support yard documentation.
75
+
76
+ ## Contributing
77
+
78
+ Bug reports and pull requests are welcome on GitHub at https://github.com/persuavis/bp3-core.
79
+
80
+ ## License
81
+
82
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/bp3-core.gemspec ADDED
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/bp3/core/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'bp3-core'
7
+ spec.version = Bp3::Core::VERSION
8
+ spec.authors = ['Wim den Braven']
9
+ spec.email = ['wimdenbraven@persuavis.com']
10
+
11
+ spec.summary = 'bp3-core provides core concerns for black_phoebe_3.'
12
+ # spec.description = "TODO: Write a longer description or delete this line."
13
+ spec.homepage = 'https://www.black-phoebe.com'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.2.0'
16
+
17
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/persuavis/bp3-core'
21
+ spec.metadata['changelog_uri'] = 'https://github.com/persuavis/bp3-core/blob/main/CHANGELOG.md'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
28
+ end
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ spec.add_dependency 'actionview', ['>= 7.1.2', '< 8']
35
+ spec.add_dependency 'activesupport', ['>= 7.1.2', '< 8']
36
+
37
+ spec.add_development_dependency 'rake', '~> 13.0'
38
+ spec.add_development_dependency 'rspec', '~> 3.0'
39
+ spec.add_development_dependency 'rubocop', '~> 1.21'
40
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6'
41
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.25'
42
+
43
+ # For more information and examples about making a new gem, check out our
44
+ # guide at: https://bundler.io/guides/creating_gem.html
45
+ spec.metadata['rubygems_mfa_required'] = 'true'
46
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bp3
4
+ module Core
5
+ module Actions
6
+ extend ActiveSupport::Concern
7
+
8
+ # include FeatureFlags # do not include here
9
+ # include Settings # do not include here
10
+
11
+ included do
12
+ # Order matters! don't change it. These prepends are from last to first
13
+ prepend_before_action :check_tenant
14
+ prepend_before_action :check_workspace
15
+ prepend_before_action :check_site
16
+ prepend_before_action :set_rqid
17
+ before_action :set_paper_trail_whodunnit # uses user_for_paper_trail
18
+ before_action :find_or_create_user_agent
19
+ before_action :create_request_record
20
+ before_action :check_site_mode
21
+
22
+ before_action :set_global_request_state
23
+ end
24
+
25
+ class_methods do
26
+ # To support Devise this needs to be a class method
27
+ def default_url_options(options = {})
28
+ out = super().merge(options)
29
+ ignore_locale = ENV.fetch('IGNORE_LOCALIZATION', false)
30
+ return out if ignore_locale
31
+
32
+ out.merge(locale: I18n.locale)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def set_global_request_state
39
+ GlobalRequestState.inbound_request = @global_request
40
+ GlobalRequestState.current_user = current_user
41
+ GlobalRequestState.current_admin = current_admin
42
+ GlobalRequestState.current_root = current_root
43
+ GlobalRequestState.locale = I18n.locale
44
+ GlobalRequestState.view_context = view_context
45
+ end
46
+
47
+ def set_rqid
48
+ GlobalRequestState.request_id = global_rqid
49
+ end
50
+
51
+ def global_rqid
52
+ @global_rqid ||= request.request_id || SecureRandom.uuid
53
+ end
54
+
55
+ def create_request_record
56
+ return if do_not_track
57
+ return unless create_request_record_is_on?
58
+
59
+ # Apparently, we can receive multiple requests with the same request_id
60
+ # TODO: relax the uniqueness constraint instead of re-using the existing record
61
+ @global_request = Inbound::Request.find_by(id: request.request_id)
62
+ return @global_request if @global_request
63
+
64
+ @global_request = Inbound::Request.create!(id: global_rqid,
65
+ url: request.url,
66
+ ip_address: request.ip,
67
+ referer: request.referer,
68
+ inbound_user_agent: @global_user_agent,
69
+ sites_site: current_site)
70
+ end
71
+
72
+ def find_or_create_user_agent
73
+ return if do_not_track
74
+
75
+ user_agent = request.user_agent.presence || 'user-agent-is-blank'
76
+ @global_user_agent = Inbound::UserAgent.find_or_create_by!(user_agent:)
77
+ rescue ActiveRecord::RecordInvalid
78
+ # probably hit a race condition. Try again and log
79
+ @global_user_agent = Inbound::UserAgent.find_by!(user_agent:)
80
+ message = "User agent already existed (#{@global_user_agent.id}/#{user_agent})"
81
+ log_warn(key: 'find_or_create_user_agent', message:)
82
+ @global_user_agent
83
+ end
84
+
85
+ def check_site
86
+ current_site || create_site
87
+ GlobalRequestState.current_site = current_site
88
+ end
89
+
90
+ def current_site
91
+ return @current_site if @current_site
92
+
93
+ @current_site = request.request_site
94
+ end
95
+
96
+ def create_site
97
+ @current_site = Sites::Site.create!(request_domain:)
98
+ end
99
+
100
+ # TODO: cache the workspace/regex list
101
+ def check_workspace
102
+ current_workspace || create_workspace
103
+ GlobalRequestState.current_workspace = current_workspace
104
+ end
105
+
106
+ # TODO: move (some of) this into Workspace
107
+ def current_workspace
108
+ return @current_workspace if @current_workspace
109
+ return nil if current_site.nil?
110
+
111
+ current_site.workspaces.find_each do |workspace|
112
+ if workspace.has_pattern_match?(request.subdomain)
113
+ @current_workspace = workspace
114
+ return @current_workspace
115
+ end
116
+ end
117
+ end
118
+
119
+ # TODO: move (some of) this into Workspace
120
+ def create_workspace
121
+ workspace_type = Workspaces::WorkspaceType
122
+ .find_or_create_by!(sites_site: current_site, name: 'default')
123
+ @current_workspace = Workspaces::Workspace.create!(sites_site: current_site,
124
+ name: 'default', match_pattern: '\A.*\z',
125
+ workspaces_workspace_type: workspace_type)
126
+ end
127
+
128
+ def check_tenant
129
+ current_tenant || create_tenant
130
+ GlobalRequestState.current_tenant = current_tenant
131
+ end
132
+
133
+ def current_tenant
134
+ return @current_tenant if @current_tenant
135
+
136
+ @current_tenant = current_site.tenants.find_by(tenantable:)
137
+ end
138
+
139
+ def create_tenant
140
+ @current_tenant = Tenant.create!(sites_site: current_site, tenantable:)
141
+ end
142
+
143
+ def tenantable
144
+ @tenantable ||= multi_tenant_is_on? ? current_workspace : current_site
145
+ end
146
+
147
+ def request_domain
148
+ @request_domain ||= DomainExtractor.extract_domain(request_host) || request.domain || request_host
149
+ end
150
+
151
+ def request_host
152
+ request.normalized_host
153
+ end
154
+
155
+ def request_subdomain
156
+ return @request_subdomain if @request_subdomain
157
+
158
+ subdomain = request_host.gsub(/#{request_domain}\z/, '')
159
+ subdomain = subdomain[0..-2] if subdomain.ends_with?('.')
160
+ @request_subdomain = subdomain
161
+ end
162
+
163
+ def info_for_paper_trail
164
+ state = GlobalRequestState.new
165
+ {
166
+ rqid: global_rqid,
167
+ sites_site_id: state.current_site&.id,
168
+ tenant_id: state.current_tenant&.id,
169
+ workspaces_workspace_id: state.current_workspace&.id,
170
+ root_id: state.current_root&.id,
171
+ sites_admin_id: state.current_admin&.id,
172
+ users_user_id: state.current_user&.id
173
+ }
174
+ end
175
+
176
+ def set_ransack_auth_object
177
+ current_root || current_admin || current_user
178
+ end
179
+
180
+ # used by set_paper_trail_whodunnit
181
+ def user_for_paper_trail
182
+ current_root || current_admin || current_user || current_visitor || 'guest'
183
+ end
184
+
185
+ def pundit_user
186
+ UserContext.new
187
+ end
188
+
189
+ def either_site
190
+ GlobalRequestState.either_site
191
+ end
192
+
193
+ def check_site_mode
194
+ return unless current_site.sites_modes.enabled.timely.any?
195
+
196
+ current_site.sites_modes.enabled.timely.each do |mode|
197
+ next unless mode.name == 'maintenance'
198
+
199
+ @resource = @sites_mode = mode
200
+ @content = @resource.content
201
+ @content_format = @resource.content_format
202
+ render template: 'sites/modes/maintenance'
203
+ break
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end