fbe 0.9.0 → 0.11.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 +4 -4
- data/Gemfile.lock +1 -1
- data/assets/bylaws/bug-report-was-rewarded.liquid +1 -1
- data/assets/bylaws/code-contribution-was-rewarded.liquid +1 -1
- data/assets/bylaws/code-review-was-rewarded.liquid +1 -1
- data/assets/bylaws/enhancement-suggestion-was-rewarded.liquid +1 -1
- data/assets/bylaws/resolved-bug-was-rewarded.liquid +1 -1
- data/lib/fbe/award.rb +145 -3
- data/lib/fbe/conclude.rb +39 -2
- data/lib/fbe/github_graph.rb +52 -1
- data/lib/fbe/middleware/formatter.rb +14 -0
- data/lib/fbe/octo.rb +61 -5
- data/lib/fbe/pmp.rb +17 -1
- data/lib/fbe.rb +1 -1
- data/test/fbe/test_bylaws.rb +13 -12
- data/test/fbe/test_octo.rb +24 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3201ffc4c057451b41320d5fd1b93a856802015dd3dbfee82dc4859a8cb90eab
|
4
|
+
data.tar.gz: e1a340dc3d5315d7ee1b5f1087729cc4f22f06bb363c47a478fe5b25716c29e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 807f80b0aaa6b93995d57144b2cf129365a0d0646d33b5d53f841b9287abe52bdfd47d2040e7583ee10acf9f97d6578d94be6f714149764fa8dac3e967e209db
|
7
|
+
data.tar.gz: 0f94c246a1f2ab9132b1dc80fca6c7207ce274b03ce4524dc9b01c7d574cc51e9c97e045fd00a2f5c2749b1a8b62f05496c1b3638e006796c6331bf714cc9f37
|
data/Gemfile.lock
CHANGED
data/lib/fbe/award.rb
CHANGED
@@ -62,20 +62,46 @@ class Fbe::Award
|
|
62
62
|
bylaw
|
63
63
|
end
|
64
64
|
|
65
|
-
# A term for
|
65
|
+
# A term module for processing award billing logic.
|
66
|
+
#
|
67
|
+
# This module is used to extend Factbase terms to handle award calculations
|
68
|
+
# and billing operations. It provides methods to calculate point values and
|
69
|
+
# evaluate complex award expressions.
|
66
70
|
module BTerm
|
71
|
+
# Returns a string representation of the term.
|
72
|
+
#
|
73
|
+
# @return [String] The term as a string in S-expression format
|
74
|
+
# @example
|
75
|
+
# term.to_s #=> "(give (times loc 5) 'for LoC')"
|
67
76
|
def to_s
|
68
77
|
"(#{@op} #{@operands.join(' ')})"
|
69
78
|
end
|
70
79
|
|
80
|
+
# Indicates whether the term is static.
|
81
|
+
#
|
82
|
+
# @return [Boolean] Always returns true for BTerm
|
71
83
|
def static?
|
72
84
|
true
|
73
85
|
end
|
74
86
|
|
87
|
+
# Indicates whether the term is abstract.
|
88
|
+
#
|
89
|
+
# @return [Boolean] Always returns false for BTerm
|
75
90
|
def abstract?
|
76
91
|
false
|
77
92
|
end
|
78
93
|
|
94
|
+
# Processes this term and applies its operations to a bill.
|
95
|
+
#
|
96
|
+
# @param [Fbe::Award::Bill] bill The bill to update
|
97
|
+
# @return [nil]
|
98
|
+
# @raise [RuntimeError] If there's a failure processing any term
|
99
|
+
# @example
|
100
|
+
# term = Factbase::Syntax.new('(award (give 100 "for effort"))').to_term
|
101
|
+
# term.redress!(Fbe::Award::BTerm)
|
102
|
+
# bill = Fbe::Award::Bill.new
|
103
|
+
# term.bill_to(bill)
|
104
|
+
# bill.points #=> 100
|
79
105
|
def bill_to(bill)
|
80
106
|
case @op
|
81
107
|
when :award
|
@@ -105,6 +131,16 @@ class Fbe::Award
|
|
105
131
|
end
|
106
132
|
end
|
107
133
|
|
134
|
+
# Evaluates a value in the context of a bill.
|
135
|
+
#
|
136
|
+
# @param [Object] any The value to evaluate (symbol, term, or literal)
|
137
|
+
# @param [Fbe::Award::Bill] bill The bill providing context for evaluation
|
138
|
+
# @return [Object] The evaluated value
|
139
|
+
# @raise [RuntimeError] If a symbol isn't found in the bill
|
140
|
+
# @example
|
141
|
+
# bill = Fbe::Award::Bill.new
|
142
|
+
# bill.set(:loc, 100)
|
143
|
+
# term.to_val(:loc, bill) #=> 100
|
108
144
|
def to_val(any, bill)
|
109
145
|
if any.is_a?(BTerm)
|
110
146
|
any.calc(bill)
|
@@ -117,6 +153,21 @@ class Fbe::Award
|
|
117
153
|
end
|
118
154
|
end
|
119
155
|
|
156
|
+
# Calculates the value of this term in the context of a bill.
|
157
|
+
#
|
158
|
+
# This method evaluates terms like arithmetic operations, logical
|
159
|
+
# operations, and other expressions based on the operator type.
|
160
|
+
#
|
161
|
+
# @param [Fbe::Award::Bill] bill The bill providing context for calculation
|
162
|
+
# @return [Object] The calculated value (number, boolean, etc.)
|
163
|
+
# @raise [RuntimeError] If the term operation is unknown
|
164
|
+
# @example
|
165
|
+
# bill = Fbe::Award::Bill.new
|
166
|
+
# bill.set(:x, 10)
|
167
|
+
# bill.set(:y, 5)
|
168
|
+
# term = Factbase::Syntax.new('(times x y)').to_term
|
169
|
+
# term.redress!(Fbe::Award::BTerm)
|
170
|
+
# term.calc(bill) #=> 50
|
120
171
|
def calc(bill)
|
121
172
|
case @op
|
122
173
|
when :total
|
@@ -275,29 +326,70 @@ class Fbe::Award
|
|
275
326
|
end
|
276
327
|
end
|
277
328
|
|
278
|
-
# A bill.
|
329
|
+
# A bill class that accumulates points and explanations for rewards.
|
330
|
+
#
|
331
|
+
# This class tracks variables, point values, and explanatory text
|
332
|
+
# for each award component. It provides methods to calculate total points
|
333
|
+
# and generate a human-readable summary of the rewards.
|
279
334
|
class Bill
|
335
|
+
# @return [Hash] Variables set in this bill
|
280
336
|
attr_reader :vars
|
281
337
|
|
338
|
+
# Creates a new empty bill.
|
339
|
+
#
|
340
|
+
# @example
|
341
|
+
# bill = Fbe::Award::Bill.new
|
282
342
|
def initialize
|
283
343
|
@lines = []
|
284
344
|
@vars = {}
|
285
345
|
end
|
286
346
|
|
347
|
+
# Sets a variable in the bill's context.
|
348
|
+
#
|
349
|
+
# @param [Symbol] var The variable name
|
350
|
+
# @param [Object] value The value to assign
|
351
|
+
# @return [Object] The assigned value
|
352
|
+
# @example
|
353
|
+
# bill = Fbe::Award::Bill.new
|
354
|
+
# bill.set(:lines_of_code, 500)
|
287
355
|
def set(var, value)
|
288
356
|
@vars[var] = value
|
289
357
|
end
|
290
358
|
|
359
|
+
# Adds a point value with explanatory text to the bill.
|
360
|
+
#
|
361
|
+
# @param [Integer, Float] value The point value to add
|
362
|
+
# @param [String] text The explanation for these points
|
363
|
+
# @return [nil]
|
364
|
+
# @note Zero-valued points are ignored
|
365
|
+
# @example
|
366
|
+
# bill = Fbe::Award::Bill.new
|
367
|
+
# bill.line(50, "for code review")
|
291
368
|
def line(value, text)
|
292
369
|
return if value.zero?
|
293
370
|
text = text.gsub(/\$\{([a-z_0-9]+)\}/) { |_x| @vars[Regexp.last_match[1].to_sym] }
|
294
371
|
@lines << { v: value, t: text }
|
295
372
|
end
|
296
373
|
|
374
|
+
# Calculates the total points in this bill.
|
375
|
+
#
|
376
|
+
# @return [Integer] The sum of all point values, rounded to an integer
|
377
|
+
# @example
|
378
|
+
# bill = Fbe::Award::Bill.new
|
379
|
+
# bill.line(42.5, "for answer")
|
380
|
+
# bill.points #=> 43
|
297
381
|
def points
|
298
382
|
@lines.sum { |l| l[:v] }.to_f.round.to_i
|
299
383
|
end
|
300
384
|
|
385
|
+
# Generates a human-readable summary of the bill.
|
386
|
+
#
|
387
|
+
# @return [String] A description of the points earned
|
388
|
+
# @example
|
389
|
+
# bill = Fbe::Award::Bill.new
|
390
|
+
# bill.line(50, "for code review")
|
391
|
+
# bill.line(25, "for documentation")
|
392
|
+
# bill.greeting #=> "You've earned +75 points for this: +50 for code review; +25 for documentation. "
|
301
393
|
def greeting
|
302
394
|
items = @lines.map { |l| "#{format('%+d', l[:v])} #{l[:t]}" }
|
303
395
|
case items.size
|
@@ -311,33 +403,83 @@ class Fbe::Award
|
|
311
403
|
end
|
312
404
|
end
|
313
405
|
|
314
|
-
# A
|
406
|
+
# A class for generating human-readable bylaws.
|
407
|
+
#
|
408
|
+
# This class builds textual descriptions of award bylaws including
|
409
|
+
# introductions, calculation steps, and variable substitutions.
|
410
|
+
# It produces Markdown-formatted output describing how awards are calculated.
|
315
411
|
class Bylaw
|
412
|
+
# @return [Hash] Variables defined in this bylaw
|
316
413
|
attr_reader :vars
|
317
414
|
|
415
|
+
# Creates a new empty bylaw.
|
416
|
+
#
|
417
|
+
# @example
|
418
|
+
# bylaw = Fbe::Award::Bylaw.new
|
318
419
|
def initialize
|
319
420
|
@lines = []
|
320
421
|
@intro = ''
|
321
422
|
@lets = {}
|
322
423
|
end
|
323
424
|
|
425
|
+
# Removes the specified number of most recently added lines.
|
426
|
+
#
|
427
|
+
# @param [Integer] num The number of lines to remove from the end
|
428
|
+
# @return [Array] The removed lines
|
429
|
+
# @example
|
430
|
+
# bylaw = Fbe::Award::Bylaw.new
|
431
|
+
# bylaw.line("award 50 points")
|
432
|
+
# bylaw.line("award 30 points")
|
433
|
+
# bylaw.revert(1) # Removes "award 30 points"
|
324
434
|
def revert(num)
|
325
435
|
@lines.slice!(-num, num)
|
326
436
|
end
|
327
437
|
|
438
|
+
# Sets the introductory text for the bylaw.
|
439
|
+
#
|
440
|
+
# @param [String] text The introductory text to set
|
441
|
+
# @return [String] The introductory text
|
442
|
+
# @example
|
443
|
+
# bylaw = Fbe::Award::Bylaw.new
|
444
|
+
# bylaw.intro("This bylaw determines rewards for code contributions")
|
328
445
|
def intro(text)
|
329
446
|
@intro = text
|
330
447
|
end
|
331
448
|
|
449
|
+
# Adds a line of text to the bylaw, replacing variable references.
|
450
|
+
#
|
451
|
+
# @param [String] line The line of text to add
|
452
|
+
# @return [nil]
|
453
|
+
# @example
|
454
|
+
# bylaw = Fbe::Award::Bylaw.new
|
455
|
+
# bylaw.let(:points, 50)
|
456
|
+
# bylaw.line("award ${points} points")
|
332
457
|
def line(line)
|
333
458
|
line = line.gsub(/\$\{([a-z_0-9]+)\}/) { |_x| "**#{@lets[Regexp.last_match[1].to_sym]}**" }
|
334
459
|
@lines << line
|
335
460
|
end
|
336
461
|
|
462
|
+
# Registers a variable with its value for substitution in lines.
|
463
|
+
#
|
464
|
+
# @param [Symbol] key The variable name
|
465
|
+
# @param [Object] value The value to associate with the variable
|
466
|
+
# @return [Object] The assigned value
|
467
|
+
# @example
|
468
|
+
# bylaw = Fbe::Award::Bylaw.new
|
469
|
+
# bylaw.let(:points, 50)
|
337
470
|
def let(key, value)
|
338
471
|
@lets[key] = value
|
339
472
|
end
|
340
473
|
|
474
|
+
# Generates a Markdown-formatted representation of the bylaw.
|
475
|
+
#
|
476
|
+
# @return [String] The bylaw formatted as Markdown text
|
477
|
+
# @example
|
478
|
+
# bylaw = Fbe::Award::Bylaw.new
|
479
|
+
# bylaw.intro("This bylaw determines rewards for code contributions")
|
480
|
+
# bylaw.line("award **50** points")
|
481
|
+
# bylaw.markdown
|
482
|
+
# #=> "This bylaw determines rewards for code contributions. Just award **50** points."
|
341
483
|
def markdown
|
342
484
|
pars = []
|
343
485
|
pars << "#{@intro}." unless @intro.empty?
|
data/lib/fbe/conclude.rb
CHANGED
@@ -160,8 +160,24 @@ class Fbe::Conclude
|
|
160
160
|
|
161
161
|
private
|
162
162
|
|
163
|
-
#
|
164
|
-
#
|
163
|
+
# Executes a query and processes each matching fact.
|
164
|
+
#
|
165
|
+
# This internal method handles fetching facts from the factbase,
|
166
|
+
# monitoring quotas and timeouts, and processing each fact through
|
167
|
+
# the provided block.
|
168
|
+
#
|
169
|
+
# @yield [Factbase::Transaction, Factbase::Fact] Transaction and the matching fact
|
170
|
+
# @return [Integer] The count of facts processed
|
171
|
+
# @example
|
172
|
+
# # Inside the Fbe::Conclude class
|
173
|
+
# def example_method
|
174
|
+
# roll do |fbt, fact|
|
175
|
+
# # Process the fact
|
176
|
+
# new_fact = fbt.insert
|
177
|
+
# # Return the new fact
|
178
|
+
# new_fact
|
179
|
+
# end
|
180
|
+
# end
|
165
181
|
def roll(&)
|
166
182
|
passed = 0
|
167
183
|
start = Time.now
|
@@ -185,6 +201,27 @@ class Fbe::Conclude
|
|
185
201
|
passed
|
186
202
|
end
|
187
203
|
|
204
|
+
# Populates a new fact based on a previous fact and a processing block.
|
205
|
+
#
|
206
|
+
# This internal method copies specified properties from the previous fact,
|
207
|
+
# calls the provided block for custom processing, and sets metadata
|
208
|
+
# on the new fact.
|
209
|
+
#
|
210
|
+
# @param [Factbase::Fact] fact The fact to populate
|
211
|
+
# @param [Factbase::Fact] prev The previous fact to copy from
|
212
|
+
# @yield [Factbase::Fact, Factbase::Fact] New fact and the previous fact
|
213
|
+
# @return [nil]
|
214
|
+
# @example
|
215
|
+
# # Inside the Fbe::Conclude class
|
216
|
+
# def example_method
|
217
|
+
# @fb.txn do |fbt|
|
218
|
+
# new_fact = fbt.insert
|
219
|
+
# fill(new_fact, existing_fact) do |n, prev|
|
220
|
+
# n.some_property = "new value"
|
221
|
+
# "Operation completed" # This becomes fact.details
|
222
|
+
# end
|
223
|
+
# end
|
224
|
+
# end
|
188
225
|
def fill(fact, prev)
|
189
226
|
@follows.each do |follow|
|
190
227
|
v = prev.send(follow)
|
data/lib/fbe/github_graph.rb
CHANGED
@@ -35,11 +35,29 @@ class Fbe::Graph
|
|
35
35
|
@host = host
|
36
36
|
end
|
37
37
|
|
38
|
+
# Executes a GraphQL query against the GitHub API.
|
39
|
+
#
|
40
|
+
# @param [String] qry The GraphQL query to execute
|
41
|
+
# @return [GraphQL::Client::Response] The query result data
|
42
|
+
# @example
|
43
|
+
# graph = Fbe::Graph.new(token: 'github_token')
|
44
|
+
# result = graph.query('{viewer {login}}')
|
45
|
+
# puts result.viewer.login #=> "octocat"
|
38
46
|
def query(qry)
|
39
47
|
result = client.query(client.parse(qry))
|
40
48
|
result.data
|
41
49
|
end
|
42
50
|
|
51
|
+
# Retrieves resolved conversation threads from a pull request.
|
52
|
+
#
|
53
|
+
# @param [String] owner The repository owner (username or organization)
|
54
|
+
# @param [String] name The repository name
|
55
|
+
# @param [Integer] number The pull request number
|
56
|
+
# @return [Array<Hash>] An array of resolved conversation threads with their comments
|
57
|
+
# @example
|
58
|
+
# graph = Fbe::Graph.new(token: 'github_token')
|
59
|
+
# threads = graph.resolved_conversations('octocat', 'Hello-World', 42)
|
60
|
+
# threads.first['comments']['nodes'].first['body'] #=> "Great work!"
|
43
61
|
def resolved_conversations(owner, name, number)
|
44
62
|
result = query(
|
45
63
|
<<~GRAPHQL
|
@@ -72,6 +90,16 @@ class Fbe::Graph
|
|
72
90
|
end || []
|
73
91
|
end
|
74
92
|
|
93
|
+
# Gets the total number of commits in a branch.
|
94
|
+
#
|
95
|
+
# @param [String] owner The repository owner (username or organization)
|
96
|
+
# @param [String] name The repository name
|
97
|
+
# @param [String] branch The branch name (e.g., "master" or "main")
|
98
|
+
# @return [Integer] The total number of commits in the branch
|
99
|
+
# @example
|
100
|
+
# graph = Fbe::Graph.new(token: 'github_token')
|
101
|
+
# count = graph.total_commits('octocat', 'Hello-World', 'main')
|
102
|
+
# puts count #=> 42
|
75
103
|
def total_commits(owner, name, branch)
|
76
104
|
result = query(
|
77
105
|
<<~GRAPHQL
|
@@ -93,6 +121,15 @@ class Fbe::Graph
|
|
93
121
|
result.repository.ref.target.history.total_count
|
94
122
|
end
|
95
123
|
|
124
|
+
# Gets the total number of issues and pull requests in a repository.
|
125
|
+
#
|
126
|
+
# @param [String] owner The repository owner (username or organization)
|
127
|
+
# @param [String] name The repository name
|
128
|
+
# @return [Hash] A hash with 'issues' and 'pulls' counts
|
129
|
+
# @example
|
130
|
+
# graph = Fbe::Graph.new(token: 'github_token')
|
131
|
+
# counts = graph.total_issues_and_pulls('octocat', 'Hello-World')
|
132
|
+
# puts counts #=> {"issues"=>42, "pulls"=>17}
|
96
133
|
def total_issues_and_pulls(owner, name)
|
97
134
|
result = query(
|
98
135
|
<<~GRAPHQL
|
@@ -116,6 +153,9 @@ class Fbe::Graph
|
|
116
153
|
|
117
154
|
private
|
118
155
|
|
156
|
+
# Creates or returns a cached GraphQL client instance.
|
157
|
+
#
|
158
|
+
# @return [GraphQL::Client] A configured GraphQL client for GitHub
|
119
159
|
def client
|
120
160
|
@client ||=
|
121
161
|
begin
|
@@ -127,13 +167,24 @@ class Fbe::Graph
|
|
127
167
|
end
|
128
168
|
end
|
129
169
|
|
130
|
-
#
|
170
|
+
# HTTP transport class for GraphQL client to communicate with GitHub API
|
171
|
+
#
|
172
|
+
# This class extends GraphQL::Client::HTTP to handle GitHub-specific
|
173
|
+
# authentication and endpoints.
|
131
174
|
class HTTP < GraphQL::Client::HTTP
|
175
|
+
# Initializes a new HTTP transport with GitHub authentication.
|
176
|
+
#
|
177
|
+
# @param [String] token GitHub API token for authentication
|
178
|
+
# @param [String] host GitHub API host (default: 'api.github.com')
|
132
179
|
def initialize(token, host)
|
133
180
|
@token = token
|
134
181
|
super("https://#{host}/graphql")
|
135
182
|
end
|
136
183
|
|
184
|
+
# Provides headers for GraphQL requests including authentication.
|
185
|
+
#
|
186
|
+
# @param [Object] _context The GraphQL request context (unused)
|
187
|
+
# @return [Hash] Headers for the request
|
137
188
|
def headers(_context)
|
138
189
|
{ Authorization: "Bearer #{@token}" }
|
139
190
|
end
|
@@ -54,11 +54,25 @@ class Fbe::Middleware::Formatter < Faraday::Logging::Formatter
|
|
54
54
|
|
55
55
|
private
|
56
56
|
|
57
|
+
# Indents text with two spaces, including all lines.
|
58
|
+
#
|
59
|
+
# @param [String, nil] txt The text to indent
|
60
|
+
# @return [String] The indented text, or an empty string if input was nil
|
61
|
+
# @example
|
62
|
+
# shifted("line1\nline2")
|
63
|
+
# #=> " line1\n line2"
|
57
64
|
def shifted(txt)
|
58
65
|
return '' if txt.nil?
|
59
66
|
" #{txt.gsub("\n", "\n ")}"
|
60
67
|
end
|
61
68
|
|
69
|
+
# Formats HTTP headers as a multi-line string.
|
70
|
+
#
|
71
|
+
# @param [Hash, nil] headers The headers to format
|
72
|
+
# @return [String] The formatted headers, or an empty string if input was nil
|
73
|
+
# @example
|
74
|
+
# dump_headers({"Content-Type" => "application/json", "Authorization" => "Bearer token"})
|
75
|
+
# #=> "Content-Type: \"application/json\"\nAuthorization: \"Bearer token\""
|
62
76
|
def dump_headers(headers)
|
63
77
|
return '' if headers.nil?
|
64
78
|
headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
|
data/lib/fbe/octo.rb
CHANGED
@@ -50,7 +50,8 @@ def Fbe.octo(options: $options, global: $global, loog: $loog)
|
|
50
50
|
loog.warn('The GitHub API token is an empty string, won\'t use it')
|
51
51
|
else
|
52
52
|
o = Octokit::Client.new(access_token: token)
|
53
|
-
loog.info("Accessing GitHub API with a token (#{token.length} chars, ending by #{token[-4..].inspect}
|
53
|
+
loog.info("Accessing GitHub API with a token (#{token.length} chars, ending by #{token[-4..].inspect}, " \
|
54
|
+
"#{Octokit::Client.new(access_token: token).rate_limit.remaining} quota remaining)")
|
54
55
|
end
|
55
56
|
o.auto_paginate = true
|
56
57
|
o.per_page = 100
|
@@ -124,17 +125,40 @@ def Fbe.octo(options: $options, global: $global, loog: $loog)
|
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
127
|
-
# Fake GitHub client
|
128
|
+
# Fake GitHub client for testing purposes.
|
129
|
+
#
|
130
|
+
# This class provides mock implementations of Octokit methods for testing.
|
131
|
+
# It returns predictable data structures that mimic GitHub API responses.
|
128
132
|
class Fbe::FakeOctokit
|
133
|
+
# Generates a random time in the past.
|
134
|
+
#
|
135
|
+
# @return [Time] A random time within the last 10,000 seconds
|
136
|
+
# @example
|
137
|
+
# fake_client = Fbe::FakeOctokit.new
|
138
|
+
# time = fake_client.random_time #=> 2024-09-04 12:34:56 -0700
|
129
139
|
def random_time
|
130
140
|
Time.now - rand(10_000)
|
131
141
|
end
|
132
142
|
|
143
|
+
# Converts a string name to a deterministic integer.
|
144
|
+
#
|
145
|
+
# @param [String, Integer] name The name to convert or pass through
|
146
|
+
# @return [Integer, String] The sum of character codes if input is a string, otherwise the original input
|
147
|
+
# @example
|
148
|
+
# fake_client = Fbe::FakeOctokit.new
|
149
|
+
# fake_client.name_to_number("octocat") #=> 728
|
150
|
+
# fake_client.name_to_number(42) #=> 42
|
133
151
|
def name_to_number(name)
|
134
152
|
return name unless name.is_a?(String)
|
135
153
|
name.chars.sum(&:ord)
|
136
154
|
end
|
137
155
|
|
156
|
+
# Returns a mock rate limit object.
|
157
|
+
#
|
158
|
+
# @return [Object] An object with a remaining method that returns 100
|
159
|
+
# @example
|
160
|
+
# fake_client = Fbe::FakeOctokit.new
|
161
|
+
# fake_client.rate_limit.remaining #=> 100
|
138
162
|
def rate_limit
|
139
163
|
o = Object.new
|
140
164
|
def o.remaining
|
@@ -150,14 +174,27 @@ class Fbe::FakeOctokit
|
|
150
174
|
]
|
151
175
|
end
|
152
176
|
|
153
|
-
#
|
177
|
+
# Gives a star to a repository.
|
178
|
+
#
|
179
|
+
# @param [String] _repo The repository name (e.g., 'user/repo')
|
180
|
+
# @return [Boolean] Always returns true
|
181
|
+
# @example
|
182
|
+
# fake_client = Fbe::FakeOctokit.new
|
183
|
+
# fake_client.star('octocat/Hello-World') #=> true
|
154
184
|
def star(_repo)
|
155
185
|
true
|
156
186
|
end
|
157
187
|
|
158
|
-
#
|
159
|
-
#
|
188
|
+
# Gets details of a GitHub user.
|
189
|
+
#
|
190
|
+
# @param [String, Integer] uid The login of the user or its numeric ID
|
191
|
+
# @return [Hash] User information including id, login, and type
|
192
|
+
# @example
|
193
|
+
# fake_client = Fbe::FakeOctokit.new
|
194
|
+
# fake_client.user(526_301) #=> {:id=>444, :login=>"yegor256", :type=>"User"}
|
195
|
+
# fake_client.user('octocat') #=> {:id=>444, :login=>nil, :type=>"User"}
|
160
196
|
def user(uid)
|
197
|
+
raise Octokit::NotFound if [404_001, 404_002].include?(uid)
|
161
198
|
login = (uid == 526_301 ? 'yegor256' : 'torvalds') if uid.is_a?(Integer)
|
162
199
|
{
|
163
200
|
id: 444,
|
@@ -166,6 +203,15 @@ class Fbe::FakeOctokit
|
|
166
203
|
}
|
167
204
|
end
|
168
205
|
|
206
|
+
# Gets workflow runs for a repository.
|
207
|
+
#
|
208
|
+
# @param [String] repo The repository name
|
209
|
+
# @param [Hash] _opts Additional options (not used in mock)
|
210
|
+
# @return [Hash] Information about workflow runs including counts and details
|
211
|
+
# @example
|
212
|
+
# fake_client = Fbe::FakeOctokit.new
|
213
|
+
# result = fake_client.repository_workflow_runs('octocat/Hello-World')
|
214
|
+
# result[:total_count] #=> 2
|
169
215
|
def repository_workflow_runs(repo, _opts = {})
|
170
216
|
{
|
171
217
|
total_count: 2,
|
@@ -176,6 +222,15 @@ class Fbe::FakeOctokit
|
|
176
222
|
}
|
177
223
|
end
|
178
224
|
|
225
|
+
# Gets usage information for a specific workflow run.
|
226
|
+
#
|
227
|
+
# @param [String] _repo The repository name
|
228
|
+
# @param [Integer] _id The workflow run ID
|
229
|
+
# @return [Hash] Billing and usage information for the workflow run
|
230
|
+
# @example
|
231
|
+
# fake_client = Fbe::FakeOctokit.new
|
232
|
+
# usage = fake_client.workflow_run_usage('octocat/Hello-World', 42)
|
233
|
+
# usage[:run_duration_ms] #=> 53000
|
179
234
|
def workflow_run_usage(_repo, _id)
|
180
235
|
{
|
181
236
|
billable: {
|
@@ -216,6 +271,7 @@ class Fbe::FakeOctokit
|
|
216
271
|
end
|
217
272
|
|
218
273
|
def repository(name)
|
274
|
+
raise Octokit::NotFound if [404_123, 404_124].include?(name)
|
219
275
|
{
|
220
276
|
id: name_to_number(name),
|
221
277
|
full_name: name.is_a?(Integer) ? 'yegor256/test' : name,
|
data/lib/fbe/pmp.rb
CHANGED
@@ -19,11 +19,27 @@ require_relative 'fb'
|
|
19
19
|
# this method throws an exception. The factbase must contain PMP-related facts.
|
20
20
|
# Most probably, a special judge must fill it up with such a fact.
|
21
21
|
#
|
22
|
+
# The method uses a double nested `others` block to create a chainable interface
|
23
|
+
# that allows accessing configuration like:
|
24
|
+
#
|
25
|
+
# Fbe.pmp.hr.reward_points
|
26
|
+
# Fbe.pmp.cost.hourly_rate
|
27
|
+
# Fbe.pmp.time.deadline
|
28
|
+
#
|
22
29
|
# @param [Factbase] fb The factbase
|
23
30
|
# @param [Hash] global The hash for global caching
|
24
31
|
# @param [Judges::Options] options The options coming from the +judges+ tool
|
25
32
|
# @param [Loog] loog The logging facility
|
26
|
-
# @return [
|
33
|
+
# @return [Object] A proxy object that allows method chaining to access PMP properties
|
34
|
+
# @example
|
35
|
+
# # Get HR reward points from PMP configuration
|
36
|
+
# points = Fbe.pmp.hr.reward_points
|
37
|
+
#
|
38
|
+
# # Get hourly rate from cost area
|
39
|
+
# rate = Fbe.pmp.cost.hourly_rate
|
40
|
+
#
|
41
|
+
# # Get deadline from time area
|
42
|
+
# deadline = Fbe.pmp.time.deadline
|
27
43
|
def Fbe.pmp(fb: Fbe.fb, global: $global, options: $options, loog: $loog)
|
28
44
|
others do |*args1|
|
29
45
|
area = args1.first
|
data/lib/fbe.rb
CHANGED
data/test/fbe/test_bylaws.rb
CHANGED
@@ -31,9 +31,9 @@ class TestBylaws < Fbe::Test
|
|
31
31
|
{ hoc: 30_000, contributors: 1 } => 32
|
32
32
|
},
|
33
33
|
'resolved-bug-was-rewarded' => {
|
34
|
-
{ hours: 1, self: 0 } =>
|
35
|
-
{ hours: 48, self: 0 } =>
|
36
|
-
{ hours: 80, self: 0 } =>
|
34
|
+
{ hours: 1, self: 0 } => 12,
|
35
|
+
{ hours: 48, self: 0 } => 6,
|
36
|
+
{ hours: 80, self: 0 } => 5,
|
37
37
|
{ hours: 300, self: 0 } => 4,
|
38
38
|
{ hours: 3_000, self: 0 } => 4,
|
39
39
|
{ hours: 30_000, self: 0 } => 4,
|
@@ -45,9 +45,10 @@ class TestBylaws < Fbe::Test
|
|
45
45
|
'code-review-was-rewarded' => {
|
46
46
|
{ hoc: 0, comments: 0, self: 0 } => 4,
|
47
47
|
{ hoc: 3, comments: 0, self: 0 } => 4,
|
48
|
-
{ hoc: 78, comments: 7, self: 0 } =>
|
49
|
-
{ hoc:
|
50
|
-
{ hoc:
|
48
|
+
{ hoc: 78, comments: 7, self: 0 } => 12,
|
49
|
+
{ hoc: 120, comments: 4, self: 0 } => 4,
|
50
|
+
{ hoc: 600, comments: 1, self: 0 } => 8,
|
51
|
+
{ hoc: 500, comments: 40, self: 0 } => 24,
|
51
52
|
{ hoc: 5_000, comments: 100, self: 0 } => 24,
|
52
53
|
{ hoc: 100, comments: 50, self: 1 } => 4,
|
53
54
|
{ hoc: 10_000, comments: 200, self: 1 } => 4
|
@@ -59,19 +60,19 @@ class TestBylaws < Fbe::Test
|
|
59
60
|
{ hoc: 78, comments: 1, reviews: 0 } => 4,
|
60
61
|
{ hoc: 50, comments: 15, reviews: 0 } => 4,
|
61
62
|
{ hoc: 50, comments: 25, reviews: 0 } => 4,
|
62
|
-
{ hoc: 180, comments: 7, reviews: 2 } =>
|
63
|
-
{ hoc: 199, comments: 8, reviews: 3 } =>
|
64
|
-
{ hoc: 150, comments: 5, reviews: 1 } =>
|
63
|
+
{ hoc: 180, comments: 7, reviews: 2 } => 24,
|
64
|
+
{ hoc: 199, comments: 8, reviews: 3 } => 24,
|
65
|
+
{ hoc: 150, comments: 5, reviews: 1 } => 24,
|
65
66
|
{ hoc: 500, comments: 25, reviews: 2 } => 4,
|
66
|
-
{ hoc: 99, comments: 6, reviews: 1 } =>
|
67
|
+
{ hoc: 99, comments: 6, reviews: 1 } => 16,
|
67
68
|
{ hoc: 1_500, comments: 3, reviews: 0 } => 4,
|
68
69
|
{ hoc: 15_000, comments: 40, reviews: 0 } => 4
|
69
70
|
},
|
70
71
|
'bug-report-was-rewarded' => {
|
71
|
-
{} =>
|
72
|
+
{} => 12
|
72
73
|
},
|
73
74
|
'enhancement-suggestion-was-rewarded' => {
|
74
|
-
{} =>
|
75
|
+
{} => 12
|
75
76
|
},
|
76
77
|
'dud-was-punished' => {
|
77
78
|
{} => -16
|
data/test/fbe/test_octo.rb
CHANGED
@@ -132,6 +132,18 @@ class TestOcto < Fbe::Test
|
|
132
132
|
assert(o.off_quota)
|
133
133
|
end
|
134
134
|
|
135
|
+
def test_print_quota_left_while_initialize
|
136
|
+
WebMock.disable_net_connect!
|
137
|
+
stub_request(:get, 'https://api.github.com/rate_limit').to_return(
|
138
|
+
body: '{}', headers: { 'X-RateLimit-Remaining' => '1234' }
|
139
|
+
)
|
140
|
+
buf = Loog::Buffer.new
|
141
|
+
o = Fbe.octo(loog: buf, global: {}, options: Judges::Options.new({ 'github_token' => 'secret_github_token' }))
|
142
|
+
assert_match(/Accessing GitHub API with a token \(19 chars, ending by "oken", 1234 quota remaining\)/, buf.to_s)
|
143
|
+
assert_nil(o.last_response, 'Not to be requests until initialize main Octokit client, ' \
|
144
|
+
'because middleware cached after first request and not apply after')
|
145
|
+
end
|
146
|
+
|
135
147
|
def test_retrying
|
136
148
|
WebMock.disable_net_connect!
|
137
149
|
global = {}
|
@@ -250,4 +262,16 @@ class TestOcto < Fbe::Test
|
|
250
262
|
o = Fbe.octo(loog: Loog::VERBOSE, global: {}, options: Judges::Options.new({ 'github_api_pause' => 0.01 }))
|
251
263
|
refute_nil(o.off_quota)
|
252
264
|
end
|
265
|
+
|
266
|
+
def test_fetches_fake_not_found_users
|
267
|
+
o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
|
268
|
+
assert_raises(Octokit::NotFound) { o.user(404_001) }
|
269
|
+
assert_raises(Octokit::NotFound) { o.user(404_002) }
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_fetches_fake_not_found_repos
|
273
|
+
o = Fbe.octo(loog: Loog::NULL, global: {}, options: Judges::Options.new({ 'testing' => true }))
|
274
|
+
assert_raises(Octokit::NotFound) { o.repository(404_123) }
|
275
|
+
assert_raises(Octokit::NotFound) { o.repository(404_124) }
|
276
|
+
end
|
253
277
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fbe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: backtrace
|
@@ -253,8 +253,8 @@ email: yegor256@gmail.com
|
|
253
253
|
executables: []
|
254
254
|
extensions: []
|
255
255
|
extra_rdoc_files:
|
256
|
-
- README.md
|
257
256
|
- LICENSE.txt
|
257
|
+
- README.md
|
258
258
|
files:
|
259
259
|
- ".0pdd.yml"
|
260
260
|
- ".gitattributes"
|
@@ -358,7 +358,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
358
358
|
- !ruby/object:Gem::Version
|
359
359
|
version: '0'
|
360
360
|
requirements: []
|
361
|
-
rubygems_version: 3.6.
|
361
|
+
rubygems_version: 3.6.7
|
362
362
|
specification_version: 4
|
363
363
|
summary: FactBase Extended (FBE), a collection of utility classes for Zerocracy judges
|
364
364
|
test_files: []
|