Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to [Break Versioning](https://www.taoensso.com/break-ve
### Fixed

- Don't overwrite libpq ENV vars with empty strings (@StantonMatt in #414).
- Skip generating duplicate routes when running `generate action`. (@sandbergja in #417)

### Security

Expand Down
7 changes: 7 additions & 0 deletions lib/hanami/cli/files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ def touch(path)
created(path)
end

def block_contains?(path, target, contents)
content = adapter.read(path)
class_start = content.index(target)
class_end = content.index(/(?:^|\s)#{CLOSE_BLOCK}(?:$|\s)/, class_start)
content[class_start..class_end].include? contents
end

private

attr_reader :out
Expand Down
25 changes: 21 additions & 4 deletions lib/hanami/cli/generators/app/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,14 @@ def insert_route(key:, namespace:, url_path:, http_method:)
route = route_definition(key:, url_path:, http_method:)

if namespace == Hanami.app.namespace
fs.inject_line_at_class_bottom(routes_location, "class Routes", route)
add_route_to_file(file: routes_location, route:)
else
slice_routes = fs.join("slices", namespace, "config", "routes.rb")

if fs.exist?(slice_routes)
fs.inject_line_at_class_bottom(slice_routes, "class Routes", route)
add_route_to_file(file: slice_routes, route:)
else
slice_matcher = /slice[[:space:]]*:#{namespace}/
fs.inject_line_at_block_bottom(routes_location, slice_matcher, route)
add_route_to_block(file: routes_location, namespace:, route:)
end
end
end
Expand Down Expand Up @@ -190,6 +189,24 @@ def route_http(action, http_method)

result
end

def add_route_to_file(file:, route:)
target_class = "class Routes"
if fs.block_contains?(file, target_class, route)
out.puts "Route (#{route}) already exists, skipping..."
else
fs.inject_line_at_class_bottom(file, "class Routes", route)
end
end

def add_route_to_block(file:, namespace:, route:)
slice_matcher = /slice[[:space:]]*:#{namespace}/
if fs.block_contains?(file, slice_matcher, route)
out.puts "Route (#{route}) already exists, skipping..."
else
fs.inject_line_at_block_bottom(file, slice_matcher, route)
end
end
end
end
end
Expand Down
54 changes: 54 additions & 0 deletions spec/unit/hanami/cli/commands/app/generate/action_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,31 @@ def error_output = err.string.chomp
end
end

context "when route already exists in route file" do
it "does not add a duplicate route" do
within_application_directory do
routes_contents = <<~CODE
# frozen_string_literal: true

require "hanami/routes"

module #{app}
class Routes < Hanami::Routes
root { "Hello from Hanami" }
get "/users", to: "users.index"
end
end
CODE
fs.write("config/routes.rb", routes_contents)

generate_action

expect(fs.read("config/routes.rb")).to eq routes_contents
expect(output).to include 'Route (get "/users", to: "users.index") already exists, skipping...'
end
end
end

context "with existing action file" do
let(:file_path) { "app/actions/#{namespace}/#{action}.rb" }

Expand Down Expand Up @@ -1252,6 +1277,35 @@ class Routes < Hanami::Routes
end
end

it "does not add a duplicate route to the slice block" do
within_application_directory do
prepare_slice!
fs.mkdir("slices/api")

routes_contents = <<~CODE
# frozen_string_literal: true

require "hanami/routes"

module #{app}
class Routes < Hanami::Routes
root { "Hello from Hanami" }

slice :#{slice}, at: "/#{slice}" do
get "/home", to: "home.index"
end
end
end
CODE
fs.write("config/routes.rb", routes_contents)

subject.call(slice:, name: "home.index")

expect(fs.read("config/routes.rb")).to eq routes_contents
expect(output).to include 'Route (get "/home", to: "home.index") already exists, skipping...'
end
end

it "raises error if slice is nonexistent" do
expect {
subject.call(slice: "foo", name: action_name)
Expand Down
Loading