Browse files

Merge pull request #1 from melezov/master

Refactory!
  • Loading branch information...
2 parents bd57b9d + 3a1f597 commit 6f62dd334d16bc65a5a0761f91049030758fb695 @AnaMarjanica committed Apr 6, 2012
View
1 README
@@ -0,0 +1 @@
+Transliteration & sanitizing utilities for URL slugs.
View
4 Sluggifier/continuous-compilation.sh
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-echo Entering continuous compilation loop ...
-`dirname $0`/sbt.sh --loop --no-jrebel "$@" ~compile
View
4 Sluggifier/open-console.sh
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-echo Firing up the Scala REPL ...
-`dirname $0`/sbt.sh "$@" console
View
6 Sluggifier/sbt.sh
@@ -1,6 +0,0 @@
-#!/bin/bash
-
-PROJECT_DIR=`dirname $(readlink -f $0)`
-PARENT=`basename $PROJECT_DIR`
-
-$PROJECT_DIR/../sbt.sh "project $PARENT" "$@"
View
38 Sluggifier/src/main/scala/hr/element/sluggifier/Sluggify.scala
@@ -1,38 +0,0 @@
-package hr.element.sluggifier
-
-import com.ibm.icu.text.Transliterator
-
-
-
-object Sluggify {
- def makeSlug(txt: String, replacement: String) = {
- //convert latin letters to ASCII (ex. đ->d)
- val txtTrans = transliterate(txt)
- //convert all to lower case
- val txtLower = txtTrans.toLowerCase
- //replace all unsafe and reserved characters
- convertCharacters(txtLower, replacement)
- }
-
- def transliterate(s: String) = {
- Transliterator.getInstance(TransliterateRules.latinToASCII).transliterate(s)
- }
-
-/*
- * URL unsafe characters list
- * reserved: URLs use some characters for special use in defining their syntax.
- * reserved: $ & + , / : ; = ? @
- * unsafe: Some characters present the possibility of being misunderstood within URLs
- * unsafe: space # % { } | \ ^ ~ [ ] ' "
- */
- def convertCharacters(txt: String, replacement: String) = {
- convertReservedCharacters(convertUnsafeCharacters(txt, replacement), replacement)
- }
-
- private def convertReservedCharacters(txt: String, replacement: String) =
- txt.replaceAll("[$,&,+,,,/,:,;,=,?,@]", replacement)
-
- private def convertUnsafeCharacters(txt: String, replacement: String) =
- txt.replaceAll("""[ ,#,%,{,},|,\\,^,~,[,],']""", replacement)
-
-}
View
36 build.sbt
@@ -0,0 +1,36 @@
+organization := "hr.element.etb"
+
+name := "etb-slug"
+
+version := "0.0.1"
+
+// ### Build settings ###
+
+libraryDependencies ++= Seq(
+ "com.ibm.icu" % "icu4j" % "49.1"
+, "org.scalatest" %% "scalatest" % "1.7.1" % "test"
+)
+
+crossScalaVersions := Seq("2.9.1-1", "2.9.1", "2.9.0-1", "2.9.0")
+
+scalaVersion <<= (crossScalaVersions)(_.head)
+
+scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "UTF-8", "-optimise")
+
+unmanagedSourceDirectories in Compile <<= (scalaSource in Compile)( _ :: Nil)
+
+unmanagedSourceDirectories in Test <<= (scalaSource in Test )( _ :: Nil)
+
+
+// ### Publishing ###
+
+publishTo := Some("Element Releases" at "http://maven.element.hr/nexus/content/repositories/releases/")
+
+credentials += Credentials(Path.userHome / ".publish" / "element.credentials")
+
+publishArtifact in (Compile, packageDoc) := false
+
+
+// ### Misc ###
+
+initialCommands := "import hr.element.doit.slug._"
View
4 continuous-compilation.sh
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-echo Entering continuous compilation loop ...
-`dirname $0`/sbt.sh --loop --no-jrebel "$@" ~compile
View
5 prepare-workspace.bat
@@ -0,0 +1,5 @@
+@echo off
+
+echo Performing dependency update for all projects ...
+echo Will also create Eclipse .project and .classpath files ...
+call "%~dp0\sbt.bat" --no-jrebel %* update eclipse
View
15 project/Dependencies.scala
@@ -1,15 +0,0 @@
-import sbt._
-import Keys._
-
-import Helpers._
-import Dependencies._
-
-object Dependencies {
- val scalatest = "org.scalatest" %% "scalatest" % "1.7.1" % "test"
-
- val icu4j = "com.ibm.icu" % "icu4j" % "2.6.1"
-
-
-
-
-}
View
160 project/SharedSettings.scala
@@ -1,160 +0,0 @@
-import sbt._
-import Keys._
-
-import Helpers._
-import Dependencies._
-
-object InitialCommands {
- val settings = Seq(
- )
-}
-
-
-// ---------------------------------------------------------------------------
-
-object Default {
- val settings =
- Defaults.defaultSettings ++
- Resolvers.settings ++
- Publishing.settings ++
- Format.settings ++ Seq(
- organization := "hr.element.sluggifier",
- crossScalaVersions := Seq("2.9.1", "2.9.0-1", "2.9.0"),
- scalaVersion <<= (crossScalaVersions) { versions => versions.head },
- scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "UTF-8", "-optimise", "-Yrepl-sync"),
- javacOptions := Seq("-deprecation", "-encoding", "UTF-8", "-source", "1.6", "-target", "1.6"),
- unmanagedSourceDirectories in Compile <<= (scalaSource in Compile)( _ :: Nil),
- unmanagedSourceDirectories in Test <<= (scalaSource in Test )( _ :: Nil)
- )
-
-
-}
-
-// ---------------------------------------------------------------------------
-
-object Repositories {
- val ElementNexus = "Element Nexus" at "http://maven.element.hr/nexus/content/groups/public/"
- val ElementReleases = "Element Releases" at "http://maven.element.hr/nexus/content/repositories/releases/"
- val ElementSnapshots = "Element Snapshots" at "http://maven.element.hr/nexus/content/repositories/snapshots/"
- val Maven2 = "Maven2" at "http://repo2.maven.org/maven2/"
-
-
-}
-
-// ---------------------------------------------------------------------------
-
-object Resolvers {
- import Repositories._
-
- val settings = Seq(
- resolvers := Seq(
- ElementNexus
- , ElementReleases
- , ElementSnapshots
- , Maven2
- ),
- externalResolvers <<= resolvers map { rs =>
- Resolver.withDefaultResolvers(rs, mavenCentral = true, scalaTools = false)
- }
- )
-}
-
-// ---------------------------------------------------------------------------
-
-object Publishing {
- import Repositories._
-
- val settings = Seq(
- publishTo <<= (version) { version => Some(
- if (version.endsWith("SNAPSHOT")) ElementSnapshots else ElementReleases
- )},
- credentials += Credentials(Path.userHome / ".publish" / "element.credentials"),
- publishArtifact in (Compile, packageSrc) := false,
- publishArtifact in (Compile, packageDoc) := false
- )
-}
-
-// ---------------------------------------------------------------------------
-
-object Format {
- // Scalariform plugin
- import com.typesafe.sbtscalariform._
- import ScalariformPlugin._
- import scalariform.formatter.preferences._
-
- ScalariformKeys.preferences := FormattingPreferences()
- .setPreference(AlignParameters, false)
- .setPreference(AlignSingleLineCaseStatements, false)
- .setPreference(AlignSingleLineCaseStatements.MaxArrowIndent, 40)
- .setPreference(CompactControlReadability, false)
- .setPreference(CompactStringConcatenation, false)
- .setPreference(DoubleIndentClassDeclaration, true)
- .setPreference(FormatXml, false)
- .setPreference(IndentLocalDefs, false)
- .setPreference(IndentPackageBlocks, false)
- .setPreference(IndentSpaces, 2)
- .setPreference(IndentWithTabs, false)
- .setPreference(MultilineScaladocCommentsStartOnFirstLine, true)
- .setPreference(PlaceScaladocAsterisksBeneathSecondAsterisk, true)
- .setPreference(PreserveDanglingCloseParenthesis, false)
- .setPreference(PreserveSpaceBeforeArguments, false)
- .setPreference(RewriteArrowSymbols, false)
- .setPreference(SpaceBeforeColon, false)
- .setPreference(SpaceInsideBrackets, false)
- .setPreference(SpaceInsideParentheses, false)
- .setPreference(SpacesWithinPatternBinders, true)
-
- lazy val settings =
- ScalariformPlugin.defaultScalariformSettings
-}
-
-// ---------------------------------------------------------------------------
-
-object Helpers {
- implicit def depToFunSeq(m: ModuleID) = Seq((_: String) => m)
- implicit def depFunToSeq(fm: String => ModuleID) = Seq(fm)
- implicit def depSeqToFun(mA: Seq[ModuleID]) = mA.map(m => ((_: String) => m))
- implicit def warName2SMA(name: String) = (_: String, _: ModuleID, _: Artifact) => name
-
-// ---------------------------------------------------------------------------
-
- def top(
- title: String,
- projectAggs: Seq[sbt.ProjectReference] = Seq()) =
- Project(
- title,
- file("."),
- settings = Default.settings ++ Seq(
- name := title,
- version := "0.0.0"
- )
- ) aggregate(projectAggs: _*)
-
- def parent(
- title: String,
- projectAggs: Seq[sbt.ProjectReference] = Seq()) =
- Project(
- title,
- file(title.replace('-', '/')),
- settings = Default.settings ++ Seq(
- name := "Sluggifier-" + title,
- version := "0.0.0"
- )
- ) aggregate(projectAggs: _*)
-
- def project(
- title: String,
- ver: String,
- deps: Seq[Seq[String => ModuleID]] = Seq(),
- projectDeps: Seq[ClasspathDep[ProjectReference]] = Seq()) =
- Project(
- title,
- file(title.replace('-', '/')),
- settings = Default.settings ++ Seq(
- name := "Sluggifier-" + title,
- version := ver
- ) :+ (libraryDependencies <++= scalaVersion( sV =>
- for (depSeq <- deps; dep <- depSeq) yield dep(sV))
- )
- ) dependsOn(projectDeps: _*)
-}
View
18 project/Sluggifier.scala
@@ -1,18 +0,0 @@
-import sbt._
-import Keys._
-
-import Helpers._
-import Dependencies._
-
-object Sluggifier extends Build{
- lazy val sluggifier = project(
- "Sluggifier",
- "0.0.1",
- Seq(
- icu4j
- ),
- Seq(
- )
- )
-}
-
View
22 project/plugins.sbt
@@ -8,24 +8,4 @@
resolvers += Classpaths.typesafeResolver
-addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0-RC1")
-
-// +-------------------------------------------------------------------------------------+
-// | SBT Scalariform (https://github.com/typesafehub/sbtscalariform) |
-// | Performs source code formatting |
-// | |
-// | See also: Scalariform reference (http://mdr.github.com/scalariform/) |
-// +-------------------------------------------------------------------------------------+
-
-resolvers += Classpaths.typesafeResolver
-
-addSbtPlugin("com.typesafe.sbtscalariform" % "sbtscalariform" % "0.3.0")
-
-resolvers:= Seq("Apache Maven" at "http://mvnrepository.com/artifact/")
-
-externalResolvers <<= resolvers map { rs =>
- Resolver.withDefaultResolvers(rs, mavenCentral = true, scalaTools = false)
-}
-
-
-
+addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0")
View
75 sbt.bat
@@ -0,0 +1,75 @@
+@echo off
+setlocal
+pushd
+cd "%~dp0"
+
+set JVM_PARAMS=-Xss2m -Xmx712m -XX:MaxPermSize=256m -XX:+CMSClassUnloadingEnabled
+
+set LIFT_RUN_MODE=-Drun.mode=development
+set TRY_JREBEL=true
+set LOG_LEVEL=
+set NO_PAUSE=false
+set DO_LOOP=false
+
+:PARSER_LOOP
+if "%~1"=="" goto :PARSER_END
+
+if "%~1"=="--prod" (
+ set LIFT_RUN_MODE=-Drun.mode=production
+ goto :PARSER_CONTINUE
+)
+
+if "%~1"=="--debug" (
+ set LOG_LEVEL="set logLevel:=Level.Debug"
+ goto :PARSER_CONTINUE
+)
+
+if "%~1"=="~lift" (
+ set SBT_PARAMS=%SBT_PARAMS% container:start ~compile container:stop
+ set JREBEL_PLUGINS=%JREBEL_PLUGINS% -Drebel.lift_plugin=true
+ goto :PARSER_CONTINUE
+)
+
+if "%~1"=="--no-jrebel" (
+ set TRY_JREBEL=false
+ goto :PARSER_CONTINUE
+)
+
+if "%~1"=="--loop" (
+ set DO_LOOP=true
+ goto :PARSER_CONTINUE
+)
+
+if "%~1"=="--no-pause" (
+ set NO_PAUSE=true
+ goto :PARSER_CONTINUE
+)
+
+set SBT_PARAMS=%SBT_PARAMS% %1
+
+:PARSER_CONTINUE
+shift
+goto :PARSER_LOOP
+:PARSER_END
+
+set JVM_PARAMS=%JVM_PARAMS% %LIFT_RUN_MODE%
+if %TRY_JREBEL%.==true. (
+ if exist "%JREBEL_HOME%\jrebel.jar" set JVM_PARAMS=%JVM_PARAMS% -noverify -javaagent:"%JREBEL_HOME%\jrebel.jar" %JREBEL_PLUGINS%
+)
+
+set GRUJ_PATH=project\strap\gruj_vs_sbt-launch-0.11.2.jar
+set RUN_CMD=java %JVM_PARAMS% -jar %GRUJ_PATH% %LOG_LEVEL% %SBT_PARAMS%
+
+:RUN_LOOP
+%RUN_CMD%
+
+if %DO_LOOP%.==true. (
+ if %NO_PAUSE%.==false. (
+ echo Press Enter to continue or Press CTRL+C to exit!
+ pause
+ )
+ goto :RUN_LOOP
+)
+
+popd
+endlocal
View
44 src/main/scala/hr/element/etb/slug/Sluggify.scala
@@ -0,0 +1,44 @@
+package hr.element.etb.slug
+
+import com.ibm.icu.text.Transliterator
+import java.util.regex.Pattern
+
+object Sluggifier {
+ protected val URLUnsafeChars =
+ "[^-._a-z0-9]+"
+
+ private val default =
+ new Sluggifier(TransliterateRules.latinToASCII, "-")
+
+ def apply(text: String) = default(text)
+}
+
+case class Sluggifier(transRules: String, replacement: String) {
+ private val Trans = Transliterator.getInstance(transRules)
+
+ private val URLUnsafeReplacePattern =
+ "(%s|%s)+" format(Sluggifier.URLUnsafeChars, Pattern.quote(replacement)) r
+
+ private val URLUnsafeTrimPattern =
+ "^%s|%1$s$" format(Sluggifier.URLUnsafeChars) r
+
+ // convert latin letters to ASCII (ex. đ->d)
+ protected val transliterate =
+ Trans.transliterate(_: String)
+
+ // convert all to lower case
+ protected val lowerCase =
+ (_: String).toLowerCase
+
+ // trim all URL non-safe characters from the beginning and the end
+ protected val trim =
+ URLUnsafeTrimPattern.replaceAllIn(_: String, "")
+
+ // replace all URL non-safe characters
+ protected val sanitize =
+ URLUnsafeReplacePattern.replaceAllIn(_: String, replacement)
+
+ // do all from above
+ def apply(text: String) =
+ (transliterate andThen lowerCase andThen trim andThen sanitize)(text)
+}
View
11 ...ement/sluggifier/TransliterateRules.scala → ...element/etb/slug/TransliterateRules.scala
@@ -1,8 +1,13 @@
-package hr.element.sluggifier
+package hr.element.etb.slug
object TransliterateRules {
- /* more rules available at:
+ /* More rules available at:
* http://www.icu-project.org/icu-bin/translit?TEMPLATE_FILE=data/translit_rule_main.html
*/
val latinToASCII = "Latin-ASCII"
-}
+
+ /*
+ * ICU4J FAQ available at:
+ * http://userguide.icu-project.org/icufaq/icu4j-faq
+ */
+}
View
38 src/test/scala/hr/element/etb/slug/SluggifySpec.scala
@@ -0,0 +1,38 @@
+package hr.element.etb.slug
+package test
+
+import org.scalatest._
+import org.scalatest.matchers._
+
+class SluggifySpec extends FeatureSpec with GivenWhenThen with MustMatchers {
+
+ feature("Sluggify URL sanitization"){
+ info("Sluggify must convert every evil character combination into a pretty URL-safe string")
+
+ scenario("Uppercase character lowercasing"){
+ val in = 'A' to 'Z' mkString;
+ given ("a string containing uppercase letters: %s" format in)
+ val res = in.toLowerCase
+ then ("it should return a string with no uppercase characters: %s" format res)
+ val out = Sluggifier(in)
+ out must equal (res)
+ }
+
+ scenario("URL unsafe character removal"){
+ val in = """!"#$%&'()*+,/:;<=>?@[\]^`{|}~""" mkString;
+ given ("a string containing only non-safe characters: %s" format in)
+ then ("it should return an empty string.")
+ val out = Sluggifier(in)
+ out must be ('empty)
+ }
+
+ scenario("URL unsafe character trimming"){
+ val in = """~ Dinamo ! Is the best! ~""" mkString;
+ given ("an URL unsafe string: %s" format in)
+ val res = "dinamo-is-the-best"
+ then ("the resulting slug must be trimmed: %s" format res)
+ val out = Sluggifier(in)
+ out must equal (res)
+ }
+ }
+}
View
4 test-loop.bat
@@ -0,0 +1,4 @@
+@echo off
+
+echo Entering continuous test loop ...
+call "%~dp0\sbt.bat" --loop --no-jrebel %* ~test
View
4 test-loop.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+echo Entering continuous test loop ...
+`dirname $0`/sbt.sh --loop --no-jrebel "$@" ~test

0 comments on commit 6f62dd3

Please sign in to comment.