rodauth-model 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ea8768862eb1ba1bf2bdf1bb93872c0167230bf8c139ca49db5b17927288797
4
- data.tar.gz: fe85b970b503ecd33a9c074d1b8763616e65fac5e88fd0fbbd49d2b986ae29ea
3
+ metadata.gz: 59bbf27ffd624fa72ce609fcb9968118bf358e3e3f10893db7d0d73abf356d12
4
+ data.tar.gz: 2c286827b98cef9f1d593f740375ae2f74c759dee7dd2d7c954380a03659ecd4
5
5
  SHA512:
6
- metadata.gz: 4b3f777be5c15eadacb69e074a4bf7b9aacad161e3b6c98f4c2f7ac0a3712af07259e82e62b339e18539522d98694d5d38d95318a11b10cac43c3cf042e87f0e
7
- data.tar.gz: 7788e39bd9de5138773a90c5cac2babdef39da6b9a2a557a37af902c4b1bc851ec26e7e09dd6c6a62d799c3b39198750c39a24560025c2acb7e9fb0223bb4379
6
+ metadata.gz: a8ef44bd93fc5cdb4fabaf58544bcf3ddc7ab8084a6a38b73777cb26163fe0ad8ded45978975cf4423b8f44b97e1c49dc9d3d01f6ca7aac71f06d120a7bebf5b
7
+ data.tar.gz: 9d8067365baec855fed3ad8f2db189d8842e3244b2867ca6b67413715ddd15465aa7f5db1d416f80e4925df2b226f7edda80e68cd40a83a0697e7a1cfe669644
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
- ## [Unreleased]
1
+ ## 0.2.1 (2022-10-26)
2
2
 
3
- ## [0.1.0] - 2022-01-04
3
+ * Fix `elsif` warning in sequel code (@janko)
4
4
 
5
- - Initial release
5
+ ## 0.2.0 (2022-05-11)
6
+
7
+ * Add support for Sequel models (@janko)
8
+
9
+ * Delete associated authentication audit logs after destroying account (@janko)
10
+
11
+ ## 0.1.0 (2022-05-07)
12
+
13
+ * Initial release (@janko)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # rodauth-model
2
2
 
3
- Extension for [Rodauth] providing a mixin for the account model that defines password attribute and associations based on enabled authentication features. At the moment only Active Record is supported.
3
+ Extension for [Rodauth] providing a mixin for the account model that defines password attribute and associations based on enabled authentication features. Supports both Active Record and Sequel models.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,7 +10,7 @@ $ bundle add rodauth-model
10
10
 
11
11
  ## Usage
12
12
 
13
- Assuming with have a `RodauthApp` Roda subclass with Rodauth configured, we can build the mixin from a Rodauth class, and include it into the account model:
13
+ The model mixin is built by calling `Rodauth::Model(...)` with the Rodauth auth class, and included into the account model:
14
14
 
15
15
  ```rb
16
16
  require "rodauth/model" # require before enabling any authentication features
@@ -22,7 +22,7 @@ class RodauthApp < Roda
22
22
  end
23
23
  ```
24
24
  ```rb
25
- class Account < ActiveRecord::Base
25
+ class Account < ActiveRecord::Base # Sequel::Model
26
26
  include Rodauth::Model(RodauthApp.rodauth)
27
27
  end
28
28
  ```
@@ -30,7 +30,7 @@ end
30
30
  If you have multiple Rodauth configurations, pass the one for which you want associations to be defined.
31
31
 
32
32
  ```rb
33
- class Account < ActiveRecord::Base
33
+ class Account < ActiveRecord::Base # Sequel::Model
34
34
  include Rodauth::Model(RodauthApp.rodauth(:admin))
35
35
  end
36
36
  ```
@@ -40,7 +40,7 @@ end
40
40
  Regardless of whether you're storing the password hash in a column in the accounts table, or in a separate table, the `#password` attribute can be used to set or clear the password hash.
41
41
 
