Skip to content

Commit 212f6c7

Browse files
authored
Extract methods from experience-cs (#87)
This has come out of RaspberryPiFoundation/experience-cs#546. This adds two new optional model concerns which provide the following methods (see individual commits for details): - [Add RpiAuth::Models::Roles#parsed_roles](60367e1) - [Add RpiAuth::Models::AccountTypes#student_account?](176ecfe) I've also made a small improvement to the model concern specs and fixed some rubocop violations. I've tested the changes in RaspberryPiFoundation/experience-cs#687.
2 parents cfa92aa + 5c13c81 commit 212f6c7

File tree

8 files changed

+146
-36
lines changed

8 files changed

+146
-36
lines changed

.rubocop_todo.yml

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
# This configuration was generated by
22
# `rubocop --auto-gen-config`
3-
# on 2024-07-02 19:48:59 UTC using RuboCop version 1.64.1.
3+
# on 2025-06-04 10:53:17 UTC using RuboCop version 1.75.2.
44
# The point is for the user to remove these configuration records
55
# one by one as the offenses are removed from the code base.
66
# Note that changes in the inspected code, or installation of new
77
# versions of RuboCop, may require this file to be generated again.
88

9-
# Offense count: 11
9+
# Offense count: 12
1010
# Configuration parameters: EnforcedStyle, AllowedGems, Include.
1111
# SupportedStyles: Gemfile, gems.rb, gemspec
1212
# Include: **/*.gemspec, **/Gemfile, **/gems.rb
1313
Gemspec/DevelopmentDependencies:
1414
Exclude:
15-
- "rpi_auth.gemspec"
15+
- 'rpi_auth.gemspec'
1616

17-
# Offense count: 1
17+
# Offense count: 2
1818
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
1919
Metrics/AbcSize:
2020
Max: 23
@@ -28,20 +28,12 @@ RSpec/ExampleLength:
2828
RSpec/MultipleExpectations:
2929
Max: 4
3030

31-
# Offense count: 8
31+
# Offense count: 9
3232
# Configuration parameters: AllowSubject.
3333
RSpec/MultipleMemoizedHelpers:
3434
Max: 6
3535

36-
# Offense count: 10
36+
# Offense count: 13
3737
# Configuration parameters: AllowedGroups.
3838
RSpec/NestedGroups:
3939
Max: 5
40-
41-
# Offense count: 1
42-
# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata.
43-
# Include: **/*_spec.rb
44-
RSpec/SpecFilePathFormat:
45-
Exclude:
46-
- "spec/rpi_auth/models/authenticatable_spec.rb"
47-
- "spec/rpi_auth/models/with_tokens_spec.rb"

lib/rpi_auth.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
require 'rpi_auth/configuration'
66
require 'rpi_auth/models/authenticatable'
77
require 'rpi_auth/models/with_tokens'
8+
require 'rpi_auth/models/roles'
9+
require 'rpi_auth/models/account_types'
810
require 'omniauth/rails_csrf_protection'
911

1012
module RpiAuth

lib/rpi_auth/models/account_types.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# frozen_string_literal: true
2+
3+
module RpiAuth
4+
module Models
5+
module AccountTypes
6+
STUDENT_PREFIX = 'student:'
7+
8+
extend ActiveSupport::Concern
9+
10+
include Authenticatable
11+
12+
def student_account?
13+
user_id =~ /^#{STUDENT_PREFIX}/o
14+
end
15+
end
16+
end
17+
end

lib/rpi_auth/models/roles.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
module RpiAuth
4+
module Models
5+
module Roles
6+
extend ActiveSupport::Concern
7+
8+
include Authenticatable
9+
10+
def parsed_roles
11+
roles&.split(',')&.map(&:strip) || []
12+
end
13+
end
14+
end
15+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
RSpec.describe RpiAuth::Models::AccountTypes, type: :model do
6+
subject(:user) { user_class.new(user_id:) }
7+
8+
let(:user_class) do
9+
Class.new(User) do
10+
include RpiAuth::Models::Authenticatable
11+
include RpiAuth::Models::AccountTypes
12+
end
13+
end
14+
15+
describe '#student_account?' do
16+
context "when user_id has the 'student:' prefix" do
17+
let(:user_id) { RpiAuth::Models::AccountTypes::STUDENT_PREFIX + SecureRandom.uuid }
18+
19+
it 'returns truthy' do
20+
expect(user).to be_student_account
21+
end
22+
end
23+
24+
context "when user_id does not have the 'student:' prefix" do
25+
let(:user_id) { SecureRandom.uuid }
26+
27+
it 'returns falsey' do
28+
expect(user).not_to be_student_account
29+
end
30+
end
31+
end
32+
end

spec/rpi_auth/models/authenticatable_spec.rb

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
require 'spec_helper'
44

5-
class DummyUser
6-
include RpiAuth::Models::Authenticatable
7-
end
5+
RSpec.describe RpiAuth::Models::Authenticatable, type: :model do
6+
subject { user_class.new }
87

9-
RSpec.describe DummyUser, type: :model do
10-
subject { described_class.new }
8+
let(:user_class) do
9+
Class.new(User) do
10+
include RpiAuth::Models::Authenticatable
11+
end
12+
end
1113

1214
it { is_expected.to respond_to(:user_id) }
1315
it { is_expected.to respond_to(:country) }
@@ -22,11 +24,11 @@ class DummyUser
2224
it { is_expected.not_to respond_to(:from_omniauth) }
2325

2426
describe 'PROFILE_KEYS' do
25-
it { expect(described_class::PROFILE_KEYS).to be_an(Array) }
27+
it { expect(user_class::PROFILE_KEYS).to be_an(Array) }
2628
end
2729

2830
describe '#from_omniauth' do
29-
subject(:omniauth_user) { described_class.from_omniauth(auth) }
31+
subject(:omniauth_user) { user_class.from_omniauth(auth) }
3032

3133
let(:info) do
3234
{
@@ -53,7 +55,7 @@ class DummyUser
5355
end
5456

5557
it 'returns a user with the correct attributes' do
56-
expect(omniauth_user).to be_a described_class
58+
expect(omniauth_user).to be_a user_class
5759
expect(omniauth_user).to have_attributes(user_id: 'testuserid', name: 'Bodkin Van Horn',
5860
nickname: 'Hoos-Foos', email: '[email protected]',
5961
country: 'Zimbabwe', country_code: 'ZW',
@@ -64,13 +66,13 @@ class DummyUser
6466
context 'with unusual keys in info' do
6567
let(:info) { { foo: :bar, flibble: :woo } }
6668

67-
it { is_expected.to be_a described_class }
69+
it { is_expected.to be_a user_class }
6870
end
6971

7072
context 'with no info' do
7173
let(:info) { nil }
7274

73-
it { is_expected.to be_a described_class }
75+
it { is_expected.to be_a user_class }
7476
end
7577

7678
context 'with no auth set' do

spec/rpi_auth/models/roles_spec.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
RSpec.describe RpiAuth::Models::Roles, type: :model do
6+
subject(:user) { user_class.new(roles:) }
7+
8+
let(:user_class) do
9+
Class.new(User) do
10+
include RpiAuth::Models::Authenticatable
11+
include RpiAuth::Models::Roles
12+
end
13+
end
14+
15+
describe '#parsed_roles' do
16+
context 'when roles is set to comma-separated string' do
17+
let(:roles) { 'role-1,role-2' }
18+
19+
it 'returns array of role names' do
20+
expect(user.parsed_roles).to eq(%w[role-1 role-2])
21+
end
22+
end
23+
24+
context 'when roles names have leading & trailing spaces' do
25+
let(:roles) { ' role-1 , role-2 ' }
26+
27+
it 'strips the spaces' do
28+
expect(user.parsed_roles).to eq(%w[role-1 role-2])
29+
end
30+
end
31+
32+
context 'when roles is set to empty string' do
33+
let(:roles) { '' }
34+
35+
it 'returns empty array' do
36+
expect(user.parsed_roles).to eq([])
37+
end
38+
end
39+
40+
context 'when roles is set to nil' do
41+
let(:roles) { nil }
42+
43+
it 'returns empty array when roles is set to nil' do
44+
expect(user.parsed_roles).to eq([])
45+
end
46+
end
47+
end
48+
end

spec/rpi_auth/models/with_tokens_spec.rb

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22

33
require 'spec_helper'
44

5-
class DummyUser
6-
include RpiAuth::Models::Authenticatable
7-
include RpiAuth::Models::WithTokens
8-
end
9-
10-
RSpec.describe DummyUser, type: :model do
5+
RSpec.describe RpiAuth::Models::WithTokens, type: :model do
116
include ActiveSupport::Testing::TimeHelpers
127

13-
subject(:user) { described_class.new }
8+
subject(:user) { user_class.new }
9+
10+
let(:user_class) do
11+
Class.new(User) do
12+
include RpiAuth::Models::Authenticatable
13+
include RpiAuth::Models::WithTokens
14+
end
15+
end
1416

1517
it { is_expected.to respond_to(:access_token) }
1618
it { is_expected.to respond_to(:refresh_token) }
@@ -33,9 +35,9 @@ class DummyUser
3335
end
3436

3537
describe '#from_omniauth' do
36-
subject(:user) { described_class.from_omniauth(auth) }
38+
subject(:user) { user_class.from_omniauth(auth) }
3739

38-
let(:omniauth_user) { described_class.new }
40+
let(:omniauth_user) { user_class.new }
3941
let(:info) { omniauth_user.serializable_hash }
4042
let(:credentials) { { token: SecureRandom.base64(12), refresh_token: SecureRandom.base64(12), expires_in: rand(60..240) } }
4143

@@ -52,7 +54,7 @@ class DummyUser
5254
)
5355
end
5456

55-
it { is_expected.to be_a described_class }
57+
it { is_expected.to be_a user_class }
5658

5759
it 'sets the access_token' do
5860
expect(user.access_token).to eq credentials[:token]
@@ -79,13 +81,13 @@ class DummyUser
7981
context 'with unusual keys in info' do
8082
let(:info) { { foo: :bar, flibble: :woo } }
8183

82-
it { is_expected.to be_a described_class }
84+
it { is_expected.to be_a user_class }
8385
end
8486

8587
context 'with no info' do
8688
let(:info) { nil }
8789

88-
it { is_expected.to be_a described_class }
90+
it { is_expected.to be_a user_class }
8991
end
9092

9193
context 'with no auth set' do

0 commit comments

Comments
 (0)