bp3-core 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a9c4c0dbb3305cf4f01673f5774ec40ffc8ea92ce204cdb29a474de30fec52b
4
- data.tar.gz: 1512daac46af4d57c18eb5953895f12ffafa352204792bae9865867640b0d60c
3
+ metadata.gz: 76165d42f4b28714655519eeddedc6d08dd0b5bc9a5e919b389f173aa0bf20a0
4
+ data.tar.gz: dba935fa44898891607c50b377cbb4d8636300c7285592838826331cc47d5da6
5
5
  SHA512:
6
- metadata.gz: 75409b743dd9c8f9fd2b9e33c585b8712599f4a0f1fb2066879a37f65105b5036264b29964f1ae635c16a468e4828640ad2474e476b8f72923a97ddb806275b5
7
- data.tar.gz: 1bd7fa7234f47dea8f6871a36718a6f11ec7dbb605bd81fca38a3d6d5e99b3c2b482dc9e30138d2a9d7007ee6943fcc201bac385ed0c09ebcbf50296fc8bd38d
6
+ metadata.gz: 548dd80c8000de996d2abe85c4e158b979e9c809957f1403f15519280ee2dcb3c1fa865bf4355bc6fa25d9a6cf19791bc09cf3d6d0a84b7c0a7888cdd4960c21
7
+ data.tar.gz: 6dbc07299dcc22c84643250fd8fe018dc2b8c94de6a30fe303431d61f38438ffa32c897f43132a49d4fa9d75491ceb33cf11e988c9c2a88e21406a7dd33e6abc
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2026-10-26
4
+
5
+ ### Breaking
6
+ - Scope Tenantable by the primary login
7
+ Tenantable default scopes reach as far as the primary login, the request state's
8
+ `current_login` (the least privileged one signed in), rather than as far as any root or admin signed
9
+ in. A root signed in beside a user or an admin no longer reaches every site, and an admin signed in
10
+ beside a user no longer reaches every tenant; with one login signed in, nothing changes
11
+ - Read request state through `Bp3::Core::Rqid.global_request_state_class`
12
+ Tenantable reads the request state through `Bp3::Core::Rqid.global_request_state_class`
13
+ rather than a `GlobalRequestState` constant, so `Bp3::Core::Rqid.global_request_state_class_name`
14
+ must be configured wherever Tenantable is used
15
+ - Add `Bp3::Core::Tenantable.reaches_every_site?`, `.reaches_every_tenant?` and `.global_request_state`
16
+
17
+ ### Bug fixes
18
+
19
+ - Fix efon? and cfon?
20
+ Fix `efon?` and `cfon?` returning the default when the flag is explicitly off; `cfon` returns the
21
+ default when the config is not set
22
+
3
23
  ## [0.2.0] - 2026-01-09
4
24
 
5
25
  - Add bp3-request_state dependency
data/CLAUDE.md CHANGED
@@ -4,95 +4,116 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
 
5
5
  ## Project Overview
6
6
 
