Class: Carson::Waybill

Inherits:
Object
  • Object
show all
Defined in:
lib/carson/waybill.rb

Overview

The shipping document filed with the bureau (GitHub PR). Has a tracking number, knows the bureaucrats’ response (cleared/held/ accepted/rejected), and can ask the bureau to accept the parcel into the registry. Uses gh CLI internally — that’s a tool, not the domain.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(label:, warehouse_path:, tracking_number: nil, url: nil, review_gate: nil) ⇒ Waybill

Returns a new instance of Waybill.



25
26
27
28
29
30
31
32
33
# File 'lib/carson/waybill.rb', line 25

def initialize( label:, warehouse_path:, tracking_number: nil, url: nil, review_gate: nil )
	@label = label
	@warehouse_path = warehouse_path
	@tracking_number = tracking_number
	@url = url
	@review_gate = review_gate
	@state = nil
	@ci = nil
end

Instance Attribute Details

#labelObject (readonly)

Returns the value of attribute label.



23
24
25
# File 'lib/carson/waybill.rb', line 23

def label
  @label
end

#tracking_numberObject (readonly)

Returns the value of attribute tracking_number.



23
24
25
# File 'lib/carson/waybill.rb', line 23

def tracking_number
  @tracking_number
end

#urlObject (readonly)

Returns the value of attribute url.



23
24
25
# File 'lib/carson/waybill.rb', line 23

def url
  @url
end

Instance Method Details

#accept!(method:) ⇒ Object

Ask the bureau to accept the parcel into the registry. Updates own state after the attempt.



149
150
151
152
153
# File 'lib/carson/waybill.rb', line 149

def accept!( method: )
	gh( "pr", "merge", tracking_number.to_s, "--#{method}" )
	refresh!
	self
end

#accepted?Boolean

Has the bureau accepted the parcel into the registry?

Returns:

  • (Boolean)


82
83
84
# File 'lib/carson/waybill.rb', line 82

def accepted?
	@state&.dig( "state" ) == "MERGED"
end

#cleared?Boolean

Has the bureau cleared the parcel for delivery? All bureaucrats pass, no merge blocks, merge state is clean.

Returns:

  • (Boolean)


98
99
100
101
102
103
104
105
106
# File 'lib/carson/waybill.rb', line 98

def cleared?
	return false unless filed?
	return false if draft?
	return false unless @ci == :pass
	return false if merge_conflicting? || merge_behind? || merge_policy_blocked?
	merge_status = @state&.dig( "mergeStateStatus" ).to_s.upcase
	mergeable = @state&.dig( "mergeable" ).to_s.upcase
	merge_status == "CLEAN" || mergeable == "MERGEABLE"
end

#default_titleObject

Generate a human-readable title from the label.



66
67
68
69
70
# File 'lib/carson/waybill.rb', line 66

def default_title
	label.tr( "-", " " ).gsub( "/", ": " ).sub( /\A\w/ ) do |character|
		character.upcase
	end
end

#draft?Boolean

Is the waybill still a draft?

Returns:

  • (Boolean)


92
93
94
# File 'lib/carson/waybill.rb', line 92

def draft?
	@state&.dig( "isDraft" ) || false
end

#file!(title: nil, body_file: nil) ⇒ Object

File the waybill with the bureau. Creates a PR on GitHub.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/carson/waybill.rb', line 43

def file!( title: nil, body_file: nil )
	filing_title = title || default_title
	arguments = [ "pr", "create", "--title", filing_title, "--head", label ]

	if body_file && File.exist?( body_file )
		arguments.push( "--body-file", body_file )
	else
		arguments.push( "--body", "" )
	end

	stdout, stderr, success, = gh( *arguments )
	if success
		@url = stdout.to_s.strip
		@tracking_number = @url.split( "/" ).last.to_i
		@tracking_number = nil if @tracking_number == 0
	end

	# If create failed or returned no number, try to find existing.
	find_existing! unless filed?
	self
end

#filed?Boolean

Has the waybill been filed with the bureau?

Returns:

  • (Boolean)


38
39
40
# File 'lib/carson/waybill.rb', line 38

def filed?
	!tracking_number.nil?
end

#held?Boolean

Is something blocking this waybill?

Returns:

  • (Boolean)


109
110
111
112
# File 'lib/carson/waybill.rb', line 109

def held?
	return false if cleared? || accepted? || rejected?
	filed?
end

#hold_reasonObject

Why is the waybill being held?



115
116
117
118
119
120
121
122
123
124
# File 'lib/carson/waybill.rb', line 115

def hold_reason
	return "draft" if draft?
	return "pending_at_registry" if @ci == :pending
	return "failed_at_registry" if @ci == :fail
	return "error_at_registry" if @ci == :error
	return "merge_conflict" if merge_conflicting?
	return "behind_registry" if merge_behind?
	return "policy_block" if merge_policy_blocked?
	"mergeability_pending"
end

#hold_summaryObject

Human-readable explanation of why the waybill is held.



127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/carson/waybill.rb', line 127

def hold_summary
	case hold_reason
	when "draft" then "waybill is still a draft"
	when "pending_at_registry" then "waiting for bureaucrats to check"
	when "failed_at_registry" then "bureaucrats rejected the parcel"
	when "error_at_registry" then "unable to reach the bureaucrats"
	when "merge_conflict" then "parcel has conflicts with registry"
	when "behind_registry" then "parcel is behind the registry"
	when "policy_block" then "blocked by bureau policy"
	else "waiting for bureau assessment"
	end
end

#mergeability_pending?Boolean

Is the hold specifically because mergeability is still pending?

Returns:

  • (Boolean)


141
142
143
# File 'lib/carson/waybill.rb', line 141

def mergeability_pending?
	hold_reason == "mergeability_pending"
end

#refresh!Object

Check with the bureau for the latest on this waybill.



75
76
77
78
79
# File 'lib/carson/waybill.rb', line 75

def refresh!
	@state = fetch_state
	@ci = fetch_ci
	self
end

#rejected?Boolean

Has the bureau rejected the waybill (closed without merge)?

Returns:

  • (Boolean)


87
88
89
# File 'lib/carson/waybill.rb', line 87

def rejected?
	@state&.dig( "state" ) == "CLOSED"
end

#stub_bureau_response(state: nil, ci: nil) ⇒ Object

Stub the bureau’s response for testing without gh CLI.



171
172
173
174
# File 'lib/carson/waybill.rb', line 171

def stub_bureau_response( state: nil, ci: nil )
	@state = state if state
	@ci = ci if ci
end

#to_observationObject

Returns a hash of the bureau’s current state for tracking records.



158
159
160
161
162
163
164
165
166
# File 'lib/carson/waybill.rb', line 158

def to_observation
	return {} unless @state.is_a?( Hash )

	{
		pull_request_state: @state[ "state" ],
		pull_request_draft: @state[ "isDraft" ],
		pull_request_merged_at: @state[ "mergedAt" ]
	}
end