Pragmater
A command line interface that does one thing well by being entirely focused on managing/formatting source file directive pragmas (a.k.a. magic comments). Examples:
#! /usr/bin/env ruby
# frozen_string_literal: true
# encoding: UTF-8
With Ruby 2.3.0, frozen strings are supported via a pragma. This gem provides an easy way to insert or remove pragmas to single or multiple Ruby source files in order to benefit from improved memory and concurrency performance.
- Features
- Requirements
- Setup
- Usage
- Command Line Interface (CLI)
- Customization
- Available Pragmas
- Syntax
- Precedence
- Frozen String Literals
- Consistency
- Development
- Tests
- License
- Security
- Code of Conduct
- Contributions
- Developer Certificate of Origin
- Versions
- Community
- Credits
Features
-
Supports inserting a pragma or multiple pragmas to single or multiple source files.
-
Supports removing pragma(s) from single or multiple source files.
-
Supports file list filtering. Defaults to any file.
-
Ensures duplicate pragmas never exist.
-
Ensures pragmas are consistently formatted.
Requirements
Setup
To install with security, run:
# 💡 Skip this line if you already have the public certificate installed.
gem cert --add <(curl --compressed --location https://alchemists.io/gems.pem)
gem install pragmater --trust-policy HighSecurity
To install without security, run:
gem install pragmater
Usage
Command Line Interface (CLI)
From the command line, type: pragmater --help
Both the insert
and remove
commands support the same options for specifying pragmas and/or
included files. Example:
pragmater insert --comments "# frozen_string_literal: true" --patterns "Gemfile" "Guardfile" "Rakefile" ".gemspec" "config.ru" "bin/**/*" "**/*.rake" "**/*.rb"
The insert
and remove
commands default to the current working directory so a path isn’t
necessary unless you want to run Pragmater in a directory structure other than your current working
directory.
Customization
This gem can be configured via a global configuration: $HOME/.config/pragmater/configuration.yml
It can also be configured via XDG environment variables.
The default configuration is as follows:
comments: []
patterns: []
root_dir: "."
Feel free to take the above configuration, modify, and save as your own custom configuration.yml
.
The configuration.yml
file can be configured as follows:
-
comments
: Defines the array of pragmas you want to insert into your source files. Whatever is defined here will be the default used for insert and remove operations. -
includes
: Defines the array file patterns to apply to. Whatever is defined here will be the default used for insert and remove operations. -
root_dir
: Defines the root directory to apply theincludes
patterns too. By default, this will be the current directory you are running Pragmater from but can be a different directory entirely.
Available Pragmas
With Ruby 2.3 and higher, the following pragmas are available:
-
# encoding:
Defaults toUTF-8
but any supported encoding can be used. For a list of values, launch an IRB session and runEncoding.name_list
. -
# coding:
The shorthand for# encoding:
. Supports the same values as mentioned above. -
# frozen_string_literal:
Defaults tofalse
but can take eithertrue
orfalse
as a value. When enabled, Ruby will throw errors when strings are used in a mutable fashion. -
# warn_indent:
Defaults tofalse
but can take eithertrue
orfalse
as a value. When enabled, and running Ruby with the-w
option, it’ll throw warnings for code that isn’t indented by two spaces.
Syntax
The pragma syntax allows for two kinds of styles. Example:
# encoding: UTF-8
# -*- encoding: UTF-8 -*-
Only the former syntax is supported by this gem as the latter syntax is more verbose and requires additional typing.
Precedence
When different multiple pragmas are defined, they all take precedence:
# encoding: binary
# frozen_string_literal: true
In the above example, both binary encoding and frozen string literals behavior will be applied.
When defining multiple pragmas that are similar, behavior can differ based on the kind of pragma used. The following walks through each use case so you know what to expect:
# encoding: binary
# encoding: UTF-8
In the above example, only the binary encoding will be applied while the UTF-8 encoding will be
ignored (same principle applies for the coding
pragma too).
# frozen_string_literal: false
# frozen_string_literal: true
In the above example, frozen string literal support will be enabled instead of being disabled.
# warn_indent: false
# warn_indent: true
In the above example, indentation warnings will be enabled instead of being disabled.
Frozen String Literals
Support for frozen string literals was added in Ruby 2.3.0. The ability to freeze strings within a source can be done by placing a frozen string pragma at the top of each source file. Example:
# frozen_string_literal: true
This is great for selective enablement of frozen string literals but might be too much work for some (even with the aid of this gem). As an alternative, frozen string literals can be enabled via the following Ruby command line option:
--enable=frozen-string-literal
It is important to note that, once enabled, this freezes strings program-wide – It’s an all or nothing option.
Regardless of whether you leverage the capabilities of this gem or the Ruby command line option mentioned above, the following Ruby command line option is available to aid debugging and tracking down frozen string literal issues:
--debug=frozen-string-literal
Finally, you can use --debug
(or $DEBUG=true
) to force all raised exceptions to print to the console whether they are rescued or not. This is best used in conjunction with the above.
Ruby 2.3.0 also added the following methods to the String
class:
-
String#+@
: Answers a duplicated, mutable, string if not already frozen. Example:immutable = "test".freeze mutable = +immutable mutable.frozen? # false mutable.capitalize! # "Test"
-
String#-@
: Answers a immutable string if not already frozen. Example:mutable = "test" immutable = -mutable immutable.frozen? # true immutable.capitalize! # FrozenError
You can also use the methods, shown above, for variable initialization. Example:
immutable = -"test"
mutable = +"test"
immutable.frozen? # true
mutable.frozen? # false
💡 Use of String#-@
was enhanced in Ruby 2.5.0 to
deduplicate all instances of the same string thus reducing your memory footprint. This can be
valuable in situations where you are not using the frozen string comment and need to selectively
freeze strings.
💡 Use of String#dup
was significantly enhanced in Ruby 3.3.0 to be as performant as String#+@
so you can use String#dup
instead of String#+@
since String#dup
is easier to read.
Consistency
As an added bonus, this gem ensures pragmas for all analyzed files are formatted in a consistent
style. This means there is always a space after the octothorp (#
). Here are multiple pragmas
presented together for a visual comparison:
#! /usr/bin/env ruby
# encoding: UTF-8
# coding: UTF-8
# frozen_string_literal: true
# warn_indent: true
One oddity to the above is the use of # !/usr/bin/env ruby
is not allowed but #! /usr/bin/env
ruby
is which is why spacing is slightly different for shell pragmas.
Development
To contribute, run:
git clone https://github.com/bkuhlmann/pragmater
cd pragmater
bin/setup
You can also use the IRB console for direct access to all objects:
bin/console
Tests
To test, run:
bin/rake
Credits
-
Built with Gemsmith.
-
Engineered by Brooke Kuhlmann.