Class: PlanMyStuff::BaseProjectItem
- Inherits:
-
ApplicationRecord
- Object
- ApplicationRecord
- PlanMyStuff::BaseProjectItem
- Defined in:
- lib/plan_my_stuff/base_project_item.rb
Overview
Shared base for GitHub Projects V2 item wrappers. Holds attribute definitions, generic build/create/move/update/delete/assign machinery, and core instance helpers.
Not meant to be used directly. Concrete subclasses (ProjectItem, TestingProjectItem) add their own domain-specific methods.
Class methods:
BaseProjectItem.create!(issue) -- add existing issue
BaseProjectItem.create!(title, draft: true, body: "...") -- create draft item
BaseProjectItem.move_item!(item_id:, status:)
BaseProjectItem.assign!(item_id:, assignee:)
Instance methods:
item.move_to!("Done")
item.assign!("octocat")
Direct Known Subclasses
Instance Attribute Summary
Attributes inherited from ApplicationRecord
Class Method Summary collapse
-
.assign!(number:, content_node_id:, assignees:, draft: false, repo: nil) ⇒ void
Assigns users to a project item.
-
.build(item_hash, project:) ⇒ PlanMyStuff::BaseProjectItem
Builds a persisted item from parsed item data.
-
.create!(issue_or_title, draft: false, body: nil, project_number: nil, user: nil) ⇒ PlanMyStuff::BaseProjectItem
Creates a project item by adding an existing issue or creating a draft.
-
.delete_item!(item_id:, project_number: nil) ⇒ String
Deletes a project item from its parent project.
-
.move_item!(item_id:, status:, project_number: nil) ⇒ Hash
Moves a project item to a new status column.
-
.update_date_field!(item_id:, field_name:, date:, project_number: nil) ⇒ Hash
Updates a date custom field on a project item.
-
.update_field!(item_id:, field_name:, value:, project_number: nil) ⇒ Hash
Updates a text custom field on a project item.
-
.update_single_select_field!(item_id:, field_name:, value:, project_number: nil) ⇒ Hash
Updates a single-select custom field on a project item.
Instance Method Summary collapse
-
#as_json(options = {}) ⇒ Hash
Serializes the project item to a JSON-safe hash, excluding the back-reference to the parent project to prevent recursive serialization cycles.
-
#assign!(assignees, user: nil) ⇒ void
Assigns users to this item on its parent project.
-
#assignees ⇒ Array<String>
GitHub logins assigned to the underlying Issue, PullRequest, or DraftIssue.
-
#body ⇒ String?
User-visible body (metadata comment stripped).
-
#content_node_id ⇒ String?
Node ID of the underlying content (Issue, PR, or DraftIssue).
-
#destroy!(user: nil) ⇒ String?
Deletes this item from its parent project.
- #draft? ⇒ Boolean
- #field_values ⇒ Hash
-
#id ⇒ String?
GitHub node ID (e.g. “PVTI_…”).
- #issue ⇒ PlanMyStuff::Issue?
- #issue=(val) ⇒ void
-
#metadata ⇒ PlanMyStuff::ProjectItemMetadata
Parsed metadata.
-
#move_to!(status, user: nil) ⇒ Hash
Moves this item to a new status column on its parent project.
- #number ⇒ Integer?
- #project ⇒ PlanMyStuff::BaseProject?
-
#raw_body ⇒ String?
Full body as stored on GitHub (draft items only).
- #repo ⇒ PlanMyStuff::Repo?
- #state ⇒ String?
- #status ⇒ String?
- #title ⇒ String?
-
#type ⇒ String?
GitHub item type (e.g. “DRAFT_ISSUE”, “ISSUE”, “PULL_REQUEST”).
-
#update_field!(field_name, value) ⇒ Hash
Updates a text custom field on this item.
-
#update_single_select_field!(field_name, value) ⇒ Hash
Updates a single-select custom field on this item.
-
#updated_at ⇒ Time?
GitHub’s updatedAt timestamp.
- #url ⇒ String?
Methods inherited from ApplicationRecord
#destroyed?, #initialize, #new_record?, #persisted?, read_field
Constructor Details
This class inherits a constructor from PlanMyStuff::ApplicationRecord
Class Method Details
.assign!(number:, content_node_id:, assignees:, draft: false, repo: nil) ⇒ void
This method returns an undefined value.
Assigns users to a project item. Issues/PRs use REST via Issue.update!, drafts use GraphQL.
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 259 def assign!(number:, content_node_id:, assignees:, draft: false, repo: nil) if draft client = PlanMyStuff.client user_ids = assignees.map do |assignee| user_data = client.graphql( PlanMyStuff::GraphQL::Queries::USER_NODE_ID, variables: { login: assignee }, ) user_id = user_data.dig(:user, :id) raise(PlanMyStuff::APIError, "GitHub user not found: #{assignee}") if user_id.nil? user_id end client.graphql( PlanMyStuff::GraphQL::Queries::ASSIGN_DRAFT, variables: { draftIssueId: content_node_id, assigneeIds: user_ids }, ) else PlanMyStuff::Issue.update!(number: number, repo: repo, assignees: assignees) end end |
.build(item_hash, project:) ⇒ PlanMyStuff::BaseProjectItem
Builds a persisted item from parsed item data.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 64 def build(item_hash, project:) raw_body_val = item_hash[:body] item = new( id: item_hash[:id], type: item_hash[:type], content_node_id: item_hash[:content_node_id], title: item_hash[:title], raw_body: raw_body_val, number: item_hash[:number], url: item_hash[:url], repo: item_hash[:repo], state: item_hash[:state], status: item_hash[:status], updated_at: item_hash[:updated_at], field_values: item_hash[:field_values] || {}, assignees: item_hash[:assignees] || [], project: project, ) if raw_body_val parsed = PlanMyStuff::MetadataParser.parse(raw_body_val) item. = PlanMyStuff::ProjectItemMetadata.from_hash(parsed[:metadata] || {}) item.body = parsed[:body] end item.instance_variable_set(:@github_response, item_hash[:github_response]) item.__send__(:persisted!) item end |
.create!(issue_or_title, draft: false, body: nil, project_number: nil, user: nil) ⇒ PlanMyStuff::BaseProjectItem
Creates a project item by adding an existing issue or creating a draft.
104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 104 def create!(issue_or_title, draft: false, body: nil, project_number: nil, user: nil) item = if draft add_draft_item!(title: issue_or_title, body: body, project_number: project_number) else add_item!(issue: issue_or_title, project_number: project_number) end PlanMyStuff::Notifications.instrument('project_item_added', item, user: user) item end |
.delete_item!(item_id:, project_number: nil) ⇒ String
Deletes a project item from its parent project. Returns the deletedItemId from the GraphQL response on success.
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 231 def delete_item!(item_id:, project_number: nil) project_number = resolve_default_project_number!(project_number) org = PlanMyStuff.configuration.organization project_id = resolve_project_id(org, project_number) data = PlanMyStuff.client.graphql( PlanMyStuff::GraphQL::Queries::DELETE_PROJECT_ITEM, variables: { projectId: project_id, itemId: item_id }, ) deleted_id = data.dig(:deleteProjectV2Item, :deletedItemId) raise(PlanMyStuff::APIError, "Failed to delete project item #{item_id}") if deleted_id.nil? deleted_id end |
.move_item!(item_id:, status:, project_number: nil) ⇒ Hash
Moves a project item to a new status column.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 124 def move_item!(item_id:, status:, project_number: nil) project_number = resolve_default_project_number!(project_number) project = BaseProject.find(project_number) status_field = project.status_field option_id = resolve_status_option_id!(status_field, status) PlanMyStuff.client.graphql( PlanMyStuff::GraphQL::Queries::UPDATE_SINGLE_SELECT_FIELD, variables: { projectId: project.id, itemId: item_id, fieldId: status_field[:id], optionId: option_id, }, ) end |
.update_date_field!(item_id:, field_name:, date:, project_number: nil) ⇒ Hash
Updates a date custom field on a project item.
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 204 def update_date_field!(item_id:, field_name:, date:, project_number: nil) project_number = resolve_default_project_number!(project_number) project = BaseProject.find(project_number) field = resolve_text_field!(project, field_name) PlanMyStuff.client.graphql( PlanMyStuff::GraphQL::Queries::UPDATE_DATE_FIELD, variables: { projectId: project.id, itemId: item_id, fieldId: field[:id], date: date.to_s, }, ) end |
.update_field!(item_id:, field_name:, value:, project_number: nil) ⇒ Hash
Updates a text custom field on a project item.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 178 def update_field!(item_id:, field_name:, value:, project_number: nil) project_number = resolve_default_project_number!(project_number) project = BaseProject.find(project_number) field = resolve_text_field!(project, field_name) PlanMyStuff.client.graphql( PlanMyStuff::GraphQL::Queries::UPDATE_TEXT_FIELD, variables: { projectId: project.id, itemId: item_id, fieldId: field[:id], value: value, }, ) end |
.update_single_select_field!(item_id:, field_name:, value:, project_number: nil) ⇒ Hash
Updates a single-select custom field on a project item.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 151 def update_single_select_field!(item_id:, field_name:, value:, project_number: nil) project_number = resolve_default_project_number!(project_number) project = BaseProject.find(project_number) field = resolve_single_select_field!(project, field_name) option_id = resolve_status_option_id!(field, value) PlanMyStuff.client.graphql( PlanMyStuff::GraphQL::Queries::UPDATE_SINGLE_SELECT_FIELD, variables: { projectId: project.id, itemId: item_id, fieldId: field[:id], optionId: option_id, }, ) end |
Instance Method Details
#as_json(options = {}) ⇒ Hash
Serializes the project item to a JSON-safe hash, excluding the back-reference to the parent project to prevent recursive serialization cycles.
559 560 561 562 563 564 565 566 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 559 def as_json( = {}) merged_except = Array.wrap([:except]) + ['project'] merged_methods = Array.wrap([:methods]) + [:draft?] super(.merge(except: merged_except, methods: merged_methods)).merge( 'project_id' => project&.id, 'project_number' => project&.number, ) end |
#assign!(assignees, user: nil) ⇒ void
This method returns an undefined value.
Assigns users to this item on its parent project. For draft items the underlying call uses the draft-issue assign mutation; for issue-backed items it delegates to Issue.update! with assignees:.
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 535 def assign!(assignees, user: nil) assignee_list = Array.wrap(assignees) self.class.assign!( number: number, content_node_id: content_node_id, assignees: assignee_list, draft: draft?, repo: repo, ) PlanMyStuff::Notifications.instrument( 'project_item_assigned', self, user: user, assignees: assignee_list, ) end |
#assignees ⇒ Array<String>
Returns GitHub logins assigned to the underlying Issue, PullRequest, or DraftIssue.
50 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 50 attribute :assignees, default: -> { [] } |
#body ⇒ String?
Returns user-visible body (metadata comment stripped).
32 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 32 attribute :body, :string |
#content_node_id ⇒ String?
Returns node ID of the underlying content (Issue, PR, or DraftIssue).
24 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 24 attribute :content_node_id, :string |
#destroy!(user: nil) ⇒ String?
Deletes this item from its parent project. Marks the in-memory instance as destroyed so destroyed? returns true and persisted? returns false.
No-op if the instance is already destroyed.
515 516 517 518 519 520 521 522 523 524 525 526 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 515 def destroy!(user: nil) return if destroyed? deleted_id = self.class.delete_item!( project_number: project.number, item_id: id, ) PlanMyStuff::Notifications.instrument('project_item_removed', self, user: user) destroyed! deleted_id end |
#draft? ⇒ Boolean
569 570 571 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 569 def draft? type == 'DRAFT_ISSUE' end |
#field_values ⇒ Hash
48 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 48 attribute :field_values, default: -> { {} } |
#id ⇒ String?
Returns GitHub node ID (e.g. “PVTI_…”).
22 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 22 attribute :id, :string |
#issue ⇒ PlanMyStuff::Issue?
54 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 54 attribute :issue |
#issue=(val) ⇒ void
This method returns an undefined value.
574 575 576 577 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 574 def issue=(val) @issue_assigned = true super end |
#metadata ⇒ PlanMyStuff::ProjectItemMetadata
Returns parsed metadata.
34 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 34 attribute :metadata, default: -> { PlanMyStuff::ProjectItemMetadata.new } |
#move_to!(status, user: nil) ⇒ Hash
Moves this item to a new status column on its parent project.
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 457 def move_to!(status, user: nil) previous_status = self.status result = self.class.move_item!( project_number: project.number, item_id: id, status: status, ) PlanMyStuff::Notifications.instrument( 'project_item_status_changed', self, user: user, status: status, previous_status: previous_status, ) result end |
#number ⇒ Integer?
36 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 36 attribute :number, :integer |
#project ⇒ PlanMyStuff::BaseProject?
52 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 52 attribute :project |
#raw_body ⇒ String?
Returns full body as stored on GitHub (draft items only).
30 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 30 attribute :raw_body, :string |
#repo ⇒ PlanMyStuff::Repo?
40 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 40 attribute :repo |
#state ⇒ String?
42 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 42 attribute :state, :string |
#status ⇒ String?
44 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 44 attribute :status, :string |
#title ⇒ String?
28 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 28 attribute :title, :string |
#type ⇒ String?
Returns GitHub item type (e.g. “DRAFT_ISSUE”, “ISSUE”, “PULL_REQUEST”).
26 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 26 attribute :type, :string |
#update_field!(field_name, value) ⇒ Hash
Updates a text custom field on this item.
483 484 485 486 487 488 489 490 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 483 def update_field!(field_name, value) self.class.update_field!( project_number: project.number, item_id: id, field_name: field_name, value: value, ) end |
#update_single_select_field!(field_name, value) ⇒ Hash
Updates a single-select custom field on this item.
499 500 501 502 503 504 505 506 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 499 def update_single_select_field!(field_name, value) self.class.update_single_select_field!( project_number: project.number, item_id: id, field_name: field_name, value: value, ) end |
#updated_at ⇒ Time?
Returns GitHub’s updatedAt timestamp.
46 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 46 attribute :updated_at, :datetime |
#url ⇒ String?
38 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 38 attribute :url, :string |