42
42
  ```rb
43
- account = Account.create!(email: "user@example.com", password: "secret")
43
+ account = Account.create(email: "user@example.com", password: "secret")
44
44
 
45
45
  # when password hash is stored in a column on the accounts table
46
46
  account.password_hash #=> "$2a$12$k/Ub1I2iomi84RacqY89Hu4.M0vK7klRnRtzorDyvOkVI.hKhkNw."
@@ -71,23 +71,23 @@ You can also reference the associated models directly:
71
71
  Account::AuthenticationAuditLog.where(message: "login").group(:account_id)
72
72
  ```
73
73
 
74
- The associated models define the inverse `belongs_to :account` association:
74
+ The associated models define the inverse `account` association:
75
75
 
76
76
  ```rb
77
- Account::ActiveSessionKey.includes(:account).map(&:account)
77
+ Account::ActiveSessionKey.eager(:account).map(&:account)
78
78
  ```
79
79
 
80
80
  ### Association options
81
81
 
82
- By default, all associations except for audit logs have `dependent: :delete` set, to allow for easy deletion of account records in the console. You can use `:association_options` to modify global or per-association options:
82
+ By default, all associations are configured to be deleted when the associated account record is deleted. When using Active Record, you can use `:association_options` to modify global or per-association options:
83
83
 
84
84
  ```rb
85
- # don't auto-delete associations when account model is deleted
85
+ # don't auto-delete associations when account model is deleted (Active Record)
86
86
  Rodauth::Model(RodauthApp.rodauth, association_options: { dependent: nil })
87
87
 
88
- # require authentication audit logs to be eager loaded before retrieval
88
+ # require authentication audit logs to be eager loaded before retrieval (Sequel)
89
89
  Rodauth::Model(RodauthApp.rodauth, association_options: -> (name) {
90
- { strict_loading: true } if name == :authentication_audit_logs
90
+ { forbid_lazy_load: true } if name == :authentication_audit_logs
91
91
  })
92
92
  ```
93
93
 
@@ -117,7 +117,7 @@ Below is a list of all associations defined depending on the features loaded:
117
117
  | webauthn | `:webauthn_keys` | `has_many` | `WebauthnKey` | `account_webauthn_keys` |
118
118
  | webauthn | `:webauthn_user_id` | `has_one` | `WebauthnUserId` | `account_webauthn_user_ids` |
119
119
 
120
- Note that some Rodauth tables use composite primary keys, which Active Record doesn't support out of the box. For associations to work properly, you might need to add the [composite_primary_keys] gem to your Gemfile.
120
+ Note that some Rodauth tables use composite primary keys, which Active Record doesn't support out of the box. For associations to work properly in Active Record, you might need to add the [composite_primary_keys] gem to your Gemfile. On Sequel, associations will work without any changes, because Sequel supports composite primary keys.
121
121
 
122
122
  ## Extending associations
123
123
 
@@ -178,10 +178,6 @@ end
178
178
 
179
179
  ## Future plans
180
180
 
181
- ### Sequel support
182
-
183
- Currently on Active Record models are supported, but I would like support Sequel models in the near future as well.
184
-
185
181
  ### Joined associations
186
182
 
187
183
  It's possible to have multiple Rodauth configurations that operate on the same tables, but it's currently possible to define associations just for a single configuration. I would like to support grabbing associations from multiple associations.
@@ -68,14 +68,10 @@ module Rodauth
68
68
 
69
69
  model.const_set(name.to_s.singularize.camelize, associated_model)
70
70
 
71
- unless name == :authentication_audit_logs
72
- dependent = type == :has_many ? :delete_all : :delete
73
- end
74
-
75
71
  model.public_send type, name, scope,
76
72
  class_name: associated_model.name,
77
73
  foreign_key: foreign_key,
78
- dependent: dependent,
74
+ dependent: type == :has_many ? :delete_all : :delete,
79
75
  inverse_of: :account,
80
76
  **options,
