Adding Jasmine JavaScript specs to a Webpack(er) project
The goal is to get Jasmine specs running in a Rails project using Webpacker, with the browser based test runner. Should be easily adaptable to a pure Webpack setup.
Step 1: Install Jasmine
yarn add jasmine-core
Step 2: Add two separate packs
Since we do not want to mix Jasmine into our regular Javascript, we will create two additional packs. The first only contains Jasmine and the test runner. The second will contain our normal application code and the specs themselves.
We cannot...
Cucumber: Identifying slow steps that drag down your test speed
In most projects I know, Cucumber test suite speed is not an issue. Of course, running 350 features takes its time, but still each test for itself is reasonably fast. There is nothing you can do to fundamentally speed up such a test (of course, you should be using parallel_tests).
However, in projects that go beyond clicking around in a web GUI and checking results, there might be various measures to speed things up. Katapult tests for example could be sped up more than 4 times by re...
Generating and streaming ZIP archives on the fly
When your Rails application offers downloading a bunch of files as ZIP archive, you basically have two options:
- Write a ZIP file to disk and send it as a download to the user.
- Generate a ZIP archive on the fly while streaming it in chunks to the user.
This card is about option 2, and it is actually fairly easy to set up.
We are using this to generate ZIP archives with lots of files (500k+) on the fly, and it works like a charm.
Why stream downloads?
Offering downloads of large archives can be cumbersome:
- It takes time to b...
Enabling view rendering for controller specs
Views are normally (for good reason) not rendered in controller specs. If you need it to happen, use:
RSpec 1 (Rails 2):
RSpec 2 (Rails 3):
Note that you can't use that inside it blocks but need to put it in the nesting example group, like this:
describe '#update' do
cont...
Carrierwave: How to remove container directories when deleting a record
When deleting a record in your Rails app, Carrierwave automatically takes care of removing all associated files.
However, the file's container directory will not be removed automatically. If you delete records regularly, this may be an annoyance.
Here is a solution which was adapted from the Carrierwave GitHub wiki and cleans up any empty parent directories it can find.
class ExampleUploader < CarrierWave...
Capistrano 3: Running a command on all servers
This Capistrano task runs a command on all servers.
bundle exec cap production app:run cmd='zgrep -P "..." RAILS_ROOT/log/production.log'
Code
# lib/capistrano/tasks/app.rake
namespace :app do
# Use e.g. to grep logs on all servers:
# b cap production app:run_cmd cmd='zgrep -P "..." RAILS_ROOT/log/production.log'
#
# * Use RAILS_ROOT as a placeholder for the remote Rails root directory.
# * Append ` || test $? =1;` to grep calls in order to avoid exit code 1 (= "nothing found")
# * To be able to process ...
Heads up: RSpec-Mocks' #stub_const will define intermediate modules that have not been loaded yet
The issue: You are using stub_const to change a constant value for your test.
stub_const "SomeClass::CONST", 'test'
All of a sudden, tests fail with undefined method 'some_method' for #<SomeClass:0x00000000101433a8>.
The reason
When using stub_const before the Class containing the constant has been loaded, a module is automatically created with the name.
Since RSpec does no autoloading, it will create a SomeClass module by itself. This is arguably a good idea.
As a workaround, use stub_const in your Rails specs li...
Why has_many :through associations can return the same record multiple times
An association defined with has_many :through will return the same record multiple times if multiple join models for the same record exist (a n:m relation). To prevent this, you need to add ->{ uniq } as second argument to has_many (below Rails 4 it is a simple option: has_many :xyz, :uniq => true).
Example
Say you have an Invoice with multiple Items. Each Item has a Product:
class Invoice < ActiveRecord::Base
has_many :items
has_many :products, :through => :items
end
class Item < ActiveRecord::Base
...
How to update the bundler version in a Gemfile.lock
-
Install the latest
bundlerversion:gem install bundler Fetching bundler-2.3.5.gem Successfully installed bundler-2.3.5 1 gem installed -
Update the bundler version in
Gemfile.lock:bundle update --bundler -
Confirm it worked:
$ tail -n2 Gemfile.lock BUNDLED WITH 2.3.5
Notes:
-
Bundler should automatically detect the latest installed version. If it does not, you can specify your preferred version like so:
b...
Testing ActiveRecord callbacks with RSpec
Our preferred way of testing ActiveRecord is to simply create/update/destroy the record and then check if the expected behavior has happened.
We used to bend over backwards to avoid touching the database for this. For this we used a lot of stubbing and tricks like it_should_run_callbacks.
Today we would rather make a few database queries than have a fragile test full of stubs.
Example
Let's say your User model creates a first Project on cr...
Heads up: Quering array columns only matches equally sorted arrays
Given you have an array column like this:
create_table "users", force: :cascade do |t|
t.integer "movie_ids", default: [], array: true
end
You might think that the following queries yield the same result:
User.where(movie_ids: [16, 17])
User.where(movie_ids: [17, 16])
Turn's out - they are not! They do care about array ordering more than I do.
To query for identical arrays independent of their order you have to either:
- Sort both the query and database content. If you're on Rails 7.1 you can use the new [`normal...
Migration from the Asset Pipeline to Webpacker
This is a short overview of things that are required to upgrade a project from the Asset Pipeline to Webpacker. Expect this upgrade to take a few days even the diff is quite small afterwards.
Preparations
1. Find all libraries that are bundled with the asset pipeline. You can check the application.js and the application.css for require and import statements. The source of a library is most often a gem or a vendor directory.
2. Find an working example for each library in the application and write it down.
3. Find out the ver...
Use DatabaseCleaner with multiple test databases
There is a way to use multiple databases in Rails.
You may have asked yourself how you're able to keep your test databases clean, if you're running multiple databases with full read and write access at the same time. This is especially useful when migrating old/existing databases into a new(er) one.
Your database.yml may look like this:
default: &default
adapter: postgresql
encoding: unicode
username: <%= ENV['DATABASE_USER'] %>
host: <%= ENV['DATABASE...
Shortcuts for getting ids for an ActiveRecord scope
You can use .ids on an ActiveRecord scope to pluck all the ids of the relation:
# Modern Rails
User.where("users.name LIKE 'Foo Bar'").ids
# Rails 3.2+ equivalent
User.where("users.name LIKE 'Foo Bar'").pluck(:id)
# Edge rider equivalent for Rails 2+
User.where("users.name LIKE 'Foo Bar'").collect_ids
When reading model columns during class definition, you must handle a missing/empty database
When doing some meta-programming magic and you want to do something for all attributes of a class, you may need to access connection or some of its methods (e.g. columns) during class definition.
While everything will be fine while you are working on a project that is in active development, the application will fail to boot when the database is missing or has no tables. This means that Raketasks like db:create or db:migrate fail on a freshly cloned project.
The reason is your environment.rb which is loaded for Raketasks and calls...
How to search through logs on staging or production environments
We generally use multiple application servers (at least two) and you have to search on all of them if you don't know which one handled the request you are looking for.
Rails application logs usually live in /var/www/<project-environment-name>/shared/log.
Web server logs usually live in /var/www/<project-environment-name>/log.
Searching through single logs with grep / zgrep
You can use grep in this directory to only search the latest logs or zgrep to also search older (already zipped) logs. zgrep is used just like grep ...
Detecting if a Ruby gem is loaded
Detect if a gem has been activated
A gem is activated if it is either in the current bundle (Gemfile.lock), or if you have manually activated it using Kernel#gem (old-school).
To detect if e.g. activerecord has been activated:
if Gem.loaded_specs.has_key?('activerecord')
# ActiveRecord was activated
end
Detect if a particular gem version has been activated
To detect if e.g. activerecord ma...
MySQL: Disable query cache for database profiling
If you want to see how long your database queries actually take, you need to disable MySQL's query cache. This can be done globally by logging into a database console, run
SET GLOBAL query_cache_type=OFF;
and restart your rails server.
You can also disable the cache on a per query basis by saying
SELECT SQL_NO_CACHE * FROM ...
You also probably want to disable Rails internal (per-request) cache. For this, wrap your code with a call to ActiveRecord::Base.uncached. For example, as an around_filter:
d...
Why two Ruby Time objects are not equal, although they appear to be
So you are comparing two Time objects in an RSpec example, and they are not equal, although they look equal:
expected: Tue May 01 21:59:59 UTC 2007,
got: Tue May 01 21:59:59 UTC 2007 (using ==)
The reason for this is that Time actually tracks fractions of a second, although #to_s doesn't say so and even though you probably only care about seconds. This means that two consecutive calls of Time.now probably return two inequal values.
Consider freezing time in your tests so it is not dependent on the speed of the executi...
How to fix: Pasting in IRB 1.2+ is very slow
IRB 1.2 (shipped with Ruby 2.7, but works on 2.5+) brings pretty syntax highlighting and multiline cursor navigation. However, pasting longer contents is incredibly slow. You can fix that by disabling said features. [1]
Ruby 3.0.0-pre2 solved the issue (however, the fix does not appear to be included in IRB 1.2.6, it must be Ruby itself).
Option 1:
Add a command line flag when opening an IRB:
irb --nomultiline
This also works on modern Rails...
ES6 imports are hoisted to the top
From Exploring ES6:
Module imports are hoisted (internally moved to the beginning of the current scope). Therefore, it doesn’t matter where you mention them in a module and the following code works without any problems:
foo(); import { foo } from 'my_module';
Footgun example
When you're not aware of import hoisting you may be surprised that your code runs in a different order than you see in the source file.
The example below is taken from the [...
Storing trees in databases
This card compares patterns to store trees in a relation database like MySQL or PostgreSQL. Implementation examples are for the ActiveRecord ORM used with Ruby on Rails, but the techniques can be implemented in any language or framework.
We will be using this example tree (from the acts_as_nested_set docs):
root
|
+-- Child 1
| |
| +-- Child 1.1
| |
| +-- Child 1.2
|
+-- ...
Vortrag: Content Security Policy: Eine Einführung
Grundidee
CSP hat zum Ziel einen Browser-seitigen Mechanismus zu schaffen um einige Angriffe auf Webseiten zu verhindern, hauptsächlich XSS-Angriffe.
Einschub: Was ist XSS?
XSS = Cross Site Scripting. Passiert wenn ein User ungefiltertes HTML in die Webseite einfügen kann.
<div class="comment">
Danke für den interessanten Beitrag! <script>alert('you have been hacked')</script>
</div>
Rails löst das Problem weitgehend, aber
- Programmierfehler weiter möglich
- manchmal Sicherheitslücken in Gems oder Rails
Lösungsid...
PostgreSQL cheat sheet for MySQL lamers
So you're switching to PostgreSQL from MySQL? Here is some help...
General hints on PostgreSQL
-
\?opens the command overview -
\dlists things:\dulists users,\dtlists tables etc
Command comparison
| Description | MySQL command | PostgreSQL equivalent |
|---|---|---|
| Connect to the database | mysql -u $USERNAME -p |
sudo -u postgres psql |
| Show databases | SHOW DATABASES; | \l[ist] |
| Use/Connect to a database named 'some_database' | USE some_database; | \c some_dat... |