Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add caching to CoffeeFilter, refactoring

  • Loading branch information...
commit 72b849d9bdc9ec41a158b14a64c74a3088d849cb 1 parent 0816ee9
@raymyers authored
View
1  .classpath
@@ -6,5 +6,6 @@
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
<classpathentry exported="true" kind="con" path="com.springsource.sts.gradle.classpathcontainer"/>
+ <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/>
<classpathentry kind="output" path="bin"/>
</classpath>
View
13 .project
@@ -6,13 +6,26 @@
</projects>
<buildSpec>
<buildCommand>
+ <name>org.eclipse.wst.common.project.facet.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
+ <buildCommand>
+ <name>org.eclipse.wst.validation.validationbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
</buildSpec>
<natures>
+ <nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+ <nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>com.springsource.sts.gradle.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
</natures>
</projectDescription>
View
2  README.md
@@ -1,6 +1,6 @@
# Java Coffeescript Extensions
-Utilities for Java projects using coffeescript
+Utilities for Java projects using coffeescript.
Features provided:
View
2  build.gradle
@@ -20,7 +20,7 @@ repositories {
dependencies {
compile group: 'commons-lang', name: 'commons-lang', version: '2.5'
- compile group: 'com.google.guava', name: 'guava', version: 'r06'
+ compile group: 'com.google.guava', name: 'guava', version: '10.0.1'
compile group: 'org.mozilla', name: 'rhino', version: '1.7R3'
compile group: 'javax.servlet', name: 'servlet-api', version: '2.5'
View
158 src/main/java/com/cadrlife/coffee/CoffeeFilter.java
@@ -5,11 +5,6 @@
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@@ -20,14 +15,16 @@
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
+import com.cadrlife.coffee.compile.CachingCoffeeCompiler;
import com.cadrlife.coffee.concat.CoffeescriptConcatenate;
-import com.cadrlife.coffee.jcoffeescript.JCoffeeScriptCompileException;
-import com.cadrlife.coffee.jcoffeescript.JCoffeeScriptCompiler;
+import com.cadrlife.coffee.internal.org.springframework.util.AntPathMatcher;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
+import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.common.io.CharStreams;
+import com.google.common.io.Closeables;
/**
* Filter to compile coffeescript on the fly, with concatenation support. Does
@@ -38,50 +35,30 @@
* coffeeFiles. Required. Ant-style path to all coffee files
* ex. /WEB-INF/js/*.coffee
*
- * concatenateRoot. Optional. Path to the root file to resolve dependencies and concatenate results
+ * concatenateRoot. Optional. Path to the root file to resolve dependencies and concatenate results.
+ * Must be exact path to a single file.
* ex. /WEB-INF/js/main.coffee
*
* concatenateName. Optional. Path that maps to the concatenated source code.
* ex. /js/app.js
*
- * NOT YET IMPLEMENTED: cacheUpdateSeconds. Optional. How often to force an update of the compiled coffeescript cache
- * ex. 600
*/
public class CoffeeFilter implements Filter {
private String concatenateRoot = "";
private String concatenateName = "";
private String coffeeFiles = "";
private boolean concatenationEnabled;
+ private AntPathMatcher antPathMatcher = new AntPathMatcher();
- private static final class CompiledCoffee {
- // public final Date dateCached; // Time this was put in the cache
- public final String output; // Compiled coffee
+ private CachingCoffeeCompiler compiler;
- public CompiledCoffee(Date date, String output) {
- // this.dateCached = date;
- this.output = output;
- }
- }
-
- // Regex to get the line number of the failure.
- private static final Pattern LINE_NUMBER = Pattern.compile("line ([0-9]+)");
- private static final ThreadLocal<JCoffeeScriptCompiler> compiler = new ThreadLocal<JCoffeeScriptCompiler>() {
- @Override
- protected JCoffeeScriptCompiler initialValue() {
- return new JCoffeeScriptCompiler();
- }
- };
- private Map<String, CompiledCoffee> cache; // Map of Relative Path ->
- // Compiled coffee
- // private Date lastCacheUpdate
- // = new Date(0l);
private FilterConfig filterConfig;
private ServletContext servletContext;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.servletContext = this.filterConfig.getServletContext();
- cache = new HashMap<String, CompiledCoffee>();
+ compiler = new CachingCoffeeCompiler();
coffeeFiles = filterConfig.getInitParameter("coffeeFiles");
concatenateRoot = filterConfig.getInitParameter("concatenateRoot");
concatenateName = filterConfig.getInitParameter("concatenateName");
@@ -97,53 +74,60 @@ public void destroy() {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
- if (!isEnabled()) {
- chain.doFilter(request, response);
- return;
- }
HttpServletRequest httpReq = (HttpServletRequest) request;
String requestURI = httpReq.getRequestURI();
ServletContext servletContext = filterConfig.getServletContext();
String contextPath = servletContext.getContextPath();
+ if (!isEnabled() || !requestURI.endsWith(".js")) {
+ chain.doFilter(request, response);
+ return;
+ }
if (requestURI.startsWith(contextPath)) {
requestURI = requestURI.substring(contextPath.length());
}
- String coffeeRequestURI = requestURI;
- if (requestURI.endsWith(".js")) {
- coffeeRequestURI = requestURI.substring(0, requestURI.length() - 3)
- + ".coffee";
- }
- InputStream stream = servletContext.getResourceAsStream("/WEB-INF"
- + coffeeRequestURI);
- if (concatenationEnabled && requestURI.equals(concatenateName)) {
+ String coffeeRequestURI = requestURI.substring(0, requestURI.length() - 3) + ".coffee";
+ if (concatenationEnabled && requestURI.equals(concatenateName) && concatRootExists(servletContext)) {
response.setContentType("text/javascript");
- String coffee = concatenateResourcesAsString(
- rootCoffeePaths(), allCoffeePaths());
- String compiledCoffee = compileCoffeescript(coffeeRequestURI, coffee);
+ Supplier<String> coffeeSupplier = concatenateResourcesSupplier();
+ String compiledCoffee = compiler.compile(requestURI, coffeeSupplier);
response.getOutputStream().print(compiledCoffee);
- cache.put(requestURI,
- new CompiledCoffee(new Date(), compiledCoffee));
return;
}
- if (stream == null) {
+ String resourcePath = "/WEB-INF" + coffeeRequestURI;
+ if (!antPathMatcher.match(coffeeFiles, resourcePath)) {
chain.doFilter(request, response);
return;
}
- response.setContentType("text/javascript");
- // Check the cache.
- CompiledCoffee cc = cache.get(requestURI);
- if (cc != null
- // && cc.sourceLastModified.equals(file.lastModified())
- ) {
- response.getOutputStream().print(cc.output);
+
+ URL resourceUrl = servletContext.getResource(resourcePath);
+ if (resourceUrl == null) {
+ chain.doFilter(request, response);
return;
}
- // Compile the coffee and return.
- String coffee = CharStreams.toString(new InputStreamReader(stream));
- String compiledCoffee = compileCoffeescript(coffeeRequestURI, coffee);
- response.getOutputStream().print(compiledCoffee);
- cache.put(requestURI, new CompiledCoffee(new Date(), compiledCoffee));
- // Render a nice error page?
+
+ response.setContentType("text/javascript");
+ response.getOutputStream().print(compiler.compile(requestURI, urlAsStringSupplier(resourceUrl)));
+
+ }
+
+ private Supplier<String> urlAsStringSupplier(final URL resourceUrl) {
+ return new Supplier<String>() {
+ public String get() {
+ try {
+ InputStream in = resourceUrl.openStream();
+ Closeables.closeQuietly(in);
+ String result = CharStreams.toString(new InputStreamReader(in));
+ return result;
+ } catch (IOException e) {
+ throw new RuntimeException();
+ }
+ }
+ };
+ }
+
+ private boolean concatRootExists(ServletContext servletContext)
+ throws MalformedURLException {
+ return null != servletContext.getResource(concatenateRoot);
}
/*
@@ -154,16 +138,6 @@ protected boolean isEnabled() {
return true;
}
- private String compileCoffeescript(String requestURI, String coffee) {
- try {
- return getCompiler().compile(coffee);
- } catch (JCoffeeScriptCompileException e) {
- e.printStackTrace();
- throw new CompilationException(requestURI, coffee,
- e.getMessage(), getLineNumber(e), -1, -1);
- }
- }
-
private Iterable<String> rootCoffeePaths()
throws IOException {
ServletContextPatternResolver resolver = new ServletContextPatternResolver(
@@ -176,20 +150,27 @@ private String compileCoffeescript(String requestURI, String coffee) {
throws IOException {
ServletContextPatternResolver resolver = new ServletContextPatternResolver(
servletContext);
- return resolver.getResourcePaths(concatenateRoot);
+ return resolver.getResourcePaths(coffeeFiles);
}
- private String concatenateResourcesAsString(
- Iterable<String> rootResources,
- Iterable<String> includeResources) throws IOException {
- Iterable<VirtualFile> rootFiles = resourcesToFiles(rootResources);
- Iterable<VirtualFile> includeFiles = resourcesToFiles(includeResources);
- return concatenateFilesAsString(rootFiles, includeFiles);
+ private Supplier<String> concatenateResourcesSupplier() throws IOException {
+ return new Supplier<String> (){
+ public String get() {
+ try {
+ Iterable<VirtualFile> rootFiles = resourcesToFiles(rootCoffeePaths());
+ Iterable<VirtualFile> includeFiles = resourcesToFiles(allCoffeePaths());
+ return concatenateFilesAsString(rootFiles, includeFiles);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
}
private String concatenateFilesAsString(Iterable<VirtualFile> rootFiles,
Iterable<VirtualFile> includeFiles) throws IOException {
- return new CoffeescriptConcatenate().concatenate(rootFiles, includeFiles);
+ return new CoffeescriptConcatenate().concatenate(rootFiles, includeFiles);
}
private Iterable<VirtualFile> resourcesToFiles(Iterable<String> rootResources) {
@@ -210,20 +191,5 @@ public VirtualFile apply(String path) {
return rootFiles;
}
- /**
- * @return the line number that the exception happened on, or 0 if not found
- * in the message.
- */
- public static int getLineNumber(JCoffeeScriptCompileException e) {
- Matcher m = LINE_NUMBER.matcher(e.getMessage());
- if (m.find()) {
- return Integer.parseInt(m.group(1));
- }
- return 0;
- }
-
- public static JCoffeeScriptCompiler getCompiler() {
- return compiler.get();
- }
}
View
90 src/main/java/com/cadrlife/coffee/compile/CachingCoffeeCompiler.java
@@ -0,0 +1,90 @@
+package com.cadrlife.coffee.compile;
+
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.cadrlife.coffee.jcoffeescript.JCoffeeScriptCompileException;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+
+/*
+ * Caches by both source code and filename. Will recompile when the source code has changed.
+ * Stores up to 100 files by default. This can be changed by passing a custom CacheBuilder to the constructor.
+ */
+public class CachingCoffeeCompiler {
+ public static class CacheOptions {
+ public int maxSize = 100;
+ public int expirationTime = 10;
+ public TimeUnit expirationTimeUnit = TimeUnit.MINUTES;
+ }
+ private final Cache<CompilationCacheFilenameKey, String> cache;
+
+ // Regex to get the line number of the failure.
+ private static final Pattern LINE_NUMBER = Pattern.compile("line ([0-9]+)");
+ private ThreadSafeCoffeeScriptCompiler compiler;
+
+ public CachingCoffeeCompiler() {
+ this(new CacheOptions());
+ }
+
+ public CachingCoffeeCompiler(CacheOptions cacheOptions) {
+ this(cacheOptions, new ThreadSafeCoffeeScriptCompiler());
+ }
+
+ CachingCoffeeCompiler(CacheOptions cacheOptions, ThreadSafeCoffeeScriptCompiler compiler) {
+ this.compiler = compiler;
+ this.cache = CacheBuilder.newBuilder()
+ .maximumSize(cacheOptions.maxSize)
+ .expireAfterWrite(cacheOptions.expirationTime, cacheOptions.expirationTimeUnit)
+ .build(new CoffeeCacheLoader());
+ }
+
+
+ /*
+ * This call will return the cached version if it exists, otherwise will
+ * block until compiler finishes. Will only invoke the supplier as needed.OsOs
+ */
+ public String compile(String requestURI, Supplier<String> stringSupplier) {
+ CompilationCacheFilenameKey key = new CompilationCacheFilenameKey();
+ key.filename = requestURI;
+ key.sourceCodeSupplier = stringSupplier;
+ return cache.getUnchecked(key);
+ }
+
+ public String compile(String requestURI, String coffee) {
+ return compile(requestURI, Suppliers.ofInstance(coffee));
+ }
+
+ /**
+ * @return the line number that the exception happened on, or 0 if not found
+ * in the message.
+ */
+ public static int getLineNumber(JCoffeeScriptCompileException e) {
+ Matcher m = LINE_NUMBER.matcher(e.getMessage());
+ if (m.find()) {
+ return Integer.parseInt(m.group(1));
+ }
+ return 0;
+ }
+
+ private final class CoffeeCacheLoader extends CacheLoader<CompilationCacheFilenameKey, String> {
+ @Override
+ public String load(CompilationCacheFilenameKey request) throws Exception {
+ String sourceCode = request.sourceCodeSupplier.get();
+ try {
+ System.out.println("cmp " + request.filename);
+ return compiler.compile(sourceCode);
+ } catch (JCoffeeScriptCompileException e) {
+ e.printStackTrace();
+ throw new CompilationException(request.filename,
+ sourceCode, e.getMessage(), getLineNumber(e),
+ -1, -1);
+ }
+ }
+ }
+
+}
View
34 src/main/java/com/cadrlife/coffee/compile/CompilationCacheFilenameKey.java
@@ -0,0 +1,34 @@
+package com.cadrlife.coffee.compile;
+
+import com.google.common.base.Supplier;
+
+class CompilationCacheFilenameKey {
+ String filename;
+ Supplier<String> sourceCodeSupplier;
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((filename == null) ? 0 : filename.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ CompilationCacheFilenameKey other = (CompilationCacheFilenameKey) obj;
+ if (filename == null) {
+ if (other.filename != null)
+ return false;
+ } else if (!filename.equals(other.filename))
+ return false;
+ return true;
+ }
+
+}
View
3  ...cadrlife/coffee/CompilationException.java → .../coffee/compile/CompilationException.java
@@ -1,8 +1,7 @@
-package com.cadrlife.coffee;
+package com.cadrlife.coffee.compile;
import java.util.Arrays;
import java.util.List;
-//import play.vfs.VirtualFile;
/**
* A java compilation error
View
20 src/main/java/com/cadrlife/coffee/compile/ThreadSafeCoffeeScriptCompiler.java
@@ -0,0 +1,20 @@
+package com.cadrlife.coffee.compile;
+
+import com.cadrlife.coffee.jcoffeescript.JCoffeeScriptCompileException;
+import com.cadrlife.coffee.jcoffeescript.JCoffeeScriptCompiler;
+
+/*
+ * Thread-safe wrapper around JCoffeeScriptCompiler.
+ */
+public class ThreadSafeCoffeeScriptCompiler {
+ private static ThreadLocal<JCoffeeScriptCompiler> compiler = new ThreadLocal<JCoffeeScriptCompiler>() {
+ protected JCoffeeScriptCompiler initialValue() {
+ return new JCoffeeScriptCompiler();
+ };
+ };
+
+ public String compile(String coffee) throws JCoffeeScriptCompileException {
+ return compiler.get().compile(coffee);
+ }
+
+}
View
68 src/main/java/com/cadrlife/coffee/concat/CoffeescriptConcatenate.java
@@ -20,31 +20,11 @@
public class CoffeescriptConcatenate {
CoffeescriptDependencyScanner dependencyScanner = new CoffeescriptDependencyScanner();
- /* Given a list of source files,
- * create a list of all files with the classes they contain and the classes
- * those classes depend on.
- */
- public List<FileDef> mapDependencies(Iterable<VirtualFile> rootFiles, Iterable<VirtualFile> includeFiles) throws IOException {
- List<FileDef> fileDefs = Lists.newArrayList();
- for (VirtualFile sourceFile : Iterables.concat(rootFiles, includeFiles)) {
- String contents = sourceFile.readToString(Charset.defaultCharset());
- List<String> classes = dependencyScanner.findClasses(contents);
- List<String> fileDeps = dependencyScanner.findFileDependencies(contents);
- Collection<String> classDeps = dependencyScanner.findClassDependencies(contents);
- classDeps = Collections2.filter(classDeps, not(in(classes)));
- FileDef fileDef = new FileDef();
- fileDef.setName(stripCoffeeSuffix(sourceFile.getName()));
- fileDef.setClasses(classes);
- fileDef.setDependencies(Lists.newArrayList(classDeps));
- fileDef.setFileDependencies(fileDeps);
- fileDef.setContents(contents);
- fileDefs.add(fileDef);
- }
- return fileDefs;
- }
-
- private String stripCoffeeSuffix(String fileName) {
- return fileName.replaceAll("\\.coffee$", "");
+
+ public String concatenate(Iterable<VirtualFile> rootFiles, Iterable<VirtualFile> includeFiles) throws IOException {
+ List<FileDef> deps = mapDependencies(rootFiles, includeFiles);
+ String output = concatFiles(rootFiles, deps);
+ return dependencyScanner.removeIncludeDirectives(output);
}
/* Given a list of files and their class/dependency information,
@@ -60,10 +40,13 @@ public String concatFiles(Iterable<VirtualFile> sourceFiles, final List<FileDef>
sourceFileDefs.addAll(findFileDefsWithNames(fileDefs, sourceFileNames));
List<FileDef> fileDefStack = Lists.newArrayList();
+ // reuse dependencyResolver to preserve usedfiles list
+ DependencyResolver dependencyResolver = new DependencyResolver(fileDefs);
while (!sourceFileDefs.isEmpty()) {
FileDef nextFileDef = sourceFileDefs.pop();
- List<FileDef> resolvedDef = new DependencyResolver(fileDefs).resolve(nextFileDef);
+
+ List<FileDef> resolvedDef = dependencyResolver.resolve(nextFileDef);
fileDefStack.addAll(resolvedDef);
}
StringBuilder contentBuilder = new StringBuilder();
@@ -73,7 +56,34 @@ public String concatFiles(Iterable<VirtualFile> sourceFiles, final List<FileDef>
return contentBuilder.toString();
}
+
+ /* Given a list of source files,
+ * create a list of all files with the classes they contain and the classes
+ * those classes depend on.
+ */
+ public List<FileDef> mapDependencies(Iterable<VirtualFile> rootFiles, Iterable<VirtualFile> includeFiles) throws IOException {
+ List<FileDef> fileDefs = Lists.newArrayList();
+ for (VirtualFile sourceFile : Iterables.concat(rootFiles, includeFiles)) {
+ String contents = sourceFile.readToString(Charset.defaultCharset());
+ List<String> classes = dependencyScanner.findClasses(contents);
+ List<String> fileDeps = dependencyScanner.findFileDependencies(contents);
+ Collection<String> classDeps = dependencyScanner.findClassDependencies(contents);
+ classDeps = Collections2.filter(classDeps, not(in(classes)));
+ FileDef fileDef = new FileDef();
+ fileDef.setName(stripCoffeeSuffix(sourceFile.getName()));
+ fileDef.setClasses(classes);
+ fileDef.setDependencies(Lists.newArrayList(classDeps));
+ fileDef.setFileDependencies(fileDeps);
+ fileDef.setContents(contents);
+ fileDefs.add(fileDef);
+ }
+ return fileDefs;
+ }
+ private String stripCoffeeSuffix(String fileName) {
+ return fileName.replaceAll("\\.coffee$", "");
+ }
+
private Collection<FileDef> findFileDefsWithNames(List<FileDef> fileDefs,
final Iterable<String> sourceFileNames) {
return Collections2.filter(fileDefs, new Predicate<FileDef>() {
@@ -94,10 +104,6 @@ public String apply(VirtualFile f) {
return sourceFileNames;
}
- public String concatenate(Iterable<VirtualFile> rootFiles, Iterable<VirtualFile> includeFiles) throws IOException {
- List<FileDef> deps = mapDependencies(rootFiles, includeFiles);
- String output = concatFiles(rootFiles, deps);
- return dependencyScanner.removeDirectives(output);
- }
+
}
View
2  src/main/java/com/cadrlife/coffee/concat/CoffeescriptDependencyScanner.java
@@ -74,7 +74,7 @@
/*
* Remove all '#=require' directives, used after the files have been included.
*/
- public String removeDirectives(String input) {
+ public String removeIncludeDirectives(String input) {
String strippedInput = fileDirectivePattern.matcher(input).replaceAll("");
return classDirectivePattern.matcher(strippedInput).replaceAll("");
}
View
3  src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
View
68 src/test/java/com/cadrlife/coffee/compile/CachingCoffeeCompilerTest.java
@@ -0,0 +1,68 @@
+package com.cadrlife.coffee.compile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import com.google.common.base.Supplier;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CachingCoffeeCompilerTest {
+
+ @Mock
+ ThreadSafeCoffeeScriptCompiler compiler;
+
+ @Mock
+ Supplier<String> sourceSupplier1;
+
+ @Mock
+ Supplier<String> sourceSupplier2;
+
+ @InjectMocks
+ CachingCoffeeCompiler cachingCompiler = new CachingCoffeeCompiler();
+
+
+ @Before
+ public void setup() throws Exception {
+ when(sourceSupplier1.get()).thenReturn("coffee1");
+ when(sourceSupplier2.get()).thenReturn("coffee1");
+ when(compiler.compile("coffee1")).thenReturn("js1");
+ when(compiler.compile("coffee2")).thenReturn("js2");
+ }
+
+ @Test
+ public void shouldCompile() throws Exception {
+ assertEquals("js1", cachingCompiler.compile("", sourceSupplier1));
+ }
+
+ @Test
+ public void shouldCompileOnlyOnceForSameFilename() throws Exception {
+ when(compiler.compile("coffee")).thenReturn("js");
+ assertEquals("js1", cachingCompiler.compile("uri", sourceSupplier1));
+ assertEquals("js1", cachingCompiler.compile("uri", sourceSupplier2));
+ verify(compiler, times(1)).compile("coffee1");
+ verify(compiler, never()).compile("coffee2");
+ verify(sourceSupplier1, times(1)).get();
+ verify(sourceSupplier2, never()).get();
+ }
+
+ @Test
+ public void recompileOnFilenameChange() throws Exception {
+ when(compiler.compile("coffee")).thenReturn("js");
+ assertEquals("js", cachingCompiler.compile("uri", "coffee"));
+ assertEquals("js", cachingCompiler.compile("uri2", "coffee"));
+ assertEquals("js", cachingCompiler.compile("uri2", "coffee"));
+ verify(compiler, times(2)).compile("coffee");
+ }
+
+}
View
11 ...coffee/concat/CoffeescriptConcatTest.java → ...e/concat/CoffeescriptConcatenateTest.java
@@ -13,7 +13,7 @@
import static org.junit.Assert.assertEquals;
-public class CoffeescriptConcatTest {
+public class CoffeescriptConcatenateTest {
CoffeescriptConcatenate concat;
List<VirtualFile> rootFiles;
@@ -57,13 +57,20 @@ public void shouldCompileOneFileToFilesContents() throws Exception {
}
@Test
- public void shouldNotIncludeUnneedFiles() throws Exception {
+ public void shouldNotIncludeUnneededFiles() throws Exception {
rootFiles.add(noDeps);
includeFiles.add(animal);
assertEquals(noDepsString.trim(), concat().trim());
}
@Test
+ public void shouldNotIncludeRootFileTwice() throws Exception {
+ rootFiles.add(noDeps);
+ includeFiles.add(noDeps);
+ assertEquals(noDepsString.trim(), concat().trim());
+ }
+
+ @Test
public void shouldIncludeClassDependecy() throws Exception {
rootFiles.add(snake);
includeFiles.add(animal);
Please sign in to comment.
Something went wrong with that request. Please try again.