Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
robfletcher committed Aug 20, 2012
1 parent 54aca7a commit dd7e4f7
Show file tree
Hide file tree
Showing 16 changed files with 435 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.idea
*.iml
*.ipr
*.iws
.classpath
target
out
plugin.xml
*.zip
*.log
.project
.settings
72 changes: 72 additions & 0 deletions FeatureSwitchGrailsPlugin.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
class FeatureSwitchGrailsPlugin {
// the plugin version
def version = "0.1"
// the version or versions of Grails the plugin is designed for
def grailsVersion = "2.0 > *"
// the other plugins this plugin depends on
def dependsOn = [:]
// resources that are excluded from plugin packaging
def pluginExcludes = [
"grails-app/views/error.gsp"
]

// TODO Fill in these fields
def title = "Feature Switch Plugin"
def author = "Antony Jones and Matt Tortolani"
def authorEmail = ""
def description = 'Allows turning on and off of features'

// URL to the plugin's documentation
def documentation = "http://grails.org/plugin/feature-switch"

// Extra (optional) plugin metadata

// License: one of 'APACHE', 'GPL2', 'GPL3'
def license = "APACHE"

// Details of company behind the plugin (if there is one)
// def organization = [ name: "My Company", url: "http://www.my-company.com/" ]

// Any additional developers beyond the author specified above.
def developers = [
[ name: "Antony Jones", email: "aj/desirableobjects.co.uk" ],
[ name: "Matt Tortolani", email: "hello/doodlemoonch.com", ]
]

// Location of the plugin's issue tracker.
// def issueManagement = [ system: "JIRA", url: "http://jira.grails.org/browse/GPMYPLUGIN" ]

// Online location of the plugin's browseable source code.
// def scm = [ url: "http://svn.grails-plugins.codehaus.org/browse/grails-plugins/" ]

def doWithWebDescriptor = { xml ->
// TODO Implement additions to web.xml (optional), this event occurs before
}

def doWithSpring = {
// TODO Implement runtime spring config (optional)
}

def doWithDynamicMethods = { ctx ->
// TODO Implement registering dynamic methods to classes (optional)
}

def doWithApplicationContext = { applicationContext ->
// TODO Implement post initialization spring config (optional)
}

def onChange = { event ->
// TODO Implement code that is executed when any artefact that this plugin is
// watching is modified and reloaded. The event contains: event.source,
// event.application, event.manager, event.ctx, and event.plugin.
}

def onConfigChange = { event ->
// TODO Implement code that is executed when the project configuration changes.
// The event is the same as for 'onChange'.
}

def onShutdown = { event ->
// TODO Implement code that is executed when the application shuts down (optional)
}
}
5 changes: 5 additions & 0 deletions application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#Grails Metadata file
#Mon Aug 20 15:21:45 BST 2012
app.grails.version=2.0.3
app.name=feature-switch
plugins.svn=1.0.1
41 changes: 41 additions & 0 deletions grails-app/conf/BuildConfig.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
grails.project.class.dir = "target/classes"
grails.project.test.class.dir = "target/test-classes"
grails.project.test.reports.dir = "target/test-reports"
grails.project.target.level = 1.6
//grails.project.war.file = "target/${appName}-${appVersion}.war"

grails.project.dependency.resolution = {
// inherit Grails' default dependencies
inherits("global") {
// uncomment to disable ehcache
// excludes 'ehcache'
}
log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
repositories {
grailsCentral()
// uncomment the below to enable remote dependency resolution
// from public Maven repositories
mavenLocal
mavenCentral()
//mavenRepo "http://snapshots.repository.codehaus.org"
//mavenRepo "http://repository.codehaus.org"
//mavenRepo "http://download.java.net/maven/2/"
//mavenRepo "http://repository.jboss.com/maven2/"
}
dependencies {

}

plugins {

test ':spock:0.6'

build(":tomcat:$grailsVersion",
":release:1.0.0") {
export = false
}



}
}
24 changes: 24 additions & 0 deletions grails-app/conf/Config.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// configuration for plugin testing - will not be included in the plugin zip