7
- bp3-core is a Ruby gem providing core ActiveSupport concerns for BP3 (persuavis/black_phoebe_3), a multi-site multi-tenant Rails application. The gem provides reusable modules that handle common patterns like tenancy, feature flags, ransack integration, request tracking, and sequence numbering.
7
+ bp3-core is a Ruby gem providing core ActiveSupport concerns for BP3 (persuavis/black_phoebe_3), a
8
+ multi-site multi-tenant Rails application. The concerns handle tenancy, request tracking, sequence
9
+ ordering, ransack, feature flags, cookies and system logging, for the application's models and controllers
10
+ and for the other bp3 gems, which mix them into third-party classes (noticed, friendly_id, pg_search,
11
+ ActionText, ActiveStorage, paper_trail, ActiveAdmin, Rails' mailer previews).
8
12
 
9
- ## Common Commands
13
+ ## Commands
10
14
 
11
- ### Testing and Linting
12
- - Run tests: `bundle exec rspec` or `rake spec`
13
- - Run linting: `bundle exec rubocop` or `rake rubocop`
14
- - Run both tests and linting (default): `rake`
15
- - Run a single test file: `bundle exec rspec spec/path/to/file_spec.rb`
16
- - Run a specific test: `bundle exec rspec spec/path/to/file_spec.rb:LINE_NUMBER`
15
+ The gems run on Ruby 3.4.6 (`.ruby-version`). Prefix commands with `~/.rvm/bin/rvm 3.4.6 do` when the
16
+ shell is not already on it; a missing `rspec` means the wrong Ruby, not a missing bundle.
17
17
 
18
- ### Development
19
- - Install dependencies: `bundle install`
20
- - Interactive console: `bin/console`
21
- - Install gem locally: `rake install`
22
- - Generate documentation: `yard doc`
18
+ - `bundle exec rake` - specs and RuboCop (the default task)
19
+ - `bundle exec rspec spec/path/to/file_spec.rb[:LINE]` - one file or example
20
+ - `bundle exec rubocop` / `bundle exec rubocop -a`
21
+ - `bin/setup`, `bin/console`, `rake install`, `yard doc`
23
22
 
24
- ### Release
25
- - Update version in `lib/bp3/core/version.rb`
26
- - Run `rake release` to create git tag, push commits/tags, and publish to rubygems.org
23
+ The specs have no database or Rails app: test a concern's decisions through plain Ruby (e.g.
24
+ `Tenantable.reaches_every_site?` with a struct standing in for the request state), and try a change against
25
+ the application by putting this repo's `lib` ahead of the bundled gem. `lib/bp3/core/test.rb` is a plain
26
+ class that stubs `before_action`, `belongs_to` and the like and includes every concern; the gem does not
27
+ require it.
27
28
 
28
29
  ## Architecture
29
30
 
30
- ### Core Concerns
31
-
32
- The gem provides several ActiveSupport::Concern modules that are included in ActiveRecord models and controllers:
33
-
34
- **Tenantable** (`lib/bp3/core/tenantable.rb`)
35
- - Handles multi-tenant architecture with sites, tenants, and workspaces
36
- - Automatically sets foreign keys from GlobalRequestState
37
- - Creates associations: `sites_site_id` → Sites::Site, `tenant_id` → Tenant, `workspaces_workspace_id` → Workspaces::Workspace
38
- - Provides aliases: `site`/`site=`, `workspace`/`workspace=`
39
- - Implements default scopes based on current site/tenant context
40
- - Validates tenant and workspace belong to correct site
41
- - Uses reflection to determine which columns exist and only creates relevant associations
42
- - GlobalRequestState is expected to be defined in the consuming application
43
-
44
- **Ransackable** (`lib/bp3/core/ransackable.rb`)
45
- - Provides ransack integration for searchable/sortable models
46
- - Implements `ransackable_attributes`, `ransackable_associations`, `ransackable_scopes`
47
- - Uses global configuration for exceptions: `Bp3::Core::Ransackable.attribute_exceptions` and `Bp3::Core::Ransackable.association_exceptions`
48
- - Must be configured in `config/initializers/bp3-core.rb` in consuming application
49
-
50
- **Rqid** (`lib/bp3/core/rqid.rb`)
51
- - Tracks request ID (rqid) for all records
52
- - Automatically sets rqid on record creation from GlobalRequestState
53
- - Creates associations to original request, response, and visit records
54
- - Requires configuration of `Bp3::Core::Rqid.global_request_state_class_name` and `Bp3::Core::Rqid.global_request_state_method`
55
-
56
- **Sqnr** (`lib/bp3/core/sqnr.rb`)
57
- - Provides sequence number (sqnr) ordering
58
- - Adds `sqnr` (ascending) and `rnqs` (descending) scopes
59
- - Class macro `use_sqnr_for_ordering` sets `implicit_order_column` to sqnr
60
-
61
- **Actions**, **Cookies**, **Displayable**, **FeatureFlags**, **Settings**, **SystemLogs**
62
- - Additional concerns for controllers and models
63
- - See README.md for usage patterns
64
-
65
- ### Module Organization
66
-
67
- The gem follows standard Ruby gem structure:
68
- - `lib/bp3-core.rb` - Main entry point, requires `lib/bp3/core.rb`
69
- - `lib/bp3/core.rb` - Requires all concern modules, defines `Bp3::Core` namespace
70
- - `lib/bp3/core/*.rb` - Individual concern modules
71
- - Module attributes use `mattr_accessor` for configuration points that must be set by consuming applications
72
-
73
- ### Configuration Pattern
74
-
75
- Concerns that need application-specific configuration use `mattr_accessor` and class methods:
76
- - `Bp3::Core.system_exception_name` / `.system_log_name`
77
- - `Bp3::Core::Ransackable.attribute_exceptions` / `.association_exceptions`
78
- - `Bp3::Core::Rqid.global_request_state_class_name` / `.global_request_state_method`
79
-
80
- These are set in the consuming application's `config/initializers/bp3-core.rb`.
31
+ ### Model concerns
32
+
33
+ **Tenantable** (`tenantable.rb`)
34
+ - `configure_tenancy` looks at which of `sites_site_id`, `tenant_id` and `workspaces_workspace_id` the
35
+ table has and only sets up those: `belongs_to` (optional when the column is nullable), `site`/`workspace`
36
+ aliases, `before_validation` callbacks that fill the column from the request state's `either_*_id`, and
37
+ validations that tenant and workspace belong to the record's site
38
+ - Site and tenant get default scopes from the request state's `either_site`/`either_tenant`. How far they
39
+ reach is decided by the primary login, `current_login`, the least privileged one signed in: a root reaches
40
+ every site and an admin every tenant of theirs, but not when a less privileged login is signed in beside
41
+ them -- see `Tenantable.reaches_every_site?` and `.reaches_every_tenant?`
42
+ - Including it calls `connection` to load column metadata, unless `IGNORE_NOTIFICATIONS` is set
43
+ - The gem does not know the application's `GlobalRequestState`, only its parent `Bp3::RequestState::Base`
44
+ (gem bp3-request_state): use that API -- `current_login`, `current_root`, `current_admin`, `either_site`
45
+ and the like -- through `Bp3::Core::Tenantable.global_request_state`, never the constant
46
+
47
+ **Rqid** (`rqid.rb`)
48
+ - `before_create` sets `rqid` from the configured request state class and method
49
+ - optional `belongs_to` `original_request`, `original_response` and `original_visit` on `rqid` (more than
50
+ one record may share an rqid)
51
+ - `Bp3::Core::Rqid.global_request_state_class` is where every bp3 gem gets the request state class
52
+
53
+ **Sqnr** (`sqnr.rb`): `sqnr` (ascending) and `rnqs` (descending) scopes; `use_sqnr_for_ordering` sets
54
+ `implicit_order_column`
55
+
56
+ **Ransackable** (`ransackable.rb`): `ransackable_attributes` (all columns), `ransackable_associations` (all
57
+ associations), `ransackable_scopes` (none), `ransackable_fields` (all three), `ransortable_attributes`,
58
+ minus the global `attribute_exceptions` and `association_exceptions`
59
+
60
+ **Displayable** (`displayable.rb`): `to_dom_id`, `display_name`/`admin_display_name` ("ClassName id"),
61
+ `i18n_key`, and `filtered_attributes`, which masks `Rails.application.config.filter_parameters` (also inside
62
+ `object`/`object_changes` for versions) with `version_filter_mask`; classes override the mask to tell
63
+ sources apart
64
+
65
+ ### Controller concerns
66
+
67
+ **Actions** (`actions.rb`): the request pipeline. Prepended before actions, in order: `set_rqid`,
68
+ `check_site`, `check_workspace`, `check_tenant` (each finds or creates the record and stores it in the
69
+ request state); then paper_trail whodunnit, user agent and `Inbound::Request` records (skipped under do not
70
+ track), the maintenance-mode check, and `set_global_request_state` for the logins, locale and view context.
71
+ Also `info_for_paper_trail`, `user_for_paper_trail`, `pundit_user` and `default_url_options` with the
72
+ locale. The order of the prepends matters; do not change it
73
+
74
+ **Cookies** (`cookies.rb`): the `_bp3_visitor` signed cookie that finds or creates the `Users::Visitor`,
75
+ the do-not-track cookie, and migration from the old per-site/tenant cookie name
76
+
77
+ **Settings** (`settings.rb`): `create_request_record_is_on?`, `multi_tenant_is_on?`
78
+
79
+ **FeatureFlags** (`feature_flags.rb`): `efon?` (a `Feature::Enabler`), `cfon?` (a record's `configs`),
80
+ `efcfon?` (either, the config winning when both are set)
81
+
82
+ ### Anywhere
83
+
84
+ **SystemLogs** (`system_logs.rb`): `log`, `log_debug`, `log_info`, `log_warn`, `log_error`,
85
+ `log_exception`, as instance methods and on the module itself. Each writes to the Rails log and to the
86
+ configured log and exception classes (`log_to_io:` and `log_to_db:` switch either off); a failing database
87
+ write is logged, not raised
88
+
89
+ ### Configuration
90
+
91
+ Set in the application's `config/initializers/bp3-core.rb`; the class names are constantized and cached on
92
+ first use:
93
+ - `Bp3::Core::Rqid.global_request_state_class_name` (`'GlobalRequestState'`) and
94
+ `.global_request_state_method` (`:request_id`)
95
+ - `Bp3::Core::SystemLogs.system_log_name` (`'System::Log'`) and `.system_exception_name`
96
+ (`'System::Exception'`)
97
+ - `Bp3::Core::Ransackable.attribute_exceptions` and `.association_exceptions`
98
+
99
+ The concerns also name application classes directly: `Sites::Site`, `Tenant`, `Workspaces::Workspace`,
100
+ `Users::Visitor`, `Inbound::Request`, `Inbound::UserAgent`, `Feature::Enabler`, `UserContext`,
101
+ `DomainExtractor`.
81
102
 
82
103
  ### Dependencies
83
104
 
84
- The gem depends on:
85
- - `activesupport ~> 8.1` - For ActiveSupport::Concern and core extensions
86
- - `actionview ~> 8.1` - For view helpers
87
- - Development: `rspec`, `rubocop`, `rubocop-rake`, `rubocop-rspec`
105
+ - `activesupport ~> 8.1`, `actionview ~> 8.1`
106
+ - `bp3-request_state ~> 0.1` - `Bp3::RequestState::Base`, the parent of the application's request state
88
107
 
89
108
  ## Code Style
90
109
 
91
- RuboCop configuration (`.rubocop.yml`):
92
- - Target Ruby version: 3.2.2
93
- - Custom metrics: AbcSize max 26, MethodLength max 15, ModuleLength max 150
94
- - RSpec: ExampleLength max 10, MultipleExpectations max 4
95
- - Style/Documentation disabled
96
- - Uses `rubocop-rake` and `rubocop-rspec` plugins
110
+ - RuboCop (`.rubocop.yml`): target Ruby 3.2.2, NewCops, rubocop-rake and rubocop-rspec; AbcSize 26,
111
+ MethodLength 15, ModuleLength 150, BlockLength 66, Cyclomatic/PerceivedComplexity 10; RSpec ExampleLength
112
+ 10, MultipleExpectations 4; Style/Documentation off
113
+ - Every file starts with `# frozen_string_literal: true`
114
+ - American English; no trailing whitespace on any added or modified line
115
+
116
+ ## Release
97
117
 
98
- All files use `# frozen_string_literal: true`.
118
+ Bump `lib/bp3/core/version.rb` and add a `CHANGELOG.md` entry. `rake release` tags, pushes and publishes to
119
+ rubygems.org; leave releasing and committing to the user.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bp3-core (0.2.0)
4
+ bp3-core (0.3.0)
5
5
  actionview (~> 8.1)
6
6
  activesupport (~> 8.1)
7
7
  bp3-request_state (~> 0.1)
@@ -9,9 +9,9 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- actionpack (8.1.2)
13
- actionview (= 8.1.2)
14
- activesupport (= 8.1.2)
12
+ actionpack (8.1.4)
13
+ actionview (= 8.1.4)
14
+ activesupport (= 8.1.4)
15
15
  nokogiri (>= 1.8.5)
16
16
  rack (>= 2.2.4)
17
17
  rack-session (>= 1.0.1)
@@ -19,13 +19,13 @@ GEM
19
19
  rails-dom-testing (~> 2.2)
20
20
  rails-html-sanitizer (~> 1.6)
21
21
  useragent (~> 0.16)
22
- actionview (8.1.2)
23
- activesupport (= 8.1.2)
22
+ actionview (8.1.4)
23
+ activesupport (= 8.1.4)
24
24
  builder (~> 3.1)
25
25
  erubi (~> 1.11)
26
26
  rails-dom-testing (~> 2.2)
27
27
  rails-html-sanitizer (~> 1.6)
28
- activesupport (8.1.2)
28
+ activesupport (8.1.4)
29
29
  base64
30
30
  bigdecimal
31
31
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -40,53 +40,54 @@ GEM
40
40
  uri (>= 0.13.1)
41
41
  ast (2.4.3)
42
42
  base64 (0.3.0)
43
- bigdecimal (4.0.1)
43
+ bigdecimal (4.1.3)
44
44
  bp3-request_state (0.1.3)
45
45
  actionpack (~> 8.1)
46
46
  activesupport (~> 8.1)
47
47
  request_store (>= 1.5.1)
48
48
  builder (3.3.0)
49
- concurrent-ruby (1.3.6)
49
+ concurrent-ruby (1.3.8)
50
50
  connection_pool (3.0.2)
51
- crass (1.0.6)
51
+ crass (1.0.8)
52
52
  diff-lcs (1.6.2)
53
53
  drb (2.2.3)
54
54
  erubi (1.13.1)
55
- i18n (1.14.8)
55
+ i18n (1.15.2)
56
56
  concurrent-ruby (~> 1.0)
57
- json (2.18.0)
58
- language_server-protocol (3.17.0.5)
57
+ json (3.1.0)
58
+ language_server-protocol (3.17.0.6)
59
59
  lint_roller (1.1.0)
60
60
  logger (1.7.0)
61
- loofah (2.25.0)
61
+ loofah (2.25.2)
62
62
  crass (~> 1.0.2)
63
63
  nokogiri (>= 1.12.0)
64
- minitest (6.0.1)
64
+ minitest (6.0.6)
65
+ drb (~> 2.0)
65
66
  prism (~> 1.5)
66
- nokogiri (1.19.0-x86_64-darwin)
67
+ nokogiri (1.19.4-x86_64-darwin)
67
68
  racc (~> 1.4)
68
- parallel (1.27.0)
69
- parser (3.3.10.0)
69
+ parallel (2.3.0)
70
+ parser (3.3.13.0)
70
71
  ast (~> 2.4.1)
71
72
  racc
72
- prism (1.7.0)
73
+ prism (1.9.0)
73
74
  racc (1.8.1)
74
- rack (3.2.4)
75
- rack-session (2.1.1)
75
+ rack (3.2.7)
76
+ rack-session (2.1.2)
76
77
  base64 (>= 0.1.0)
77
78
  rack (>= 3.0.0)
78
- rack-test (2.2.0)
79
+ rack-test (2.3.0)
79
80
  rack (>= 1.3)
80
81
  rails-dom-testing (2.3.0)
81
82
  activesupport (>= 5.0.0)
82
83
  minitest
83
84
  nokogiri (>= 1.6)
84
- rails-html-sanitizer (1.6.2)
85
- loofah (~> 2.21)
85
+ rails-html-sanitizer (1.7.1)
86
+ loofah (~> 2.25, >= 2.25.2)
86
87
  nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
87
88
  rainbow (3.1.1)
88
- rake (13.3.1)
89
- regexp_parser (2.11.3)
89
+ rake (13.4.2)
90
+ regexp_parser (2.13.1)
90
91
  request_store (1.7.0)
91
92
  rack (>= 1.4)
92
93
  rspec (3.13.2)
@@ -98,38 +99,39 @@ GEM
98
99
  rspec-expectations (3.13.5)
99
100
  diff-lcs (>= 1.2.0, < 2.0)
100
101
  rspec-support (~> 3.13.0)
101
- rspec-mocks (3.13.7)
102
+ rspec-mocks (3.13.8)
102
103
  diff-lcs (>= 1.2.0, < 2.0)
103
104
  rspec-support (~> 3.13.0)
104
- rspec-support (3.13.6)
105
- rubocop (1.82.1)
106
- json (~> 2.3)
105
+ rspec-support (3.13.7)
106
+ rubocop (1.92.1)
107
+ json (>= 2.3)
107
108
  language_server-protocol (~> 3.17.0.2)
108
109
  lint_roller (~> 1.1.0)
109
- parallel (~> 1.10)
110
+ parallel (>= 1.10)
110
111
  parser (>= 3.3.0.2)
111
112
  rainbow (>= 2.2.2, < 4.0)
112
- regexp_parser (>= 2.9.3, < 3.0)
113
- rubocop-ast (>= 1.48.0, < 2.0)
113
+ regexp_parser (>= 2.11.3, < 3.0)
114
+ rubocop-ast (>= 1.49.0, < 2.0)
114
115
  ruby-progressbar (~> 1.7)
115
116
  unicode-display_width (>= 2.4.0, < 4.0)
116
- rubocop-ast (1.49.0)
117
+ rubocop-ast (1.50.0)
117
118
  parser (>= 3.3.7.2)
118
119
  prism (~> 1.7)
119
120
  rubocop-rake (0.7.1)
120
121
  lint_roller (~> 1.1)
121
122
  rubocop (>= 1.72.1)
122
- rubocop-rspec (3.9.0)
123
+ rubocop-rspec (3.10.2)
123
124
  lint_roller (~> 1.1)
124
- rubocop (~> 1.81)
125
+ regexp_parser (>= 2.0)
126
+ rubocop (~> 1.86, >= 1.86.2)
125
127
  ruby-progressbar (1.13.0)
126
128
  securerandom (0.4.1)
127
129
  tzinfo (2.0.6)
128
130
  concurrent-ruby (~> 1.0)
129
- unicode-display_width (3.2.0)
130
- unicode-emoji (~> 4.1)
131
- unicode-emoji (4.2.0)
132
- uri (1.1.1)
131
+ unicode-display_width (3.3.0)
132
+ unicode-emoji (~> 4.3)
133
+ unicode-emoji (4.3.0)
134
+ uri (1.1.2)
133
135
  useragent (0.16.11)
134
136
 
135
137
  PLATFORMS
data/README.md CHANGED
@@ -80,7 +80,11 @@ The specific columns expected by `Tenantable` are:
80
80
  - site: `sites_site_id`
81
81
  - tenant: `tenant_id`
82
82
  - workspace: `workspaces_workspace_id`
83
- Tenantable will use reflection to determine which one(s) exist, and will create associations and callbacks accordingly.
83
+ Tenantable will use reflection to determine which one(s) exist, and will create associations and callbacks accordingly.
84
+
85
+ Its default scopes reach as far as the primary login, the request state's `current_login`: a root
86
+ reaches every site and an admin every tenant of their site, unless a less privileged login is signed
87
+ in beside them. It reads the request state through `Bp3::Core::Rqid.global_request_state_class`.
84
88
 
85
89
  ## Development
86
90
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Bp3
4
4
  module Core
5
- # rubocop:disable Metrics/ModuleLength
5
+ # rubocop:disable-next Metrics/ModuleLength
6
6
  module Actions
7
7
  extend ActiveSupport::Concern
8
8
 
@@ -209,6 +209,5 @@ module Bp3
209
209
  @grsc ||= Bp3::Core::Rqid.global_request_state_class
210
210
  end
211
211
  end
212
- # rubocop:enable Metrics/ModuleLength
213
212
  end
214
213
  end
@@ -58,7 +58,7 @@ module Bp3
58
58
  }
