Sometimes we're interpolating a link inside a translation, and marking
the whole translations as HTML safe.
However, some translations added by admins to the database or through
crowdin are not entirely under our control.
Although AFAIK crowdin checks for potential cross-site scripting
attacks, it's a good practice to sanitize parts of a string potentially
out of our control before marking the string as HTML safe.
There's a case where we would face a Cross-Site Scripting attack. An
attacker could use the browser's developer tools to add (on their
browser) a `<code>` tag with a `<script>` tag inside in the text of the
draft version. After doing so, commenting on that text would result in
the attacker's JavaScript being executed.
It's possible to create a newsletter or a proposed action with
<script> tags by filling in the body using a textarea instead of a
CKEditor. While we trust our administrators not to do so, it's better to
completely eliminate that possibility.
The name `html_safe` is very confusing, and many developers (including
me a few years ago) think what that method does is convert the HTML
contents to safe content. It's actually quite the opposite: it marks the
string as safe, so the HTML inside it isn't stripped out by Rails.
In some cases we were marking strings as safe because we wanted to add
some HTML. However, it meant the whole string was considered safe, and
not just the contents which were under our control.
In particular, some translations added by admins to the database or
through crowding were marked as safe, when it wasn't necessarily the
case.
Although AFAIK crowdin checks for potential cross-site scripting
attacks, it's a good practice to sanitize parts of a string potentially
out of our control before marking the string as HTML safe.
We use this method in two different scenarios. In an AJAX request, we
don't want to return every booth if the search is blank. However, in a
normal HTTP GET request, we want to return every record when the search
is empty, as we do everywhere else.
It's possible the behaviour of the AJAX call is unusual, since it
searches all booths, and not just the ones assigned to a poll. If we
changed this behaviour, we could simplify the code and remove the
`quick_search` method.
This way we can simplify the way we generate form fields. In some cases,
we also use the human attribute in table headers, which IMHO makes
sense.
I haven't moved all of them: for example, sometimes a label is
different depending on whether it's shown to administrators, valuators,
or users. And I haven't touched the ones related to devise, since I
wasn't sure about possible side effects.
Note I've also removed placeholders when they had the same text as their
labels, since they weren't helpful. On the contrary, the added redundant
text to the form, potentially distracting users.
This is a very subtle behaviour: `match /attachment/i` could represent a
regular expression, but it could also represent a division like
`match / attachment / i`. So we need to make an exception to the usual
way we omit parenthesis in RSpec expectations.
Naming two variables the same way is confusing at the very least, and
can lead to hard to debug errors. That's why the Ruby interpreter issues
a warning when we do so.
It could be argued that the following lines use single quotes to escape
double quotes, but on the other hand, using a single quote isn't a
great benefit.
Moderate legislation proposals
- added a controller for moderation/legislation
- updated view to appropriate link + added route
- added a spec
- Feature test
- test for faded
- javascripts for visual effects
This one is a bit different than our usual scenario, since we create
three annotations and we only use two of them in the specs (because we
visit the path to that annotation). So there are probably better options
than the combination of `let!` and `before` I've chosen.
Having two questions, each of them with two comments, made the code hard
to follow.
Grouping the comments inside the block creating the questions makes it
easier to know which comment belongs to which question, even if the code
is still not 100% readable.
We also remove instance variables, which by the way used the same
variable name for two different things.
We couldn't declare them inside the block because they would be
considered local variables and its value would be lost when the block
was finished. So we were using instance variables instead.
However, with instance variables we don't get any warnings when we
misspell their names. We can avoid them by declaring the local variables
before the block starts.
I haven't found an elegant way to remove them, but since they were the
only three variables left out of 383 we used to have, I can live with
this low percentage of inelegant solutions.
We had four headings, some of them had investments, and some of them
didn't, and it was very hard to scan the code and check which investment
belongs to which heading.
Grouping the investments inside the block creating the heading makes
that task much easier, even if the code is still not 100% readable.
We also avoid unused variables which were there to keep the code
vertically algined.
We can change the code a bit so the useless assignment is either part of
the setup (where only another variable was present) or isolated in the
"action" part of the test.
There's a very common pattern in our test, where the setup only has two
lines:
variable = create(:something)
unused_variable = create(:something_else, something: variable)
In this case, since there's a blank line below these ones and then we'll
get to the body of the test, and the second variable is going to be
created based on the first variable, we can remove the useless
assignment and the readability is still OK.
Another option we almost unanimously discarded was:
variable = create(:something)
_unused_variable = create(:something_else, something: variable)
We don't use it anywhere else, either.
One more option we considered but found a bit too much for simple tests:
variable = create(:something) do |something|
create(:something_else, something: variable)
end
Then of course we could move the setup to `let` and `before` blocks, but
the tests could get over-structured really quickly.
These variables can be considered a block, and so removing them doesn't
make the test much harder to undestand.
Sometimes these variables formed the setup, sometimes they formed an
isolated part of the setup, and sometimes they were the part of the test
that made the test different from other tests.
The limit parameter wasn't specified in the test but in the default
value in the database, making the test hard to read.
Since now we've moved the other processes to separate tests, now we can
create four processes using `times` and keep the test simple.
In the scenario where we want to test scopes and use `match_array`, we
usually declare variables we never use, which raises a warning in the
Ruby interpreter (since the main cause for an unused variable is a
typo).
So I've decided to just split the tests into cases where every record is
returned and cases were no records are returned, just like we do in
other places.
There are several other options we've considered:
1. Don't declare unused variables, but declare the ones we use
2. Prefix unused variables with un underscore
3. Declare just one variable being an array containing all elements, and
access the elements using Array#[]
4. Don't declare any variables, and compare results against attributes
such as titles
None of these options was met with enthusiasm.
The test was hard to follow, and splitting the test in three it's easier
to read and doesn't create unused variables anymore. On the minus side,
now there's one extra request during the tests.