log4j = {
// Example of changing the log pattern for the default console
// appender:
//
//appenders {
// console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
//}

error 'org.codehaus.groovy.grails.web.servlet', // controllers
'org.codehaus.groovy.grails.web.pages', // GSP
'org.codehaus.groovy.grails.web.sitemesh', // layouts
'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
'org.codehaus.groovy.grails.web.mapping', // URL mapping
'org.codehaus.groovy.grails.commons', // core / classloading
'org.codehaus.groovy.grails.plugins', // plugins
'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
'org.springframework',
'org.hibernate',
'net.sf.ehcache.hibernate'

warn 'org.mortbay.log'
}
43 changes: 43 additions & 0 deletions grails-app/conf/DataSource.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
dataSource {
pooled = true
driverClassName = "org.h2.Driver"
username = "sa"
password = ""
}
hibernate {
cache.use_second_level_cache = true
cache.use_query_cache = false
cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
}
// environment specific settings
environments {
development {
dataSource {
dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
url = "jdbc:h2:mem:devDb;MVCC=TRUE"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:h2:mem:testDb;MVCC=TRUE"
}
}
production {
dataSource {
dbCreate = "update"
url = "jdbc:h2:prodDb;MVCC=TRUE"
pooled = true
properties {
maxActive = -1
minEvictableIdleTimeMillis=1800000
timeBetweenEvictionRunsMillis=1800000
numTestsPerEvictionRun=3
testOnBorrow=true
testWhileIdle=true
testOnReturn=true
validationQuery="SELECT 1"
}
}
}
}
13 changes: 13 additions & 0 deletions grails-app/conf/UrlMappings.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class UrlMappings {

static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}

"/"(view:"/index")
"500"(view:'/error')
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package uk.co.desirableobjects.featureswitch

import org.codehaus.groovy.grails.commons.GrailsApplication

class FeatureSwitchService {

GrailsApplication grailsApplication

boolean hasFeature(String feature) {

return grailsApplication.config.features[feature].enabled

}

def withFeature(String feature, Closure closure) {

if (hasFeature(feature)) {
closure()
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package uk.co.desirableobjects.featureswitch

class FeatureSwitchTagLib {

static namespace = 'feature'

FeatureSwitchService featureSwitchService

def enabled = { attrs, body ->

renderByFeature(attrs, true, body)

}

private renderByFeature(Map attrs, boolean condition, Closure body) {

String feature = attrs.feature

if (featureSwitchService.hasFeature(feature) == condition) {
out << body()
}
}

def disabled = { attrs, body ->

renderByFeature(attrs, false, body)

}

}
11 changes: 11 additions & 0 deletions grails-app/views/error.gsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html>
<head>
<title>Grails Runtime Exception</title>
<meta name="layout" content="main">
<link rel="stylesheet" href="${resource(dir: 'css', file: 'errors.css')}" type="text/css">
</head>
<body>
<g:renderException exception="${exception}" />
</body>
</html>
10 changes: 10 additions & 0 deletions scripts/_Install.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// This script is executed by Grails after plugin was installed to project.
// This script is a Gant script so you can use all special variables provided
// by Gant (such as 'baseDir' which points on project base dir). You can
// use 'ant' to access a global instance of AntBuilder
//
// For example you can create directory under project tree:
//
// ant.mkdir(dir:"${basedir}/grails-app/jobs")
//
5 changes: 5 additions & 0 deletions scripts/_Uninstall.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//
// This script is executed by Grails when the plugin is uninstalled from project.
// Use this script if you intend to do any additional clean-up on uninstall, but
// beware of messing up SVN directories!
//
10 changes: 10 additions & 0 deletions scripts/_Upgrade.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// This script is executed by Grails during application upgrade ('grails upgrade'
// command). This script is a Gant script so you can use all special variables
// provided by Gant (such as 'baseDir' which points on project base dir). You can
// use 'ant' to access a global instance of AntBuilder
//
// For example you can create directory under project tree:
//
// ant.mkdir(dir:"${basedir}/grails-app/jobs")
//
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package uk.co.desirableobjects.featureswitch

class InnocentClass {

def testWith() {
boolean result = false
withFeature('eggs') {
result = true
}
return result
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package uk.co.desirableobjects.featureswitch

import spock.lang.Specification

import grails.test.mixin.support.GrailsUnitTestMixin
import spock.lang.Unroll
import grails.test.mixin.TestFor

@Mixin(GrailsUnitTestMixin)
@TestFor(FeatureSwitchService)
class SwitchFeatureSpec extends Specification {

void setup() {

InnocentClass.metaClass.withFeature = { String feature, Closure closure -> service.withFeature(feature, closure) }

}

@Unroll
def 'A feature can be set to enabled = #enabled'() {

given:
service.grailsApplication.config.features.eggs.enabled = enabled

expect:
service.hasFeature('eggs') == enabled

where:
enabled << [true, false]

}

def 'When there is no configuration'() {

given:
service.grailsApplication.config = [:]

expect:
!service.hasFeature('peas')

}

def 'When a requested feature does not exist'() {

given:
service.grailsApplication.config.features.eggs.enabled = true

expect:
!service.hasFeature('dogs')

}

@Unroll
def 'User can use withFeature in a class which is decorated with it, where feature = #enabled'() {

given:
service.grailsApplication.config.features.eggs.enabled = enabled

expect:
new InnocentClass().testWith() == enabled

where:
enabled << [true, false]

}

}
Loading

0 comments on commit dd7e4f7

Please sign in to comment.