81
77
  **association_options(name)
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sequel"
4
+
5
+ module Rodauth
6
+ class Model
7
+ module Sequel
8
+ include ::Sequel::Inflections
9
+
10
+ ASSOCIATION_TYPES = { one: :one_to_one, many: :one_to_many }
11
+
12
+ private
13
+
14
+ def define_methods(model)
15
+ rodauth = @auth_class.allocate.freeze
16
+
17
+ unless rodauth.account_password_hash_column
18
+ model.plugin :nested_attributes
19
+ model.nested_attributes :password_hash, destroy: true
20
+ end
21
+
22
+ attr_reader :password
23
+
24
+ define_method(:password=) do |password|
25
+ @password = password
26
+ password_hash = rodauth.send(:password_hash, password) if password
27
+ set_password_hash(password_hash)
28
+ end
29
+
30
+ define_method(:set_password_hash) do |password_hash|
31
+ if rodauth.account_password_hash_column
32
+ public_send(:"#{rodauth.account_password_hash_column}=", password_hash)
33
+ else
34
+ return if password_hash.nil? && self.password_hash.nil?
35
+
36
+ attributes = { rodauth.password_hash_id_column => self.password_hash&.pk }.compact
37
+ if password_hash
38
+ attributes[rodauth.password_hash_column] = password_hash
39
+ else
40
+ attributes[:_delete] = true
41
+ end
42
+ self.password_hash_attributes = attributes
43
+ end
44
+ end
45
+ end
46
+
47
+ def define_associations(model)
48
+ model.plugin :association_dependencies
49
+
50
+ define_password_hash_association(model) unless rodauth.account_password_hash_column
51
+
52
+ feature_associations.each do |association|
53
+ association[:type] = ASSOCIATION_TYPES.fetch(association[:type])
54
+
55
+ define_association(model, **association)
56
+ end
57
+ end
58
+
59
+ def define_password_hash_association(model)
60
+ select = [rodauth.password_hash_id_column] if rodauth.send(:use_database_authentication_functions?)
61
+
62
+ define_association model,
63
+ type: :one_to_one,
64
+ name: :password_hash,
65
+ table: rodauth.password_hash_table,
66
+ key: rodauth.password_hash_id_column,
67
+ select: select
68
+ end
69
+
70
+ def define_association(model, type:, name:, table:, key:, **options)
71
+ associated_model = Class.new(::Sequel::Model)
72
+ associated_model.set_dataset(model.db[table])
73
+ associated_model.many_to_one :account, class: model.name, key: key
74
+
75
+ model.const_set(camelize(singularize(name.to_s)), associated_model)
76
+
77
+ model.public_send type, name,
78
+ class: associated_model.name,
79
+ key: key,
80
+ **options,
81
+ **association_options(name)
82
+
83
+ model.add_association_dependencies name => :delete
84
+ end
85
+
86
+ def association_options(name)
87
+ options = @association_options
88
+ options = options.call(name) if options.respond_to?(:call)
89
+ options || {}
90
+ end
91
+ end
92
+ end
93
+ end
data/lib/rodauth/model.rb CHANGED
@@ -9,6 +9,7 @@ module Rodauth
9
9
  Error = Class.new(StandardError)
10
10
 
11
11
  autoload :ActiveRecord, "rodauth/model/active_record"
12
+ autoload :Sequel, "rodauth/model/sequel"
12
13
 
13
14
  def self.associations
14
15
  @associations ||= {}
@@ -28,9 +29,9 @@ module Rodauth
28
29
  if defined?(::ActiveRecord::Base) && model < ::ActiveRecord::Base
29
30
  extend Rodauth::Model::ActiveRecord
30
31
  elsif defined?(::Sequel::Model) && model < ::Sequel::Model
31
- raise Error, "Sequel models are not yet supported"
32
+ extend Rodauth::Model::Sequel
32
33
  else
33
- raise Error, "must be an Active Record model"
34
+ raise Error, "must be an Active Record or Sequel model"
34
35
  end
35
36
 
36
37
  define_associations(model)
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "rodauth-model"
5
- spec.version = "0.1.0"
5
+ spec.version = "0.2.1"
6
6
  spec.authors = ["Janko Marohnić"]
7
7
  spec.email = ["janko@hey.com"]
8
8
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-07 00:00:00.000000000 Z
11
+ date: 2022-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rodauth
@@ -137,6 +137,7 @@ files:
137
137
  - lib/rodauth/model.rb
138
138
  - lib/rodauth/model/active_record.rb
139
139
  - lib/rodauth/model/associations.rb
140
+ - lib/rodauth/model/sequel.rb
140
141
  - rodauth-model.gemspec
141
142
  homepage: https://github.com/janko/rodauth-model
142
143
  licenses: