Skip to content

Commit

Permalink
Add doc for types of recipes
Browse files Browse the repository at this point in the history
To help people understand what the different types are. The intention
is to add a more dedicated Refaster guide after this and then link to
it from this doc.
  • Loading branch information
mike-solomon committed Oct 25, 2023
1 parent db25019 commit b8de758
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 3 deletions.
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* [Migrate Hamcrest to AssertJ](/running-recipes/popular-recipe-guides/hamcrest-to-assertj.md)
* [Authoring Recipes](authoring-recipes/authoring-recipes.md)
* [Recipe development environment](authoring-recipes/recipe-development-environment.md)
* [The different types of recipes](authoring-recipes/types-of-recipes.md)
* [Writing a Java refactoring recipe](authoring-recipes/writing-a-java-refactoring-recipe.md)
* [Recipe testing](authoring-recipes/recipe-testing.md)
* [Recipe conventions and best practices](authoring-recipes/recipe-conventions-and-best-practices.md)
Expand Down
1 change: 1 addition & 0 deletions authoring-recipes/authoring-recipes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Authoring Recipes

* [Recipe development environment](/authoring-recipes/recipe-development-environment.md)
* [The different types of recipes](/authoring-recipes/types-of-recipes.md)
* [Writing a Java refactoring recipe](/authoring-recipes/writing-a-java-refactoring-recipe.md)
* [Recipe testing](/authoring-recipes/recipe-testing.md)
* [Recipe conventions and best practices](/authoring-recipes/recipe-conventions-and-best-practices.md)
Expand Down
7 changes: 4 additions & 3 deletions authoring-recipes/recipe-development-environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ Now you can run your recipe with `mvn rewrite:run` or `mvn rewrite:dryRun`

## Next steps

* [Writing a Java Refactoring Recipe](writing-a-java-refactoring-recipe.md)
* [Maven Plugin Configuration](/reference/rewrite-maven-plugin.md)
* [Gradle Plugin Configuration](/reference/gradle-plugin-configuration.md)
* [Learn about the different types of recipes you can create](/authoring-recipes/types-of-recipes.md)
* [Write a Java refactoring recipe](writing-a-java-refactoring-recipe.md)
* [Maven plugin configuration](/reference/rewrite-maven-plugin.md)
* [Gradle plugin configuration](/reference/gradle-plugin-configuration.md)
88 changes: 88 additions & 0 deletions authoring-recipes/types-of-recipes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Types of recipes

Before you jump into coding recipes, it's important to figure out what type of recipe you're going to make.

OpenRewrite offers support for three different types of recipes: declarative, refaster, and imperative. Each of these has its own advantages and disadvantages depending on the type of recipe you want to build.

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

refaster is not a recognized word. (unrecognized-spelling)

In this doc, we'll briefly describe each of these so that you can determine what type of recipe works best for your needs.

## Declarative recipes

[Declarative recipes](/reference/yaml-format-reference.md) are the simplest and most common recipes. They are entirely written in YAML, and they generally tie together existing recipes while adding some light configuration to them.

The [commmon static analysis recipe](https://github.com/openrewrite/rewrite-static-analysis/blob/main/src/main/resources/META-INF/rewrite/common-static-analysis.yml) is a great example of this. It takes a bunch of simple recipes relating to static analysis and combines them into one larger recipe that can be run more easily. It does not add any logic, and it doesn't change the existing recipes.

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

commmon is not a recognized word. (unrecognized-spelling)

```yaml
---
type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.staticanalysis.CommonStaticAnalysis
displayName: Common static analysis issues
description: Resolve common static analysis issues discovered through 3rd party tools.
recipeList:
- org.openrewrite.staticanalysis.AtomicPrimitiveEqualsUsesGet
- org.openrewrite.staticanalysis.BigDecimalRoundingConstantsToEnums
- org.openrewrite.staticanalysis.BooleanChecksNotInverted
- org.openrewrite.staticanalysis.CaseInsensitiveComparisonsDoNotChangeCase
- org.openrewrite.staticanalysis.CatchClauseOnlyRethrows
- org.openrewrite.staticanalysis.ChainStringBuilderAppendCalls
# ...
---
```

{% hint style="success" %}
As a best practice, if your recipe can be declarative (meaning it can be built out of other recipes), then you should make it declarative.
{% endhint %}

## Refaster templates

Refaster templates are the "middle ground" of recipes. They offer more functionality than [declarative recipes](#declarative-recipes), but not as much as [imperative recipes](#imperative-recipes). On the other hand, compared to an imperative recipe, they're much quicker to create and require much less knowledge to get started.

Refaster templates are ideal for straightforward replacements such as converting `StringUtils.equals(..)` to `Objects.equals(..)`. These are more than just a string replacement, though; they offer compiler and type support.

Refaster templates can also be used as a starting point for more complex recipe implementations. This is due to the fact that, when you define a refaster template, an imperative recipe is what is actually created behind the scenes. You can find these imperative recipes in the `build` directory after you've built your repository.

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

refaster is not a recognized word. (unrecognized-spelling)

A variety of refaster templates can be found in the [StringRules](https://github.com/openrewrite/rewrite-migrate-java/blob/v2.1.1/src/main/java/org/openrewrite/java/migrate/lang/StringRules.java#L23-L48) class in `rewrite-migrate-java`:

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

refaster is not a recognized word. (unrecognized-spelling)

```java
@RecipeDescriptor(
name = "Replace redundant `String` method calls with self",
description = "Replace redundant `substring(..)` and `toString()` method calls with the `String` self."
)
@SuppressWarnings("StringOperationCanBeSimplified")
public static class RedundantCall {
@BeforeTemplate
public String start(String string) {
return string.substring(0, string.length());
}

@BeforeTemplate
public String startAndEnd(String string) {
return string.substring(0);
}

@BeforeTemplate
public String toString(String string) {
return string.toString();
}

@AfterTemplate
public String self(String string) {
return string;
}
}
```

## Imperative recipes

[Imperative recipes](https://docs.openrewrite.org/authoring-recipes/writing-a-java-refactoring-recipe) offer the most freedom and functionality at the cost of being more difficult to create. They allow you to write Java code to implement your recipe.

Imperative recipes are ideal for situations where there is a lot of complexity or nuance needed to determine what should be changed or what it should be changed into. For instance, if you wanted to write a recipe that added the `final` modifier to any local variables that aren't reassigned, you would need to create an imperative recipe as refaster templates don't have the ability to determine whether a variable has been reassigned.

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

refaster is not a recognized word. (unrecognized-spelling)

If your recipe can be defined via a declarative YAML file or via a refaster template, you should do so -- even if you can technically create an imperative recipe that does the same thing.

Check failure

Code scanning / check-spelling

Unrecognized Spelling Error

refaster is not a recognized word. (unrecognized-spelling)

The [FinalizeLocalVariables recipe](https://github.com/openrewrite/rewrite-static-analysis/blob/main/src/main/java/org/openrewrite/staticanalysis/FinalizeLocalVariables.java) is a great example of an imperative recipe.

## Next steps

* If you want more information about the types of recipes and the recipe execution pipeline, please see our [recipes documentation](/concepts-and-explanations/recipes.md)
* If you want to write your own imperative recipe, please check out the [writing a Java refactoring recipe](/authoring-recipes/writing-a-java-refactoring-recipe.md) guide.

0 comments on commit b8de758

Please sign in to comment.