For reasons that might or might not affect production installations, the
test checking simultaneous requests to create poll voters in the
officing voters controller wasn't behaving as expected.
The expected behavior, since commit 9a8bfac5b, is that the second
request reaching the `with_lock` part of the code waits for the first
request to finish and so this second request raises an
`ActiveRecord::RecordInvalid` exception when trying to save a voter with
the same poll and the same user as the first one.
However, 95% of the time that wasn't the case. Instead, when entering
the `@user.with_lock` block, the second request would replace its
`@voter` object with the `@voter` object saved in the same request, so
the second call to `save!` would succeed as it would simply update the
existing record.
This is a behavior that we could accept if it were consistent and
happened 100% of the time, but that isn't the case. 5% of the time, we
do get the `ActiveRecord::RecordInvalid` exception. So 5% of the time we
got a failure in the test:
```
1) Officing::VotersController POST create does not create two records
with two simultaneous requests
Failure/Error: @user.with_lock { @voter.save! }
ActiveRecord::RecordInvalid:
Validation failed: User User has already voted
# ./app/controllers/officing/voters_controller.rb:25:in `block in create'
# ./app/controllers/officing/voters_controller.rb:25:in `create'
# ./app/controllers/application_controller.rb:50:in `switch_locale'
# ./spec/controllers/officing/voters_controller_spec.rb:15:in `block (5 levels) in <top (required)>'
```
So we're changing the `with_lock` block so it includes the
initialization of the object. This way, we get the
`ActiveRecord::RecordInvalid` exception 100% of the time.
Note that in commit 9a8bfac5b we also rescued the
`ActionDispatch::IllegalStateError` exceptions. I'm not why we were
getting those exceptions when running the tests, and I'm not sure
whether we keep getting after these changes, but it doesn't really
matter. The reason is that in Consul Democracy 2.3.0 we're going to add
a unique index to the `poll_voters` table, which (according to the tests
done in the past) will make both the `@user.lock` block and rescuing the
`ActionDispatch::IllegalStateError` unnecessary.
So, in other words, these changes will never make it to production
because this part of the code will be changed again before releasing
version 2.3.0.
Note that, when taking votes from an erased user, since poll answers
don't belong to poll voters, we were not migrating them in the
`take_votes_from` method (and we aren't migrating them now either).
Having a class named `Poll::Question::Answer` and another class named
`Poll::Answer` was so confusing that no developer working on the project
has ever been capable of remembering which is which for more than a few
seconds.
Furthermore, we're planning to add open answers to polls, and we might
add a reference from the `poll_answers` table to the
`poll_question_answers` table to property differentiate between open
answers and closed answers. Having yet another thing named answer would
be more than what our brains can handle (we know it because we did this
once in a prototype).
So we're renaming `Poll::Question::Answer` to `Poll::Question::Option`.
Hopefully that'll make it easier to remember. The name is also (more or
less) consistent with the `Legislation::QuestionOption` class, which is
similar.
We aren't changing the table or columns names for now in order to avoid
possible issues when upgrading (old code running with the new database
tables/columns after running the migrations but before deployment has
finished, for instance). We might do it in the future.
I've tried not to change the internationalization keys either so
existing translations would still be valid. However, since we have to
change the keys in `activerecord.yml` so methods like
`human_attribute_name` keep working, I'm also changing them in places
where similar keys were used (like `poll_question_answer` or
`poll/question/answer`).
Note that it isn't clear whether we should use `option` or
`question_option` in some cases. In order to keep things simple, we're
using `option` where we were using `answer` and `question_option` where
we were using `question_answer`.
Also note we're adding tests for the admin menu component, since at
first I forgot to change the `answers` reference there and all tests
passed.
This rule was added in rubocop 1.44.0. It's useful to avoid accidental
`unless !condition` clauses.
Note we aren't replacing `unless zero?` with `if nonzero?` because we
never use `nonzero?`; using it sounds like `if !zero?`.
Replacing `unless any?` with `if none?` is only consistent if we also replace
`unless present?` with `if blank?`, so we're also adding this case. For
consistency, we're also replacing `unless blank?` with `if present?`.
We're also simplifying code dealing with `> 0` conditions in order to
make the code (hopefully) easier to understand.
Also for consistency, we're enabling the `Style/InverseMethods` rule,
which follows a similar idea.
Note we're excluding a few files:
* Configuration files that weren't generated by us
* Migration files that weren't generated by us
* The Gemfile, since it includes an important comment that must be on
the same line as the gem declaration
* The Budget::Stats class, since the heading statistics are a mess and
having shorter lines would require a lot of refactoring
For the HashAlignment rule, we're using the default `key` style (keys
are aligned and values aren't) instead of the `table` style (both keys
and values are aligned) because, even if we used both in the
application, we used the `key` style a lot more. Furthermore, the
`table` style looks strange in places where there are both very long and
very short keys and sometimes we weren't even consistent with the
`table` style, aligning some keys without aligning other keys.
Ideally we could align hashes to "either key or table", so developers
can decide whether keeping the symmetry of the code is worth it in a
case-per-case basis, but Rubocop doesn't allow this option.
Since IRB has improved its support for multiline, the main argument
towars using a trailing dot no longer affects most people.
It still affects me, though, since I use Pry :), but I agree
leading dots are more readable, so I'm enabling the rule anyway.
When customizing CONSUL, one of the most common actions is adding a new
field to a form.
This requires modifying the permitted/allowed parameters. However, in
most cases, the method returning these parameters returned an instance
of `ActionController::Parameters`, so adding more parameters to it
wasn't easy.
So customizing the code required copying the method returning those
parameters and adding the new ones. For example:
```
def something_params
params.require(:something).permit(
:one_consul_attribute,
:another_consul_attribute,
:my_custom_attribute
)
end
```
This meant that, if the `something_params` method changed in CONSUL, the
customization of this method had to be updated as well.
So we're extracting the logic returning the parameters to a method which
returns an array. Now this code can be customized without copying the
original method:
```
alias_method :consul_allowed_params, :allowed_params
def allowed_params
consul_allowed_params + [:my_custom_attribute]
end
```
We were inconsistent on this one. I consider it particularly useful when
a method starts with a `return` statement.
In other cases, we probably shouldn't have a guard rule in the middle of
a method in any case, but that's a different refactoring.
We were very inconsistent regarding these rules.
Personally I prefer no empty lines around blocks, clases, etc... as
recommended by the Ruby style guide [1], and they're the default values
in rubocop, so those are the settings I'm applying.
The exception is the `private` access modifier, since we were leaving
empty lines around it most of the time. That's the default rubocop rule
as well. Personally I don't have a strong preference about this one.
[1] https://rubystyle.guide/#empty-lines-around-bodies
- Add :date_of_birth and :postal_code
- Only display new fields when aplication has configured the
custom census API and contains alias values for fields. Add 2
class Setting methods to check this feature:
- force_presence_date_of_birth?
- force_presence_postal_code?
Polls that were not votable by a user were not being displayed in the officing interface. Creating a confusing situation for officers.
With this commit polls that are not votable by a user will be displayed, with the corresponding message explaining that that poll can only be voted by residents of a certain geozone.
This way we can easily add a test which will fail if by accident we
change the method to use `Date.today`. Until now using `Date.today`
would only fail if we ran specs in a time zone with a different date.