Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 116 Name and Reuse Repeating Conditions #131

Merged
merged 24 commits into from
May 28, 2019
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e275f1b
#116 First working version
Oct 30, 2018
bf1f21c
#116 testing travis tests
Oct 30, 2018
b1183b4
#116 cleaning up
Oct 30, 2018
e7eaede
#116 cleaning up
Oct 30, 2018
980e5e3
#116 updated description
Oct 30, 2018
ef16c7e
#116 updated description
Oct 30, 2018
add2194
#116 formatting
Oct 30, 2018
4b3c180
Merge remote-tracking branch 'origin/issue-63' into issue-116
Nov 2, 2018
b00acc0
Merge pull request #132 from Cognifide/master
chutch Nov 5, 2018
1e95bd4
#116 added another test case - using condition more than once
leetcode-user Nov 5, 2018
8607d31
Merge remote-tracking branch 'origin/master' into issue-116
Nov 6, 2018
3dbf928
Merge branch 'issue-116' of https://github.com/Cognifide/AEM-Rules-fo…
Nov 6, 2018
9bf6f8c
#116 formatting
Nov 6, 2018
79116cd
#116 New approach
Nov 6, 2018
cb4e942
#116 Updated description
Nov 6, 2018
e712735
#116 Updated thing mentioned in pull request and changed few details.
Nov 7, 2018
5f2ecfb
Merge remote-tracking branch 'origin/master' into issue-116
leetcode-user Nov 7, 2018
4e13818
#116 reorganised main readme
leetcode-user Nov 7, 2018
8e1877b
Merge remote-tracking branch 'origin/master' into issue-116
May 23, 2019
55e8072
#116 Refactoring and answering PR comments.
May 23, 2019
de0a612
#116 Updated number of HTL rules.
May 23, 2019
8e9ff91
#116 Rule number changed from 5 to 4 as there is no rule #4
May 23, 2019
8afaf88
Merge remote-tracking branch 'origin/master' into issue-116
May 27, 2019
a8b8904
#116 File renamed to match rule number.
May 27, 2019
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ gradlew sonarQube -DsonarRunner.aemVersion=6.4

Below you will find descriptions of all rules available in **AEM Rules for SonarQube** plugin.

- **HTL-5** Name and re-use Repeating Conditions
chutch marked this conversation as resolved.
Show resolved Hide resolved
- Consider caching data-sly-test conditions. Additionally, this reduces code duplication.

## Good practices

- **AEM-1** Use predefined constant in annotation instead of hardcoded value.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*-
* #%L
* AEM Rules for SonarQube
* %%
* Copyright (C) 2015-2018 Cognifide Limited
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.cognifide.aemrules.htl.checks;

import com.cognifide.aemrules.htl.api.ParsingErrorRule;
import com.cognifide.aemrules.metadata.Metadata;
import com.cognifide.aemrules.tag.Tags;
import com.cognifide.aemrules.version.AemVersion;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.sling.scripting.sightly.compiler.expression.Expression;
import org.sonar.check.Priority;
import org.sonar.check.Rule;
import org.sonar.plugins.html.node.Attribute;
import org.sonar.plugins.html.node.TagNode;

@Rule(
key = NamingAndReusingConditionsCheck.RULE_KEY,
name = NamingAndReusingConditionsCheck.RULE_MESSAGE,
priority = Priority.INFO,
tags = Tags.AEM
)
@AemVersion(
all = true
)
@Metadata(
technicalDebt = "10min"
)
@ParsingErrorRule
public class NamingAndReusingConditionsCheck extends AbstractHtlCheck {

static final String RULE_KEY = "HTL-5";

static final String RULE_MESSAGE = "Consider caching data-sly-test conditions";

private static final String SLY_TEST = "data-sly-test";

private Set<String> unnamedConditions = new HashSet<>();

private Set<String> namedConditions = new HashSet<>();

@Override
public void startHtlElement(List<Expression> expressions, TagNode node) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This API has to be rewritten as it decouples expression from the attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@piotr-wilczynski You should take a look at this :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is giving You all of the expressions that exists in node, but if You want the have relation between attribute and expresion You can always check if there is any expression in the attribute and proceed somehow. There is a method called htlExpression default void htlExpression(Expression expression, Node node) It is probably what You are looking for @chutch ?

if (isConditionReused(expressions, node)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This time also, the method name is misleading since "reused" has a positive connotation whereas in your case reused means "the same condition used couple of times needlessly".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conditions inverted and method name changed to isConditionReusedCorrectly().

createViolation(node.getStartLinePosition(), RULE_MESSAGE);
}
updateConditionSets(expressions, node);
}

private boolean isConditionReused(List<Expression> expressions, TagNode node) {
String condition = expressions.stream()
.map(Expression::getRawText)
.map(text -> text.replaceAll("[${}]", ""))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please extract 'getRawText' and 'text.replaceAll("[${}]", "")' to method and reuse it in '103' line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted
private Set<String> clearExpressions(List<Expression> expressions){ return expressions.stream() .map(Expression::getRawText) .map(text -> text.replaceAll("[${}]", "")) .collect(Collectors.toSet()); }

.findFirst()
.orElse("");

return unnamedConditions.stream()
chutch marked this conversation as resolved.
Show resolved Hide resolved
.anyMatch(condition::equals) && // To see if someone is reusing not cached condition
node.getAttributes().stream()
.map(Attribute::getName)
.anyMatch(name -> name.equals(SLY_TEST)); // To see if someone is not declaring new cached condition
}

private void updateConditionSets(List<Expression> expressions, TagNode node) {
Optional<String> condition = node.getAttributes().stream()
.map(Attribute::getName)
.filter(text -> text.contains(SLY_TEST))
chutch marked this conversation as resolved.
Show resolved Hide resolved
.findFirst();
if (condition.isPresent() && !condition.get().equals(SLY_TEST)) {
chutch marked this conversation as resolved.
Show resolved Hide resolved
condition = Optional.of(condition.get().substring(14));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is 14?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

chutch marked this conversation as resolved.
Show resolved Hide resolved
namedConditions.add(condition.get());
} else {
unnamedConditions.addAll(expressions.stream()
.map(Expression::getRawText)
.map(text -> text.replaceAll("[${}]", ""))
.filter(text -> !namedConditions.contains(text))
.collect(Collectors.toSet()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
*/
package com.cognifide.aemrules.htl.rules;

import com.cognifide.aemrules.htl.checks.HtlAttributesShouldBeAtTheEndCheck;
import com.cognifide.aemrules.htl.checks.ParsingErrorCheck;
import com.cognifide.aemrules.htl.Htl;
import com.cognifide.aemrules.htl.api.HtlCheck;
import com.cognifide.aemrules.htl.checks.HtlAttributesShouldBeAtTheEndCheck;
import com.cognifide.aemrules.htl.checks.NamingAndReusingConditionsCheck;
import com.cognifide.aemrules.htl.checks.ParsingErrorCheck;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Optional;
Expand All @@ -36,7 +37,8 @@ public final class HtlCheckClasses {

private static final List<Class<? extends HtlCheck>> CLASSES = ImmutableList.of(
ParsingErrorCheck.class,
HtlAttributesShouldBeAtTheEndCheck.class
HtlAttributesShouldBeAtTheEndCheck.class,
NamingAndReusingConditionsCheck.class
);

private HtlCheckClasses() {
Expand Down
24 changes: 24 additions & 0 deletions src/main/resources/rules/HTL-5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Always try to re-use existing conditions, so the code is more readable.

== Noncompliant Code Example
```
<!--/* Bad - the same condition is evaluated multiple times */-->
<span class="uber-mode__top-bar" data-sly-test="${uberModeHelper.uberModeEnabled || forceUberMode}">
<div class="my-component">
Blah blah blah
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe ... ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

chutch marked this conversation as resolved.
Show resolved Hide resolved
</div>
</span>
<span class="uber-mode__bottom-bar" data-sly-test="${uberModeHelper.uberModeEnabled || forceUberMode}"></span>
```



== Compliant Solution
```
<span class="uber-mode__top-bar" data-sly-test.uberMode="${uberModeHelper.uberModeEnabled || forceUberMode}">
<div class="my-component">
Blah blah blah
chutch marked this conversation as resolved.
Show resolved Hide resolved
</div>
</span>
<span class="uber-mode__bottom-bar" data-sly-test="${uberMode}"></span>
```
21 changes: 0 additions & 21 deletions src/test/files/checks/htl/HtlAttributesShouldBeAtTheEndCheck.html
Original file line number Diff line number Diff line change
@@ -1,22 +1 @@
<!--
#%L
AEM Rules for SonarQube
%%
Copyright (C) 2015-2018 Cognifide Limited
%%
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#L%
-->
<div data-sly-list="${model.value}" class="list"></div> <!--/* Non-Compliant */-->
22 changes: 22 additions & 0 deletions src/test/files/checks/htl/NamingAndReusingConditionsCheck.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<span class="uber-mode__top-bar" data-sly-test="${uberModeHelper.uberModeEnabled || forceUberMode}">
<div class="my-component">
Text
</div>
</span>

<span class="uber-mode__bottom-bar" data-sly-test="${uberModeHelper.uberModeEnabled || forceUberMode}"></span> <!--/* Non-Compliant */-->

<span class="uber-mode__top-bar" data-sly-test.uberMode="${uberModeHelper.uberModeEnabled || forceUberMode}">
<div class="my-component">
Text
</div>
</span>
<span class="uber-mode__top-bar" data-sly-test="${uberMode}"></span>

<span class="uber-mode__bottom-bar" data-sly-test="${uberModeHelper.uberModeEnabled || forceUberMode}"> <!--/* Non-Compliant */-->
<div class="my-component">
Text
</div>
</span>

<span class="uber-mode__bottom-bar" data-sly-test="${uberMode}"></span>
chutch marked this conversation as resolved.
Show resolved Hide resolved
13 changes: 13 additions & 0 deletions src/test/java/com/cognifide/aemrules/htl/HtlProfileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package com.cognifide.aemrules.htl;

import static org.fest.assertions.Assertions.assertThat;

import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -42,6 +43,18 @@ public class HtlProfileTest {
public void setUp() {
profile = new HtlProfile();
this.context = new BuiltInQualityProfilesDefinition.Context();

}

@Test
public void sanity() {
profile.define(context);
Map<String, BuiltInActiveRule> activeRules = getActiveRulesByRuleKey(context);

assertThat(activeRules.size()).isEqualTo(3);

assertThat(activeRules.keySet()).contains("HTL-0");

chutch marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*-
* #%L
* AEM Rules for SonarQube
* %%
* Copyright (C) 2015-2018 Cognifide Limited
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package com.cognifide.aemrules.htl.checks;

import com.cognifide.aemrules.htl.AbstractBaseTest;
import org.junit.Test;

public class NamingAndReusingConditionsCheckTest extends AbstractBaseTest {

@Test
public void checkHtlAttributesOrder() {
check = new NamingAndReusingConditionsCheck();
filename = "src/test/files/checks/htl/NamingAndReusingConditionsCheck.html";
verify();
}
}