59
59
  end
60
60
 
61
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
61
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
62
62
  def check_old_visitor_cookie
63
63
  cookie_value = cookies.signed[visitor_cookie_name]
64
64
  return if cookie_value.blank? && do_not_track
@@ -85,9 +85,8 @@ module Bp3
85
85
  "check_visitor_cookie: cookie[#{visitor_cookie_name}]=#{cookies.signed[visitor_cookie_name]}"
86
86
  end
87
87
  end
88
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
89
88
 
90
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
89
+ # rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
91
90
  def check_new_visitor_cookie
92
91
  cookie_value = cookies.signed[new_visitor_cookie_name]
93
92
  return if cookie_value.blank? && do_not_track
@@ -110,7 +109,6 @@ module Bp3
110
109
  "check_visitor_cookie: cookie[#{new_visitor_cookie_name}]=#{cookies.signed[new_visitor_cookie_name]}"
111
110
  end
112
111
  end
113
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
114
112
 
115
113
  def visitor_cookie_name
116
114
  @visitor_cookie_name ||= "#{VISITOR_COOKIE_NAME_PREFIX}_#{cookie_site_id}_#{cookie_tenant_id}"
@@ -24,18 +24,19 @@ module Bp3
24
24
  Feature::Enabler.on(flag, enablable: ref, default:)
25
25
  end
26
26
 
27
+ # An explicit false wins over the default; the default applies only when the flag is not set
27
28
  def efon?(flag, ref: nil, default: nil)
28
- efon(flag, ref:, default:) || default || false
29
+ efon(flag, ref:, default:) || false
29
30
  end
30
31
 
31
32
  def cfon(flag, ref: nil, default: nil)
32
- return default if ref.nil?
33
-
34
- ref.configs&.[](flag)
33
+ value = ref&.configs&.[](flag)
34
+ value.nil? ? default : value
35
35
  end
36
36
 
37
+ # An explicit false wins over the default; the default applies only when the config is not set
37
38
  def cfon?(flag, ref: nil, default: nil)
38
- cfon(flag, ref:, default:) || default || false
39
+ cfon(flag, ref:, default:) || false
39
40
  end
40
41
  end
41
42
  end
@@ -21,7 +21,7 @@ module Bp3
21
21
 
22
22
  # Class methods for use in contexts without instance methods
23
23
  module ClassMethods
24
- # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
24
+ # rubocop:disable-next Metrics/ParameterLists, Metrics/MethodLength
25
25
  def log(level:, key:, message: nil, exception: nil, site: nil, details: {}, **system_log_options)
26
26
  system_log_options = SYSTEM_LOG_DEFAULTS.dup.merge(system_log_options)
