and its relation with the SDG goal model.
Add comparable module be able to sort collections of targets
by code attribute.
Co-Authored-By: Javi Martín <35156+javierm@users.noreply.github.com>
Similar to what we do with settings, only for settings we return the
value of the setting (which is what we're going to need most of the
time), and here we return the object.
Since data for this model (title and description) is not generated in
CONSUL but by the United Nations, we aren't storing it in the database
but in our YAML translation files.
The reasoning is as follows. Suppose that, a few months after CONSUL
gets SDG support, a new language is added to CONSUL.
With YAML files, getting the texts in the new language would mean
updating CONSUL to include the new language.
But if we store these texts in the database, it means we have to update
the databases of all existing CONSUL installations, either each
installation by themselves (duplicating efforts) or running a rake task
(which we would have to write each time).
So we believe using translations works better in this case.
We're still storing records in the database with the code, so they can
be easily referenced via `has_many` or `has_many :through` associations.
Ruby 2.6 introduces `Enumerable#filter` as an alias to
`Enumerable#select`, and so our Filterable.filter method will not work
with Ruby 2.6.
So we're renaming the method to `filter_by`, which is similar to
`find_by`. We could also change the `filter` method so if a block is
given it delegates to `Enumerable#filter`, the same way ActiveRecord
handles the `select` method, but IMHO this is easier to follow.
Rails 5.2 is raising a warning in some places:
DEPRECATION WARNING: Dangerous query method (method whose arguments are
used as raw SQL) called with non-attribute argument(s). Non-attribute
arguments will be disallowed in Rails 6.0. This method should not be
called with user-provided values, such as request parameters or model
attributes. Known-safe values can be passed by wrapping them in
Arel.sql().
IMHO this warning is simply wrong, since we're using known PostgreSQL
functions like LOWER() or RANDOM(). AFAIK this code works without warnings
in Rails 6.0 [1][2]
However, since the warning is annoying, we need to take measures so our
logs are clean.
[1] https://github.com/rails/rails/commit/6c82b6c99d
[2] https://github.com/rails/rails/commit/64d8c54e16
It was removed in commit 128a8164 because we hadn't reviewed it nor
tested it properly. We're now adding it again, fixing the issues we've
found while reviewing.
Legislation Processes created through the admin form were getting the default color.
However, Legislation processes created by other means (like the `db:dev_seed` rake task) were not getting these default values.
This feature was originally implemented when we were using Rails 4.
With Rails 5, we can provide default values to all new Legislation processes
and simplify the code at the same time thanks to its `attribute` method.
Related commit:
https://github.com/consul/consul/pull/4080/commits/0b83be6
Banners created through the admin form were getting the default color.
However, banners created by other means (like the `db:dev_seed` rake
task) were not getting these default values.
This feature was originally implemented when we were using Rails 4.
With Rails 5, we can provide default values to all new banners and
simplify the code at the same time thanks to its `attribute` method.
Now, when creating a new banner, instead of getting a blank space, we
get an empty line with the banner's default background color, which most
users won't know what it's about until they fill in the banner's title.
So we're not displaying the content of the banner when it's empty,
thanks to the `:empty` CSS pseudoclass.
When skipping verification, we cannot apply the validation rule saying
the document number and document type must be unique, because they'll be
`nil` in many cases. So we were skipping the rule, but that makes it
possible for the same user to vote several times (for instance, once in
a booth and once via web).
So we're changing the scope of the uniqueness rule: instead of being
unique per document number, voters are unique per user. The reason we
made them unique per document number was that back in commit 900563e3
(when we added the rule), we hadn't added the relation between users and
poll voters yet.
Up until now, we were assuming the voter was valid, but were not raising
an exception if it wasn't. And in the user interface everything seemed
to be working properly.
We were having this issue when skipping verification, when there could
be voters without a document number, which would be considered invalid.
Raising an exception when failing to save the voter and making sure the
answer and the voter are saved inside a transaction solves the problem.
We were setting it to 0, and so screen reader users might be confused by
it.
The easiest way to reuse the code and using it for both this attribute
and the width of the progress bar is to move this method to the voting
style, just like the other methods used in this view.
Note the progressbar ARIA role might not be right, since this isn't a
task which is "progressing", but an indicator of the amount spent and
amount available, which is exactly what the <meter> HTML5 tag was
designed for.
We might use a <meter> tag in the future. For now, I'm leaving it as it
is because I'm not certain about how well <meter> is supported in
accessibility tools, and because it's definitely not supported in
Internet Explorer 11, which we haven't officially dropped support for.
In the Knapsack voting style, we can't add an investment if its cost is
greater than the money we've got left, but in other voting styles money
might not be the issue.
So we're introducing the term "resources" and adapting the code
accordingly.
Since we're going to introduce a new voting style which will not be
based on money, we're extracting the logic specific to the current
voting style to a new class.
This way adding new voting styles will be easier.
We were using the same logic twice.
I've moved the logic to the Ballot model, which to me is a more natural
place to calculate whether there's enough money left than the Investment
model. After all, the remaining money is in the ballot, and not in the
investment.
One method was calling `reason_for_not_being_ballotable_by` passing just
one parameter instead of two.
The other method was calling the method `amount_spent`, which does not
exist in the Budget class.
So both methods would make the application crash if they were called.
Luckily, they aren't, so the application doesn't crash.
When a legislation process is deleted, everything related will be
deleted, including the answers. This `dependent: :destroy` was causing
that users accounts were being accidentally deleted.
Using pg_search 2.0.1 with Rails 5.2 results in deprecation warnings:
DEPRECATION WARNING: Dangerous query method (method whose arguments used
as raw SQL) called with non-attribute argument(s):
"pg_search_978c2f8941354cf552831b.rank DESC, \"tags\".\"id\" ASC".
Non-attribute arguments will be disallowed in Rails 6.0. This method
should not be called with user-provided values, such as request
parameters or model attributes. Known-safe values can be passed by
wrapping them in Arel.sql().
We're not upgrading to the latest pg_search because it only supports
ActiveRecord >= 5.2.
In Ruby 5.2, we get a warning when using the "RANDOM()" function:
DEPRECATION WARNING: Dangerous query method (method whose arguments are
used as raw SQL) called with non-attribute argument(s): "RANDOM()".
Non-attribute arguments will be disallowed in Rails 6.0. This method
should not be called with user-provided values, such as request
parameters or model attributes. Known-safe values can be passed by
wrapping them in Arel.sql().
This warning doesn't make much sense, though, since RANDOM() is a common
function which is not dangerous at all. However, since the warning is
annoying, we'll probably have to find a way to deal with it.
So I'm extracting all our RANDOM() usages into a method. This way we'll
only have to change one method to avoid this warning.
I've chosen `sample` because it's similar to Ruby's Array#sample, and
because `order_by_random` would be confusing if we consider we already
have a method called `sort_by_random`.
With two concurrent requests, it's possible to create two ballot lines
when only one of them should be created.
The reason is the code validating the line is not thread safe:
```
if ballot.amount_available(investment.heading) < investment.price.to_i
errors.add(:money, "insufficient funds")
end
```
If the second request executes this code after the first request has
executed it but before the first request has saved the record to the
database, both records will pass this validation and both will be saved
to the database.
So we need to introduce a lock. Now when the second request tries to
lock the ballot, it finds it's already locked by the first request, and
will wait for the transaction of the first request to finish before
checking whether there are sufficient funds.
Note we need to disable transactions during the test; otherwise the
second thread will wait for the first one to finish.
Also note that we need to update a couple of tests because records are
reloaded when they're locked.
In one case, reloading the ballot causes `ballot.user` to be `nil`,
since the user is hidden. So we hide the user after creating all its
associated records (which is the scenario that would take place in real
life).
In the other case, reloading the ballot causes `ballot.user` to reload
as well. So we need to reload the user object used in the test too so it
gets the updates done on `ballot.user`.
I haven't been able to reproduce this behavior in a system test. The
following test works with Rails 5.0, but it stopped working when we
moved to system tests in commit 9427f014. After that commit, for reasons
I haven't been able to debug (reintroducing truncation with
DatabaseClaner didn't seem to affect this test, and neither did
increasing the number of threads in Puma), the two AJAX requests
executed here are no longer simultaneous; the second request waits for
the first one to finish.
scenario "Race conditions with simultaneous requests", :js do
allow_any_instance_of(Budget::Ballot::Line).to receive(:check_sufficient_funds) do |object|
allow(object).to receive(:check_sufficient_funds).and_call_original
object.check_sufficient_funds
sleep 0.3
end
["First", "Second"].each do |title|
create(:budget_investment, :selected,
heading: california,
price: california.price,
title: title
)
end
login_as(user)
visit budget_investments_path(budget, heading_id: california.id)
within(".budget-investment", text: "First") { click_link "Vote" }
within(".budget-investment", text: "Second") { click_link "Vote" }
expect(page).to have_link "Remove vote"
expect(Budget::Ballot::Line.count).to eq 1
end
These columns were causing Rails 5.2 to throw a warning when ordering by
them, as if they weren't valid column names:
DEPRECATION WARNING: Dangerous query method (method whose arguments are
used as raw SQL) called with non-attribute argument(s):
:"budget/investments_count". Non-attribute arguments will be disallowed
in Rails 6.0. This method should not be called with user-provided
values, such as request parameters or model attributes. Known-safe
values can be passed by wrapping them in Arel.sql().
This change also makes their names consistent with the rest of our
tables and columns.
I'm not sure why the code didn't work without this line, but it doesn't
seem to be necessary anymore (maybe after upgrading Ruby or Rails?).
I'm removing it now because... why not now? :) The Ruby interpreter is
raising a warning due to this line, and in Ruby 2.5 constant lookup has
changed slightly (although I don't think this line is affected by that
change).
Note about the change in the Setting model: Ruby actually ignores return
values in setter methods, so the line isn't necessary.
We were evaluating its value when the server starts. On production
enviroments, that could mean weeks or months before the available
locales are checked again, and so it would be possible to use a list
which is no longer in sync with the list provided by microsoft.
This method is deprecated in Rails 5.1 because its behavior will be
different in `before` and `after` callbacks.
We're replacing the deprecated `attribute_changed?` and `attribute_was`
methods with `saved_change_to_attribute?` and
`attribute_before_last_save` during `after_save` callbacks.
https://github.com/rails/rails/pull/32835/
This method is deprecated in Rails 5.1 because its behavior will be
different in `before` and `after` callbacks.
Here we're replacing the deprecated `attribute_changed?` and
`attribute_was` with `will_save_change_to_attribute?` and
`attribute_in_database` during `before_save` callbacks.
https://github.com/rails/rails/pull/32835/
Implementation tries to be open for further extensions, such as deciding on
search dictionary based on configuration option or by locale set for
given user.
When an investment had been assigned a user tag and a valuation tag with
the same name, it appeared twice when filtering by tag.
This is because by design, in order to provide compatibility with scopes
using "select" or "distinct", the method `tagged_with` doesn't select
unique records.
Forcing the query to return unique records solves the issue.