neverbounce-cli 1.0.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 +7 -0
- data/.codeclimate.yml +8 -0
- data/.gitignore +21 -0
- data/.rspec +4 -0
- data/.rubocop.yml +1161 -0
- data/.travis.yml +27 -0
- data/.yardopts +2 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +60 -0
- data/LICENSE +9 -0
- data/README.md +138 -0
- data/exe/nb-account-info +5 -0
- data/exe/nb-jobs-create +5 -0
- data/exe/nb-jobs-delete +5 -0
- data/exe/nb-jobs-download +5 -0
- data/exe/nb-jobs-parse +5 -0
- data/exe/nb-jobs-results +5 -0
- data/exe/nb-jobs-search +5 -0
- data/exe/nb-jobs-start +5 -0
- data/exe/nb-jobs-status +5 -0
- data/exe/nb-single-check +5 -0
- data/lib/never_bounce/cli/feature/basic_initialize.rb +20 -0
- data/lib/never_bounce/cli/feature/eigencache.rb +47 -0
- data/lib/never_bounce/cli/feature/envars/examples_mapper.rb +38 -0
- data/lib/never_bounce/cli/feature/envars/item.rb +41 -0
- data/lib/never_bounce/cli/feature/envars.rb +82 -0
- data/lib/never_bounce/cli/feature/igetset.rb +41 -0
- data/lib/never_bounce/cli/feature/require_attr.rb +25 -0
- data/lib/never_bounce/cli/script/account_info.rb +85 -0
- data/lib/never_bounce/cli/script/base.rb +101 -0
- data/lib/never_bounce/cli/script/error.rb +16 -0
- data/lib/never_bounce/cli/script/feature/requires_job_id.rb +28 -0
- data/lib/never_bounce/cli/script/feature/uses_pagination.rb +45 -0
- data/lib/never_bounce/cli/script/jobs_create/supplied_input_parser.rb +48 -0
- data/lib/never_bounce/cli/script/jobs_create.rb +157 -0
- data/lib/never_bounce/cli/script/jobs_delete.rb +55 -0
- data/lib/never_bounce/cli/script/jobs_download.rb +44 -0
- data/lib/never_bounce/cli/script/jobs_parse.rb +73 -0
- data/lib/never_bounce/cli/script/jobs_results.rb +105 -0
- data/lib/never_bounce/cli/script/jobs_search.rb +135 -0
- data/lib/never_bounce/cli/script/jobs_start.rb +73 -0
- data/lib/never_bounce/cli/script/jobs_status.rb +96 -0
- data/lib/never_bounce/cli/script/manifest.rb +22 -0
- data/lib/never_bounce/cli/script/meaningful.rb +233 -0
- data/lib/never_bounce/cli/script/request_maker.rb +169 -0
- data/lib/never_bounce/cli/script/single_check.rb +146 -0
- data/lib/never_bounce/cli/script/table.rb +43 -0
- data/lib/never_bounce/cli/user_config/file_content.rb +66 -0
- data/lib/never_bounce/cli/user_config.rb +51 -0
- data/lib/never_bounce/cli/version.rb +4 -0
- data/lib/neverbounce-cli.rb +3 -0
- data/neverbounce-cli.gemspec +24 -0
- data/spec/lib/never_bounce/cli/feature/basic_initialize_spec.rb +26 -0
- data/spec/lib/never_bounce/cli/feature/eigencache_spec.rb +28 -0
- data/spec/lib/never_bounce/cli/feature/envars/examples_mapper_spec.rb +26 -0
- data/spec/lib/never_bounce/cli/feature/envars/item_spec.rb +9 -0
- data/spec/lib/never_bounce/cli/feature/envars_spec.rb +55 -0
- data/spec/lib/never_bounce/cli/feature/igetset_spec.rb +45 -0
- data/spec/lib/never_bounce/cli/feature/require_attr_spec.rb +25 -0
- data/spec/lib/never_bounce/cli/script/account_info_spec.rb +65 -0
- data/spec/lib/never_bounce/cli/script/base_spec.rb +56 -0
- data/spec/lib/never_bounce/cli/script/feature/requires_job_id_spec.rb +17 -0
- data/spec/lib/never_bounce/cli/script/feature/uses_pagination_spec.rb +21 -0
- data/spec/lib/never_bounce/cli/script/jobs_create/supplied_input_parser_spec.rb +35 -0
- data/spec/lib/never_bounce/cli/script/jobs_create_spec.rb +118 -0
- data/spec/lib/never_bounce/cli/script/jobs_delete_spec.rb +33 -0
- data/spec/lib/never_bounce/cli/script/jobs_download_spec.rb +37 -0
- data/spec/lib/never_bounce/cli/script/jobs_parse_spec.rb +45 -0
- data/spec/lib/never_bounce/cli/script/jobs_results_spec.rb +40 -0
- data/spec/lib/never_bounce/cli/script/jobs_search_spec.rb +59 -0
- data/spec/lib/never_bounce/cli/script/jobs_start_spec.rb +45 -0
- data/spec/lib/never_bounce/cli/script/jobs_status_spec.rb +37 -0
- data/spec/lib/never_bounce/cli/script/manifest_spec.rb +6 -0
- data/spec/lib/never_bounce/cli/script/meaningful_spec.rb +93 -0
- data/spec/lib/never_bounce/cli/script/request_maker_spec.rb +67 -0
- data/spec/lib/never_bounce/cli/script/single_check_spec.rb +94 -0
- data/spec/lib/never_bounce/cli/script/spec_helper.rb +59 -0
- data/spec/lib/never_bounce/cli/script/table_spec.rb +6 -0
- data/spec/lib/never_bounce/cli/user_config/file_content_spec.rb +43 -0
- data/spec/lib/never_bounce/cli/user_config_spec.rb +12 -0
- data/spec/lib/never_bounce/cli_spec.rb +6 -0
- data/spec/spec_helper.rb +51 -0
- metadata +193 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
|
|
2
|
+
require "never_bounce/cli/script/table"
|
|
3
|
+
require "never_bounce/cli/user_config"
|
|
4
|
+
|
|
5
|
+
require_relative "meaningful"
|
|
6
|
+
|
|
7
|
+
module NeverBounce; module CLI; module Script
|
|
8
|
+
# An API request maker script base class.
|
|
9
|
+
class RequestMaker < Meaningful
|
|
10
|
+
attr_writer :api_key, :request, :request_curl, :response, :server_raw, :session, :user_config
|
|
11
|
+
|
|
12
|
+
envar "API_KEY*", "API key", ["2ed45186c72f9319dc64338cdf16ab76b44cf3d1"]
|
|
13
|
+
envar "API_URL", "Custom API URL", ["https://staging-api.isp.com/v5"]
|
|
14
|
+
envar "CURL", "Print cURL request and exit", ["y", default: "N"]
|
|
15
|
+
envar "RAW", "Print raw response body", ["y", default: "N"]
|
|
16
|
+
|
|
17
|
+
# Shared envar defaults.
|
|
18
|
+
SHARED_ENVARS = {
|
|
19
|
+
"AUTO_START" => ["Start processing the job immediately after parsing", ["y", "n"]],
|
|
20
|
+
"RUN_SAMPLE" => ["Run this job as a sample", ["y", "n"]],
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# @!attribute api_key
|
|
24
|
+
# @return [String]
|
|
25
|
+
def api_key
|
|
26
|
+
@api_key ||= env["API_KEY"] || user_config.api_key or raise UsageError, "API key not given, use `API_KEY=`"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @!attribute api_url
|
|
30
|
+
# @return [String]
|
|
31
|
+
def api_url
|
|
32
|
+
@api_url ||= igetset(:api_url) do
|
|
33
|
+
env["API_URL"] || user_config.api_url
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# An instance of <tt>API::Request::Base</tt> successor.
|
|
38
|
+
# @abstract
|
|
39
|
+
# @!attribute request
|
|
40
|
+
# @return [Object]
|
|
41
|
+
def request
|
|
42
|
+
@request or raise NotImplementedError, "Redefine `request` in your class: #{self.class}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Request's cURL representation. Default is <tt>request.to_curl</tt>
|
|
46
|
+
# @!attribute request_curl
|
|
47
|
+
# @return [Array]
|
|
48
|
+
def request_curl
|
|
49
|
+
require_attr(:request).to_curl
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# An <tt>API::Response::Base</tt> successor instance.
|
|
53
|
+
# @!attribute response
|
|
54
|
+
# @return [Object]
|
|
55
|
+
def response
|
|
56
|
+
@response ||= require_attr(:session).response
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Raw server response text. Default is <tt>session.server_raw</tt>.
|
|
60
|
+
# This attribute is used by <tt>RAW=y</tt> mode only.
|
|
61
|
+
# @!attribute server_raw
|
|
62
|
+
# @return [String]
|
|
63
|
+
def server_raw
|
|
64
|
+
@server_raw ||= require_attr(:session).server_raw
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# An instance of <tt>API::Session</tt> built around {#request}.
|
|
68
|
+
# @!attribute session
|
|
69
|
+
# @return [Object]
|
|
70
|
+
def session
|
|
71
|
+
@session ||= API::Session.new(request: require_attr(:request))
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @!attribute user_config
|
|
75
|
+
# @return [UserConfig]
|
|
76
|
+
def user_config
|
|
77
|
+
@user_config ||= UserConfig.new
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
#--------------------------------------- Misc
|
|
81
|
+
|
|
82
|
+
# Extract response's value according to a heading spec.
|
|
83
|
+
#
|
|
84
|
+
# get_table_value(reasponse, ["ID", :id, :right])
|
|
85
|
+
def get_table_value(r, hspec)
|
|
86
|
+
if (m = hspec[1]).is_a? Proc
|
|
87
|
+
m.(r)
|
|
88
|
+
else
|
|
89
|
+
r.public_send(m)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
private :get_table_value
|
|
93
|
+
|
|
94
|
+
# "Inspect or nil" -- format a scalar for table-friendly output.
|
|
95
|
+
#
|
|
96
|
+
# inil(5) # => "5"
|
|
97
|
+
# inil(nil) # => "-"
|
|
98
|
+
#
|
|
99
|
+
# @return [String]
|
|
100
|
+
def inil(v)
|
|
101
|
+
v.nil?? "-" : v.inspect
|
|
102
|
+
end
|
|
103
|
+
private :inil
|
|
104
|
+
|
|
105
|
+
# Print request as a ready-to-run cURL command. Return 0.
|
|
106
|
+
def print_curl_request
|
|
107
|
+
stdout.puts "curl #{request_curl.map(&:shellescape).join(' ')}"
|
|
108
|
+
0
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Print error response as a standard table. Return 1.
|
|
112
|
+
#
|
|
113
|
+
# return print_error_response if response_error?
|
|
114
|
+
def print_error_response
|
|
115
|
+
"ErrorResponse".tap do |label|
|
|
116
|
+
headings = [
|
|
117
|
+
["Status", :status, :center],
|
|
118
|
+
["Message", :message],
|
|
119
|
+
["ExecTime", :execution_time, :right],
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
table = Table.new(
|
|
123
|
+
headings: headings.map { |ar| ar[0] },
|
|
124
|
+
rows: [headings.map { |ar| get_table_value(response, ar) }],
|
|
125
|
+
).align!(headings)
|
|
126
|
+
|
|
127
|
+
stdout.puts "\n#{label}:"
|
|
128
|
+
stdout.puts table
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
1
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Print raw response. Return 0.
|
|
135
|
+
#
|
|
136
|
+
# return print_raw_response if env_truthy? "RAW"
|
|
137
|
+
def print_server_raw
|
|
138
|
+
stdout.puts server_raw
|
|
139
|
+
0
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
#--------------------------------------- Main
|
|
143
|
+
|
|
144
|
+
def slim_main1
|
|
145
|
+
# Perform common boilerplate actions.
|
|
146
|
+
return print_curl_request if env_truthy? "CURL"
|
|
147
|
+
|
|
148
|
+
# Any of these, unless during tests, touch `response`, which triggers the actual request.
|
|
149
|
+
return print_server_raw if env_truthy? "RAW"
|
|
150
|
+
return print_error_response if response.error?
|
|
151
|
+
|
|
152
|
+
call_slim_main(0)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end; end; end
|
|
156
|
+
|
|
157
|
+
#
|
|
158
|
+
# Implementation notes:
|
|
159
|
+
#
|
|
160
|
+
# * See `def request` for a cool testing hack: `@request || raise`.
|
|
161
|
+
# It makes it possible to test abstract's class functionality without having to create successors
|
|
162
|
+
# just to stub an attribute.
|
|
163
|
+
# * Column output order in all scripts is logical+alphabetical.
|
|
164
|
+
# Most meaningful attributes are output first, the rest are loosely grouped based on decreasing
|
|
165
|
+
# importance.
|
|
166
|
+
# * We deliberately don't use `OptionParser` in final scripts, using "meta-documented" env variables
|
|
167
|
+
# instead. See `envar` and stuff.
|
|
168
|
+
# * `manifest` is an instance method to simplify its usage in output code. `self.class.manifest` is
|
|
169
|
+
# clunky and thus less smart.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
|
|
2
|
+
require "neverbounce"
|
|
3
|
+
|
|
4
|
+
require_relative "request_maker"
|
|
5
|
+
|
|
6
|
+
module NeverBounce; module CLI; module Script
|
|
7
|
+
class SingleCheck < RequestMaker
|
|
8
|
+
attr_writer :address_info, :email, :credits_info, :timeout
|
|
9
|
+
|
|
10
|
+
envar "ADDRESS_INFO", "Request additional address info", ["y", "n"]
|
|
11
|
+
envar "EMAIL*", "E-mail to check", ["alice@isp.com", "bob.smith+1@domain.com"]
|
|
12
|
+
envar "CREDITS_INFO", "Request additional credits info", ["y", "n"]
|
|
13
|
+
envar "TIMEOUT", "Timeout in seconds to verify the address", ["5"]
|
|
14
|
+
|
|
15
|
+
# @return [true]
|
|
16
|
+
# @return [false]
|
|
17
|
+
# @return [nil]
|
|
18
|
+
def address_info
|
|
19
|
+
igetset(:address_info) do
|
|
20
|
+
if env.has_key?(k = "ADDRESS_INFO")
|
|
21
|
+
env_truthy?(k)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [true]
|
|
27
|
+
# @return [false]
|
|
28
|
+
# @return [nil]
|
|
29
|
+
def credits_info
|
|
30
|
+
igetset(:credits_info) do
|
|
31
|
+
if env.has_key?(k = "CREDITS_INFO")
|
|
32
|
+
env_truthy?(k)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def email
|
|
38
|
+
@email ||= env[k = "EMAIL"] or raise UsageError, "E-mail address not given, use `#{k}=`"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# An <tt>API::Request::SingleCheck</tt>.
|
|
42
|
+
# @!attribute request
|
|
43
|
+
# @return [Object]
|
|
44
|
+
def request
|
|
45
|
+
@request ||= API::Request::SingleCheck.new({
|
|
46
|
+
address_info: address_info,
|
|
47
|
+
api_key: api_key,
|
|
48
|
+
credits_info: credits_info,
|
|
49
|
+
email: email,
|
|
50
|
+
timeout: timeout,
|
|
51
|
+
})
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def timeout
|
|
55
|
+
igetset(:timeout) do
|
|
56
|
+
if (v = env["TIMEOUT"])
|
|
57
|
+
begin
|
|
58
|
+
Integer(v)
|
|
59
|
+
rescue ArgumentError => e
|
|
60
|
+
raise UsageError, e.message
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
#--------------------------------------- Manifest
|
|
67
|
+
|
|
68
|
+
# @!attribute manifest
|
|
69
|
+
# @return [Manifest]
|
|
70
|
+
def manifest
|
|
71
|
+
@manifest ||= Manifest.new(
|
|
72
|
+
name: "nb-single-check",
|
|
73
|
+
function: "Check a single e-mail",
|
|
74
|
+
cmdline: "[options] [VAR1=value] [VAR2=value] ...",
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
#--------------------------------------- Main
|
|
79
|
+
|
|
80
|
+
# @return [Integer]
|
|
81
|
+
def slim_main
|
|
82
|
+
"Response".tap do |label|
|
|
83
|
+
headings = [
|
|
84
|
+
["Result", :result, :center],
|
|
85
|
+
[
|
|
86
|
+
"Flags",
|
|
87
|
+
->(r) { r.flags.sort.join("\n") },
|
|
88
|
+
],
|
|
89
|
+
["SuggCorr", :suggested_correction],
|
|
90
|
+
|
|
91
|
+
["ExecTime", :execution_time, :right],
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
table = Table.new(
|
|
95
|
+
headings: headings.map { |ar| ar[0] },
|
|
96
|
+
rows: [headings.map { |ar| get_table_value(response, ar) }],
|
|
97
|
+
).align!(headings)
|
|
98
|
+
|
|
99
|
+
stdout.puts "\n#{label}:"
|
|
100
|
+
stdout.puts table
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
response.address_info? and "AddressInfo".tap do |label|
|
|
104
|
+
headings = [
|
|
105
|
+
["Addr", :addr],
|
|
106
|
+
["Alias", :alias],
|
|
107
|
+
["Domain", :domain],
|
|
108
|
+
["FQDN", :fqdn],
|
|
109
|
+
["Host", :host],
|
|
110
|
+
["NormEmail", :normalized_email],
|
|
111
|
+
["OrigEmail", :original_email],
|
|
112
|
+
["Subdomain", :subdomain],
|
|
113
|
+
["TLD", :tld],
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
table = Table.new(
|
|
117
|
+
headings: headings.map { |ar| ar[0] },
|
|
118
|
+
rows: [headings.map { |ar| get_table_value(response.address_info, ar) }],
|
|
119
|
+
).align!(headings)
|
|
120
|
+
|
|
121
|
+
stdout.puts "\n#{label}:"
|
|
122
|
+
stdout.puts table
|
|
123
|
+
end # response.address_info?
|
|
124
|
+
|
|
125
|
+
response.credits_info? and "CreditsInfo".tap do |label|
|
|
126
|
+
headings = [
|
|
127
|
+
["FreeRmn", :free_credits_remaining, :right],
|
|
128
|
+
["FreeUsed", :free_credits_used, :right],
|
|
129
|
+
(["MonthlyUsage", :monthly_api_usage, :right] if response.credits_info.monthly?),
|
|
130
|
+
(["PaidRmn", :paid_credits_remaining, :right] if response.credits_info.paid?),
|
|
131
|
+
(["PaidUsed", :paid_credits_used, :right] if response.credits_info.paid?),
|
|
132
|
+
].compact
|
|
133
|
+
|
|
134
|
+
table = Table.new(
|
|
135
|
+
headings: headings.map { |ar| ar[0] },
|
|
136
|
+
rows: [headings.map { |ar| get_table_value(response.credits_info, ar) }],
|
|
137
|
+
).align!(headings)
|
|
138
|
+
|
|
139
|
+
stdout.puts "\n#{label}:"
|
|
140
|
+
stdout.puts table
|
|
141
|
+
end # response.credits_info?
|
|
142
|
+
|
|
143
|
+
0
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end; end; end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
require "terminal-table"
|
|
3
|
+
|
|
4
|
+
module NeverBounce; module CLI; module Script
|
|
5
|
+
# Our custom table class.
|
|
6
|
+
class Table < Terminal::Table
|
|
7
|
+
# Align table rows according to headings spec.
|
|
8
|
+
#
|
|
9
|
+
# headings = [
|
|
10
|
+
# ["Status", :status],
|
|
11
|
+
# ["Completed", :completed, :right],
|
|
12
|
+
# ["Processing", :processing, :right],
|
|
13
|
+
# ]
|
|
14
|
+
#
|
|
15
|
+
# table = Table.new(headings: ..., rows: ...).align!(headings)
|
|
16
|
+
# puts table
|
|
17
|
+
#
|
|
18
|
+
# NOTE: Invoke <b>after</b> adding row data.
|
|
19
|
+
#
|
|
20
|
+
# @return [self]
|
|
21
|
+
def align!(headings)
|
|
22
|
+
headings.each_with_index do |ar, i|
|
|
23
|
+
if (v = ar[2])
|
|
24
|
+
align_column(i, v)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Center-align headings by default.
|
|
32
|
+
# @return [void]
|
|
33
|
+
def headings=(ar)
|
|
34
|
+
super(ar.map do |item|
|
|
35
|
+
if item.is_a? String
|
|
36
|
+
{value: item, alignment: :center}
|
|
37
|
+
else
|
|
38
|
+
item
|
|
39
|
+
end
|
|
40
|
+
end)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end; end; end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
|
|
2
|
+
require "yaml"
|
|
3
|
+
|
|
4
|
+
require "never_bounce/cli/feature/basic_initialize"
|
|
5
|
+
|
|
6
|
+
require_relative "../user_config"
|
|
7
|
+
|
|
8
|
+
module NeverBounce; module CLI; class UserConfig
|
|
9
|
+
# User's configuration file content.
|
|
10
|
+
# @see #filename
|
|
11
|
+
# @see UserConfig
|
|
12
|
+
# @see CLI::Feature::BasicInitialize
|
|
13
|
+
class FileContent
|
|
14
|
+
CLI::Feature::BasicInitialize.load(self)
|
|
15
|
+
|
|
16
|
+
attr_writer :body, :body_hash, :env, :filename
|
|
17
|
+
|
|
18
|
+
# YAML configuration, source text.
|
|
19
|
+
# @!attribute body
|
|
20
|
+
# @return [String]
|
|
21
|
+
def body
|
|
22
|
+
@body ||= begin
|
|
23
|
+
File.read(filename)
|
|
24
|
+
rescue Errno::ENOENT # Missing file is okay, let other exceptions manifest.
|
|
25
|
+
""
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# YAML configuration, parsed.
|
|
30
|
+
# @!attribute body_hash
|
|
31
|
+
# @return [Hash]
|
|
32
|
+
def body_hash
|
|
33
|
+
@body_hash ||= body.to_s.empty?? {} : YAML.load(body)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# A copy of environment for read purposes. Default is <tt>ENV.to_h</tt>.
|
|
37
|
+
# @return [Hash]
|
|
38
|
+
def env
|
|
39
|
+
@env ||= ENV.to_h
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Configuration filename. Default is <tt>$HOME/.neverbounce.yml</tt>.
|
|
43
|
+
# @return [String]
|
|
44
|
+
def filename
|
|
45
|
+
@filename ||= File.join(env["HOME"], ".neverbounce.yml")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#---------------------------------------
|
|
49
|
+
|
|
50
|
+
# Fetch a value.
|
|
51
|
+
#
|
|
52
|
+
# config_file["api_key"]
|
|
53
|
+
# config_file[:api_key] # Same as above.
|
|
54
|
+
def [](k)
|
|
55
|
+
body_hash[k.to_s]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# <tt>true</tt> if key is set.
|
|
59
|
+
#
|
|
60
|
+
# has_key?("api_key")
|
|
61
|
+
# has_key?(:api_key) # Identical to the previous one.
|
|
62
|
+
def has_key?(k)
|
|
63
|
+
body_hash.has_key? k.to_s
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end; end; end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
require "never_bounce/cli/feature/eigencache"
|
|
3
|
+
|
|
4
|
+
module NeverBounce; module CLI
|
|
5
|
+
# User's configuration values.
|
|
6
|
+
# @see UserConfig::FileContent
|
|
7
|
+
class UserConfig
|
|
8
|
+
require_relative "user_config/file_content"
|
|
9
|
+
|
|
10
|
+
CLI::Feature::BasicInitialize.load(self)
|
|
11
|
+
CLI::Feature::Eigencache.load(self)
|
|
12
|
+
|
|
13
|
+
attr_writer :api_key, :api_url
|
|
14
|
+
|
|
15
|
+
# API key.
|
|
16
|
+
# @return [String]
|
|
17
|
+
def api_key
|
|
18
|
+
@api_key ||= fc[:api_key]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# API URL.
|
|
22
|
+
# @return [String]
|
|
23
|
+
def api_url
|
|
24
|
+
@api_url ||= fc[:api_url]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
#---------------------------------------
|
|
28
|
+
|
|
29
|
+
# @!attribute fc
|
|
30
|
+
# @return [FileContent]
|
|
31
|
+
def fc
|
|
32
|
+
_cache[:fc] ||= FileContent.new
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def fc=(obj)
|
|
36
|
+
_cache[:fc] = obj
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# "Touch" all attributes by loading them.
|
|
40
|
+
# @return [self]
|
|
41
|
+
def touch
|
|
42
|
+
api_key; api_url
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end; end
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# Implementation notes:
|
|
50
|
+
#
|
|
51
|
+
# * `api_url` has got no default here, it isn't a config responsibility.
|