27
27
  record = nil
@@ -43,7 +43,6 @@ module Bp3
43
43
  nil
44
44
  end
45
45
  end
46
- # rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
47
46
 
48
47
  def log_debug(key:, message:, details: {}, **system_log_options)
49
48
  log(level: 'debug', key:, message:, details:, **system_log_options)
@@ -84,11 +83,10 @@ module Bp3
84
83
  end
85
84
  end
86
85
 
87
- # rubocop:disable Metrics/ParameterLists
86
+ # rubocop:disable-next Metrics/ParameterLists
88
87
  def log(level:, key:, message: nil, exception: nil, site: nil, details: {}, **system_log_options)
89
88
  SystemLogs.log(level:, key:, message:, exception:, site:, details:, **system_log_options)
90
89
  end
91
- # rubocop:enable Metrics/ParameterLists
92
90
 
93
91
  def log_debug(key:, message:, details: {}, **system_log_options)
94
92
  SystemLogs.log_debug(key:, message:, details:, **system_log_options)
@@ -5,6 +5,25 @@ module Bp3
5
5
  module Tenantable
6
6
  extend ActiveSupport::Concern
7
7
 
8
+ # The application's request state, the Bp3::RequestState::Base subclass Bp3::Core::Rqid is
9
+ # configured with
10
+ def self.global_request_state
11
+ Bp3::Core::Rqid.global_request_state_class
12
+ end
13
+
14
+ # How far a default scope reaches is decided by the primary login, the request state's
15
+ # current_login: the least privileged one signed in. A root or an admin signed in beside a less
16
+ # privileged login has that login's reach rather than their own.
17
+ def self.reaches_every_site?(state = global_request_state)
18
+ login = state.current_login
19
+ !login.nil? && login == state.current_root
20
+ end
21
+
22
+ def self.reaches_every_tenant?(state = global_request_state)
23
+ login = state.current_login
24
+ !login.nil? && (login == state.current_root || login == state.current_admin)
25
+ end
26
+
8
27
  included do
9
28
  # call connection to make sure the db meta data has been loaded
10
29
  ignore_notifications_string = (ENV['IGNORE_NOTIFICATIONS'] || '').strip
@@ -16,13 +35,13 @@ module Bp3
16
35
  def set_sites_site_id
17
36
  return if sites_site_id || sites_site
18
37
 
19
- self.sites_site_id = GlobalRequestState.either_site_id
38
+ self.sites_site_id = Bp3::Core::Tenantable.global_request_state.either_site_id
20
39
  end
21
40
 
22
41
  def set_tenant_id
23
42
  return if tenant_id || tenant
24
43
 
25
- self.tenant_id = GlobalRequestState.either_tenant_id
44
+ self.tenant_id = Bp3::Core::Tenantable.global_request_state.either_tenant_id
26
45
  end
27
46
 
28
47
  def tenant_matches_site
@@ -38,7 +57,7 @@ module Bp3
38
57
  def set_workspaces_workspace_id
39
58
  return if workspaces_workspace_id || workspaces_workspace
40
59
 
41
- self.workspaces_workspace_id = GlobalRequestState.either_workspace_id
60
+ self.workspaces_workspace_id = Bp3::Core::Tenantable.global_request_state.either_workspace_id
42
61
  end
43
62
 
44
63
  def workspaces_workspace_matches_site
@@ -75,13 +94,13 @@ module Bp3
75
94
  before_validation :set_sites_site_id
76
95
 
77
96
  default_scope lambda {
78
- site = GlobalRequestState.either_site
79
- site = nil if GlobalRequestState.current_root
97
+ site = Bp3::Core::Tenantable.global_request_state.either_site
98
+ site = nil if Bp3::Core::Tenantable.reaches_every_site?
80
99
  where(sites_site_id: site.id) if site
81
100
  }
82
101
  end
83
102
 
84
- # rubocop:disable Metrics/MethodLength
103
+ # rubocop:disable-next Metrics/MethodLength
85
104
  def may_belong_to_tenant
86
105
  column = columns.detect { |c| c.name == 'tenant_id' }
87
106
  return if column.nil?
@@ -95,10 +114,10 @@ module Bp3
95
114
  validate :tenant_matches_site
96
115
 
97
116
  default_scope lambda {
98
- site = GlobalRequestState.either_site
99
- site = nil if GlobalRequestState.current_root
100
- tenant = GlobalRequestState.either_tenant
101
- tenant = nil if GlobalRequestState.either_admin
117
+ site = Bp3::Core::Tenantable.global_request_state.either_site
118
+ site = nil if Bp3::Core::Tenantable.reaches_every_site?
119
+ tenant = Bp3::Core::Tenantable.global_request_state.either_tenant
120
+ tenant = nil if Bp3::Core::Tenantable.reaches_every_tenant?
102
121
  if site && tenant # for non admins (i.e. users)
103
122
  where(sites_site_id: site.id, tenant_id: tenant.id)
104
123
  elsif site # for site admins
@@ -108,7 +127,6 @@ module Bp3
108
127
  end
109
128
  }
110
129
  end
111
- # rubocop:enable Metrics/MethodLength
112
130
 
113
131
  def may_belong_to_workspace
114
132
  column = columns.detect { |c| c.name == 'workspaces_workspace_id' }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bp3
4
4
  module Core
5
- VERSION = '0.2.0'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bp3-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wim den Braven
@@ -177,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
177
  - !ruby/object:Gem::Version
178
178
  version: '0'
179
179
  requirements: []
180
- rubygems_version: 3.6.9
180
+ rubygems_version: 4.0.4
181
181
  specification_version: 4
182
182
  summary: bp3-core provides core concerns for BP3 (persuavis/black_phoebe_3).
183
183
  test_files: []