Skip to content

Commit

Permalink
Add the ability to use variables in Gemfiles (#223)
Browse files Browse the repository at this point in the history
This allows us to add custom instructions, or adding `frozen_string_literal` when desired.
  • Loading branch information
joe-sharp authored May 16, 2024
1 parent f5e31c4 commit be4fa01
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 16 deletions.
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,15 @@ customizations include:
- heading: a string that by default adds "# This file was generated by Appraisal" to the top of each Gemfile, (the string will be commented for you)
- single_quotes: a boolean that controls if strings are single quoted in each Gemfile, defaults to false

You can also provide variables for substitution in the heading, based on each appraisal. Currently supported variables:
- `%{appraisal}`: Becomes the name of each appraisal, e.g. `rails-3`
- `%{gemfile}`: Becomes the filename of each gemfile, e.g. `rails-3.gemfile`
- `%{gemfile_path}`: Becomes the full path of each gemfile, e.g. `/path/to/project/gemfiles/rails-3.gemfile`
- `%{lockfile}`: Becomes the filename of each lockfile, e.g. `rails-3.gemfile.lock`
- `%{lockfile_path}`: Becomes the full path of each lockfile, e.g. `/path/to/project/gemfiles/rails-3.gemfile.lock`
- `%{relative_gemfile_path}`: Becomes the relative path of each gemfile, e.g. `gemfiles/rails-3.gemfile`
- `%{relative_lockfile_path}`: Becomes the relative path of each lockfile, e.g. `gemfiles/rails-3.gemfile.lock`

### Example Usage

**Appraisals**
Expand All @@ -169,8 +178,8 @@ customize_gemfiles do
heading: <<~HEADING
frozen_string_literal: true
File has been generated by Appraisal, do NOT modify it directly!
See the conventions at https://example.com/
`%{gemfile}` has been generated by Appraisal, do NOT modify it or `%{lockfile}` directly!
Make the changes to the "%{appraisal}" block in `Appraisals` instead. See the conventions at https://example.com/
HEADING
}
end
Expand All @@ -184,8 +193,8 @@ Using the `Appraisals` file defined above, this is what the resulting `Gemfile`
```ruby
# frozen_string_literal: true

# File has been generated by Appraisal, do NOT modify it directly!
# See the conventions at https://example.com/
# `rails-3.gemfile` has been generated by Appraisal, do NOT modify it or `rails-3.gemfile.lock` directly!
# Make the changes to the "rails-3" block in `Appraisals` instead. See the conventions at https://example.com/

gem 'rails', '3.2.14'
```
Expand Down
3 changes: 2 additions & 1 deletion lib/appraisal/appraisal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def git_source(*args, &block)

def write_gemfile
File.open(gemfile_path, "w") do |file|
signature = Customize.heading || "This file was generated by Appraisal"
signature =
Customize.heading(self) || "This file was generated by Appraisal"
file.puts([comment_lines(signature), quoted_gemfile].join("\n\n"))
end
end
Expand Down
20 changes: 19 additions & 1 deletion lib/appraisal/customize.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,30 @@ def initialize(heading: nil, single_quotes: false)
@@single_quotes = single_quotes
end

def self.heading
def self.heading(gemfile)
@@heading ||= nil
customize(@@heading, gemfile)
end

def self.single_quotes
@@single_quotes ||= false
end

def self.customize(heading, gemfile)
return nil unless heading

format(
heading.to_s,
appraisal: gemfile.send("clean_name"),
gemfile: gemfile.send("gemfile_name"),
gemfile_path: gemfile.gemfile_path,
lockfile: "#{gemfile.send('gemfile_name')}.lock",
lockfile_path: gemfile.send("lockfile_path"),
relative_gemfile_path: gemfile.relative_gemfile_path,
relative_lockfile_path: "#{gemfile.relative_gemfile_path}.lock",
)
end

private_class_method :customize
end
end
141 changes: 131 additions & 10 deletions spec/appraisal/customize_spec.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,139 @@
require "spec_helper"
require "appraisal/appraisal"
require "appraisal/customize"

describe Appraisal::Customize do
it "has defaults" do
expect(described_class.heading).to eq nil
expect(described_class.single_quotes).to eq false
expect { described_class.new }.to_not(change do
[described_class.heading, described_class.single_quotes]
end)
let(:appraisal) { Appraisal::Appraisal.new("test", "Gemfile") }
let(:single_line_heading) { "This file was generated with a custom heading!" }
let(:multi_line_heading) do
<<~HEADING
frozen_string_literal: true
This file was generated with a custom heading!
HEADING
end
let(:subject) { described_class.new }
let(:single_line_subject) do
described_class.new(heading: single_line_heading)
end
let(:multi_line_single_quotes_subject) do
described_class.new(heading: multi_line_heading, single_quotes: true)
end

describe ".heading" do
it "returns nil if no heading is set" do
subject
expect(described_class.heading(appraisal)).to eq(nil)
end

it "returns the heading if set" do
single_line_subject
expect(described_class.heading(appraisal)).to eq(single_line_heading)
end

it "returns the heading without an trailing newline" do
multi_line_single_quotes_subject
expect(described_class.heading(appraisal)).to eq(multi_line_heading.chomp)
expect(described_class.heading(appraisal)).to_not end_with("\n")
end
end

describe ".single_quotes" do
it "returns false if not set" do
subject
expect(described_class.single_quotes).to eq(false)
end

it "returns true if set" do
multi_line_single_quotes_subject
expect(described_class.single_quotes).to eq(true)
end
end

it "can override defaults" do
described_class.new(single_quotes: true, heading: "foo")
expect(described_class.heading).to eq "foo"
expect(described_class.single_quotes).to eq true
describe ".customize" do
let(:appraisal_name) { "test" }
let(:gemfile) { "test.gemfile" }
let(:lockfile) { "#{gemfile}.lock" }
let(:gemfile_relative_path) { "gemfiles/#{gemfile}" }
let(:lockfile_relative_path) { "gemfiles/#{lockfile}" }
let(:gemfile_full_path) { "/path/to/project/#{gemfile_relative_path}" }
let(:lockfile_full_path) { "/path/to/project/#{lockfile_relative_path}" }
before do
allow(appraisal).to receive(:gemfile_name).and_return(gemfile)
allow(appraisal).to receive(:gemfile_path).and_return(gemfile_full_path)
allow(appraisal).to receive(:lockfile_path).and_return(lockfile_full_path)
allow(appraisal).to receive(:relative_gemfile_path).
and_return(gemfile_relative_path)
end

it "returns nil if no heading is set" do
subject
expect(described_class.send(:customize, nil, appraisal)).to eq(nil)
end

it "returns the heading unchanged" do
single_line_subject
expect(described_class.send(
:customize,
single_line_heading,
appraisal,
)).to eq(single_line_heading)
end

it "returns the heading with the appraisal name" do
expect(described_class.send(
:customize,
"Appraisal: %{appraisal}", # rubocop:disable Style/FormatStringToken, Metrics/LineLength
appraisal,
)).to eq("Appraisal: #{appraisal_name}")
end

it "returns the heading with the gemfile name" do
expect(described_class.send(
:customize,
"Gemfile: %{gemfile}", # rubocop:disable Style/FormatStringToken
appraisal,
)).to eq("Gemfile: #{gemfile}")
end

it "returns the heading with the gemfile path" do
expect(described_class.send(
:customize,
"Gemfile: %{gemfile_path}", # rubocop:disable Style/FormatStringToken, Metrics/LineLength
appraisal,
)).to eq("Gemfile: #{gemfile_full_path}")
end

it "returns the heading with the lockfile name" do
expect(described_class.send(
:customize,
"Lockfile: %{lockfile}", # rubocop:disable Style/FormatStringToken, Metrics/LineLength
appraisal,
)).to eq("Lockfile: #{lockfile}")
end

it "returns the heading with the lockfile path" do
expect(described_class.send(
:customize,
"Lockfile: %{lockfile_path}", # rubocop:disable Style/FormatStringToken, Metrics/LineLength
appraisal,
)).to eq("Lockfile: #{lockfile_full_path}")
end

it "returns the heading with the relative gemfile path" do
expect(described_class.send(
:customize,
"Gemfile: %{relative_gemfile_path}", # rubocop:disable Style/FormatStringToken, Metrics/LineLength
appraisal,
)).to eq("Gemfile: #{gemfile_relative_path}")
end

it "returns the heading with the relative lockfile path" do
expect(described_class.send(
:customize,
"Gemfile: %{relative_lockfile_path}", # rubocop:disable Style/FormatStringToken, Metrics/LineLength
appraisal,
)).to eq("Gemfile: #{lockfile_relative_path}")
end
end
end

0 comments on commit be4fa01

Please sign in to comment.