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.
-
#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.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 255 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(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 Issue.update!(number: number, repo: repo, assignees: assignees) end end |
.build(item_hash, project:) ⇒ PlanMyStuff::BaseProjectItem
Builds a persisted item from parsed item data.
63 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 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 63 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], field_values: item_hash[:field_values] || {}, 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.
101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 101 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.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 228 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.
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 121 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.
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 201 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.
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 175 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.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 148 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.
551 552 553 554 555 556 557 558 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 551 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.
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 527 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 |
#body ⇒ String?
Returns user-visible body (metadata comment stripped).
33 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 33 attribute :body, :string |
#content_node_id ⇒ String?
Returns node ID of the underlying content (Issue, PR, or DraftIssue).
25 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 25 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.
508 509 510 511 512 513 514 515 516 517 518 519 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 508 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
561 562 563 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 561 def draft? type == 'DRAFT_ISSUE' end |
#field_values ⇒ Hash
49 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 49 attribute :field_values, default: -> { {} } |
#id ⇒ String?
Returns GitHub node ID (e.g. “PVTI_…”).
23 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 23 attribute :id, :string |
#issue ⇒ PlanMyStuff::Issue?
53 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 53 attribute :issue |
#issue=(val) ⇒ void
This method returns an undefined value.
566 567 568 569 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 566 def issue=(val) @issue_assigned = true super end |
#metadata ⇒ PlanMyStuff::ProjectItemMetadata
Returns parsed metadata.
35 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 35 attribute :metadata, default: -> { PlanMyStuff::ProjectItemMetadata.new } |
#move_to!(status, user: nil) ⇒ Hash
Moves this item to a new status column on its parent project.
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 447 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?
37 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 37 attribute :number, :integer |
#project ⇒ PlanMyStuff::BaseProject?
51 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 51 attribute :project |
#raw_body ⇒ String?
Returns full body as stored on GitHub (draft items only).
31 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 31 attribute :raw_body, :string |
#repo ⇒ PlanMyStuff::Repo?
41 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 41 attribute :repo |
#state ⇒ String?
43 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 43 attribute :state, :string |
#status ⇒ String?
45 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 45 attribute :status, :string |
#title ⇒ String?
29 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 29 attribute :title, :string |
#type ⇒ String?
Returns GitHub item type (e.g. “DRAFT_ISSUE”, “ISSUE”, “PULL_REQUEST”).
27 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 27 attribute :type, :string |
#update_field!(field_name, value) ⇒ Hash
Updates a text custom field on this item.
473 474 475 476 477 478 479 480 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 473 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.
489 490 491 492 493 494 495 496 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 489 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.
47 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 47 attribute :updated_at, :datetime |
#url ⇒ String?
39 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 39 attribute :url, :string |