• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MushroomObserver / mushroom-observer / 26632367492 / 1
97%
main: 97%

Build:
DEFAULT BRANCH: main
Ran 29 May 2026 10:38AM UTC
Files 1036
Run time 35s
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

29 May 2026 10:33AM UTC coverage: 96.508%. Remained the same
26632367492.1

push

github

web-flow
SpeciesList: sync_projects on the model; controller becomes a thin caller (#4390)

Moves the per-project add/remove logic from
`SpeciesListsController#update_projects` (+ its `change_project_species_lists`
helper) into a new `SpeciesList#sync_projects(submitted_project_ids,
user:)` method. The controller stays in charge of `flash_notice` (which
the model has no business doing) but the relationship logic — what's
"desired" vs. "current," which projects are even toggleable for the
given user, mutating the join through `Project#add_species_list` /
`#remove_species_list` — now lives on the model where it can be
unit-tested directly.

```ruby
# In SpeciesList:
def sync_projects(submitted_project_ids, user:)
  # Returns [[project, :added | :removed], …] for the caller to flash.
end

# In SpeciesListsController:
def update_projects(spl, submitted_ids)
  changes = spl.sync_projects(submitted_ids, user: @user)
  changes.each { |project, change| flash_project_change(project, change) }
  return if changes.empty?

  flash_notice(:species_list_show_manage_observations_too.t)
end
```

`change_project_species_lists` (the per-change flash + mutation helper)
deletes; the per-change flash collapses into a tiny
`flash_project_change(project, change)`.

Net diff: controller -32 lines, model +34 lines, tests +70 lines.

## Why

Two reasons.

1. Pulling the relationship logic off the controller stops the
   controller from quietly growing every time we need to nudge how
   project sync works. The controller was already at
   `# rubocop:disable Metrics/ClassLength` after #4389; this trims a
   bit and pushes future changes to the right place.
2. Testing the sync semantics from the controller required hitting
   the full HTTP flow (login, params, flash assertions). On the model
   it's straight-up `list.sync_projects([id.to_s], user: user)`
   followed by `assert_includes(list.reload.projects, project)`. The
   5 new model tests cover: nil submission (no-op),
   empty-submis... (continued)

39521 of 40951 relevant lines covered (96.51%)

671.36 hits per line

Source Files on job 26632367492.1
  • Tree
  • List 1036
  • Changed 2
  • Source Changed 2
  • Coverage Changed 2
Coverage ∆ File Lines Relevant Covered Missed Hits/Line
  • Back to Build 26632367492
  • 92191aff on github
  • Prev Job for on main (#26631317870.1)
  • Next Job for on main (#26632455807.1)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc