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(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.
62 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 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 62 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.
100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 100 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.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 227 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.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 120 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.
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 200 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.
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 174 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.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 147 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.
555 556 557 558 559 560 561 562 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 555 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:.
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 531 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).
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.
511 512 513 514 515 516 517 518 519 520 521 522 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 511 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
565 566 567 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 565 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?
52 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 52 attribute :issue |
#issue=(val) ⇒ void
This method returns an undefined value.
570 571 572 573 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 570 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.
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 453 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?
50 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 50 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.
479 480 481 482 483 484 485 486 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 479 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.
495 496 497 498 499 500 501 502 |
# File 'lib/plan_my_stuff/base_project_item.rb', line 495 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 |