ask-auth 0.1.4 → 0.2.1

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: 52438e8c475956073bd3f61dcba1e5f6f6ff0619cf83532ba6429603786373e6
4
- data.tar.gz: 199345260588e9813e0feddd9881fbcf7290e67dae2ed5fd259c380085927dad
3
+ metadata.gz: bb2c28ee519b76814303a1c502ad2cd707bda862ea84ba1d9172656a07fb9c92
4
+ data.tar.gz: a1eb477ff8f9fb5a7e4b6e35e268e372644e1e0c596d32f622392d003e36ff63
5
5
  SHA512:
6
- metadata.gz: 39dd2a342b2b02afb910bc1132b8e90e211a2607d0413db69808646ac04fa91653c16d9014284f5e6a841370a497621d4eb6b83c83151c46a7d6a73739f9f815
7
- data.tar.gz: 0dc793d15763b903da6b55b85e0791ef90c4c86f6d81059abab08211767d7d980b492c774d305dea683d884d68869dd98f9d4269abd72b43980909a3b4407c3a
6
+ metadata.gz: 523eb5835f6d43f808a0eed7c80ca1fb51c6e97c7f47ecfce9920b15edc5a9521edb6a42be99f17b9ebd5540bec4df0c6a562f9aabc3e45c378275dadf91ab68
7
+ data.tar.gz: 81e3eb0a8f88c908edf12942453a5156402f072b28a8de0cd776787ad7a94667dc9449411bfaa9a3c2622c56c7144dbcaf9a8b0c784c6142ff6f38b56703b639
@@ -5,28 +5,17 @@ module Ask
5
5
  module Providers
6
6
  # Resolves credentials from Rails encrypted credentials.
7
7
  #
8
- # Tries multiple strategies in order, from least to most nested:
8
+ # Supports two lookup modes determined by the +name+ argument type:
9
9
  #
10
- # 1. Full name as a single top-level key (flat layout)
11
- # +resolve(:openai_api_key)+ +Rails.application.credentials.openai_api_key+
10
+ # Symbol/String → flat key, looked up literally
11
+ # resolve(:opencode_api_key) → credentials.opencode_api_key
12
+ # resolve("opencode_api_key") → credentials.opencode_api_key
12
13
  #
13
- # 2. Progressively split into prefix + joined suffix
14
- # +resolve(:opencode_api_key)+ tries:
15
- # a. +Rails.application.credentials.opencode.api_key+ ← most common
16
- # b. +Rails.application.credentials.opencode.api.key+ ← fully split
14
+ # Array → path segments, navigated in order
15
+ # resolve([:opencode, :api_key]) credentials.opencode.api_key
16
+ # resolve([:opencode, :go, :api_key]) → credentials.opencode.go.api_key
17
17
  #
18
- # 3. For compound provider slugs, +resolve(:opencode_go_api_key)+ tries:
19
- # a. +Rails.application.credentials.opencode.go_api_key+ ← slug as single key
20
- # b. +Rails.application.credentials.opencode.go.api_key+ ← slug + api_key
21
- # c. +Rails.application.credentials.opencode.go.api.key+ ← fully split
22
- #
23
- # 4. For deeply nested, +resolve(:nvidia_nim_api_key)+ tries:
24
- # a. +Rails.application.credentials.nvidia.nim_api_key+ ← nvidia + nim_api_key
25
- # b. +Rails.application.credentials.nvidia.nim.api_key+ ← nvidia.nim + api_key
26
- # c. +Rails.application.credentials.nvidia.nim.api.key+ ← fully split
27
- #
28
- # Strategy 2b/3b/4b keep +api_key+ intact as a single credential key
29
- # instead of breaking it into +api+ and +key+.
18
+ # No splitting, no guessing. The caller determines the lookup path.
30
19
  #
31
20
  # Safely returns nil when Rails is not loaded.
32
21
  class RailsCredentials
@@ -34,41 +23,23 @@ module Ask
34
23
  return nil unless defined?(::Rails) && ::Rails.application.respond_to?(:credentials)
35
24
 
36
25
  creds = ::Rails.application.credentials
37
- s = name.to_s
38
- parts = s.split("_")
39
- return nil if parts.empty?
40
-
41
- # 1. Full name as a single top-level key
42
- # Must check the value, not just respond_to?, because
43
- # ActiveSupport::OrderedOptions#respond_to? returns true
44
- # for any method name, even when the key doesn't exist.
45
- val = creds.public_send(s) rescue nil
46
- return val if val
47
-
48
- # 2. Try progressively longer prefixes, keeping the remainder
49
- # joined as a single key name at each level.
50
- # split_at=1: parts[0] as prefix, parts[1..] joined as key
51
- # split_at=2: parts[0..1] as prefix, parts[2..] joined as key
52
- # etc.
53
- (1...parts.length).each do |split_at|
54
- prefix_parts = parts.take(split_at)
55
- key = parts.drop(split_at).join("_")
56
-
57
- obj = prefix_parts.reduce(creds) do |o, p|
58
- break nil unless o.respond_to?(p)
59
- o.public_send(p)
60
- end
61
- next unless obj
26
+ parts = Array(name).map { |p| p.to_s.strip }
27
+ return nil if parts.empty? || parts.any?(&:empty?)
62
28
 
29
+ # Navigate the path segments. Must check the VALUE, not just
30
+ # respond_to?, because ActiveSupport::OrderedOptions#respond_to?
31
+ # returns true for any method name, even when the key doesn't exist.
32
+ value = parts.reduce(creds) do |obj, part|
63
33
  begin
64
- val = obj.public_send(key)
65
- return val if val
34
+ val = obj.public_send(part)
35
+ break nil if val.nil?
36
+ val
66
37
  rescue NoMethodError
67
- # respond_to? can lie for some credential objects
38
+ break nil
68
39
  end
69
40
  end
70
41
 
71
- nil
42
+ value
72
43
  end
73
44
  end
74
45
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ask
4
4
  module Auth
5
- VERSION = "0.1.4"
5
+ VERSION = "0.2.1"
6
6
  end
7
7
  end
data/lib/ask/auth.rb CHANGED
@@ -18,10 +18,12 @@ module Ask
18
18
  #
19
19
  module Auth
20
20
  class MissingCredential < KeyError
21
- def initialize(name)
22
- super("No credential found for #{name.inspect}. " \
23
- "Set #{name.to_s.upcase} in your environment, add it to ~/.ask/credentials.yml, " \
24
- "or configure a provider.")
21
+ def initialize(names)
22
+ names = Array(names).flatten
23
+ inspected = names.map(&:inspect).join(", ")
24
+ super("No credential found for #{inspected}. " \
25
+ "Set one of #{names.map { |n| n.to_s.upcase }.uniq.join(", ")} in your environment, " \
26
+ "add it to ~/.ask/credentials.yml, or configure a provider.")
25
27
  end
26
28
  end
27
29
 
@@ -49,21 +51,38 @@ module Ask
49
51
  end
50
52
 
51
53
  # Walk providers in order and return the first non-nil credential.
52
- # +name+:: Symbol or String identifying the credential (e.g. +:github_token+)
54
+ # Tries each name in order and returns the first match.
55
+ #
56
+ # Each +name+ can be:
57
+ # Symbol/String → flat key, tried literally by all providers
58
+ # Array → path segments, used for nested lookups (e.g., Rails credentials)
59
+ #
60
+ # Ask::Auth.resolve(:openai_api_key) # flat key
61
+ # Ask::Auth.resolve(:opencode_api_key, :opencode_go_api_key) # fallbacks
62
+ # Ask::Auth.resolve([:opencode, :api_key]) # nested path: credentials.opencode.api_key
63
+ # Ask::Auth.resolve(:opencode_go_api_key, [:opencode, :api_key], user: current_user)
64
+ #
65
+ # +names+:: One or more Symbols, Strings, or Arrays identifying the credential
53
66
  # +user+:: Optional user record for per-user providers (Database, OAuth)
54
- def resolve(name, user: nil)
55
- name = name.to_s.strip
56
- return nil if name.empty?
67
+ def resolve(*names, user: nil)
68
+ return nil if names.empty?
57
69
 
58
- configuration.providers.each do |provider|
59
- value = provider.call(name, user: user)
60
- next if value.nil?
70
+ names.each do |name|
71
+ # Validate the name
72
+ parts = Array(name).map { |p| p.to_s.strip }
73
+ next if parts.empty? || parts.any?(&:empty?)
61
74
 
62
- normalized = normalize(value)
63
- return normalized unless normalized.nil?
75
+ configuration.providers.each do |provider|
76
+ # Pass the original form (Symbol/Array) so providers can interpret it
77
+ value = provider.call(name, user: user)
78
+ next if value.nil?
79
+
80
+ normalized = normalize(value)
81
+ return normalized unless normalized.nil?
82
+ end
64
83
  end
65
84
 
66
- raise MissingCredential, name
85
+ raise MissingCredential, names
67
86
  end
68
87
 
69
88
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto