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

added Planout4jConfigClasspathBackend #33

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.glassdoor.planout4j.config;

import com.google.common.io.Files;
import com.typesafe.config.Config;

import java.io.InputStream;
import java.util.*;

/**
* A {@link Planout4jConfigBackend} which loads namespace content found on the classpath.
*
* @author rschatz
*/
public class Planout4jConfigClasspathBackend implements Planout4jConfigBackend {

/**
* Returns the name of the directory on the classpath which contains the namespace files.
* @return String
*/
protected String getPlanoutSubdirectory() {
return "planout";
}

/**
* {@link Planout4jConfigClasspathBackend} does not need to be configured.
*
* @param config any {@link Config}
*/
@Override
public void configure(Config config) { }
Copy link
Contributor

Choose a reason for hiding this comment

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

The class path subdir can be a configurable parameter, but I guess this can wait for future refactoring if someone needs it


/**
* Loads and returns a mapping of namespace names to json based on files in the {@code planout} resource directory.
*
* @return a map of namespace name to content in the corresponding persistence layer
*/
@Override
public Map<String, String> loadAll() {
final Map<String, String> name2Content = new HashMap<>();

for (String jsonFile : getJsonResourceFiles()) {
final String namespace = Files.getNameWithoutExtension(jsonFile);
final String fileName = String.format(
"%s%s%s", getPlanoutSubdirectory(), System.getProperty("file.separator"), jsonFile
);
final String json = readJson(fileName);
name2Content.put(namespace, json);
}
return name2Content;
}

private String readJson(final String jsonFileName) {
return readInputStream(
Planout4jConfigClasspathBackend.class.getClassLoader().getResourceAsStream(jsonFileName)
);
}

private Iterable<String> getJsonResourceFiles() {
final List<String> files = new ArrayList<>();

final InputStream in = Planout4jConfigClasspathBackend.class.getClassLoader().getResourceAsStream(
String.format("%s/", getPlanoutSubdirectory())
);

try (final Scanner scanner = new Scanner(in)) {
while (scanner.hasNextLine()) {
final String fileName = scanner.nextLine();
if (fileName.endsWith(".json")) {
files.add(fileName);
}
}
}
return files;
}

private String readInputStream(InputStream is) {
try (final Scanner scanner = new Scanner(is).useDelimiter("\\A")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why \\A as opposed to \n?

return scanner.hasNext() ? scanner.next() : "";
}
}

/**
* {@link Planout4jConfigClasspathBackend} does not persist anything.
*
* @param namespace2Content namespace2Content to be stored
*/
@Override
public void persist(Map<String, String> namespace2Content) {
throw new UnsupportedOperationException(String.format("%s does not support persisting.", this.getClass().getSimpleName()));
}

@Override
public String persistenceLayer() {
return "CLASSPATH";
}

@Override
public String persistenceDestination() {
return this.getClass().getName();
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should return getPlanoutSubdirectory()

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.glassdoor.planout4j.config;

import org.junit.Test;

import java.util.Map;

import static org.junit.Assert.*;

/**
* Tests the {@link Planout4jConfigClasspathBackend} class.
*
* @author rschatz
*/
public class Planout4jConfigClasspathBackendTest {

@Test
public void testLoadingAll() {
final Planout4jConfigBackend backend = new Planout4jConfigClasspathBackend() {
@Override
protected String getPlanoutSubdirectory() {
return "classpathbackend";
}
};
final Map<String, String> namespaces = backend.loadAll();
assertEquals(2, namespaces.size());
assertTrue(namespaces.containsKey("ns_test1"));
assertTrue(namespaces.get("ns_test1").contains("experiment_definitions"));
}
}
109 changes: 109 additions & 0 deletions config/src/test/resources/classpathbackend/ns_test1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
{
"namespace": {
"name": "internal.smoke_test",
"unit": "user_guid",
"segments": 100
},
"experiment_definitions": [
{
"definition": "Speed_NumResults_Test",
"assign": {
"op": "seq",
"seq": [
{
"op": "set",
"var": "srJobsResultsPerPage",
"value": {
"choices": {
"op": "array",
"values": [1, 5, 15]
},
"unit": {
"op": "get",
"var": "user_guid"
},
"op": "uniformChoice"
}
},
{
"op": "set",
"var": "useParallelSegmentSearch",
"value": {
"p": 0.5,
"unit": {
"op": "get",
"var": "user_guid"
},
"op": "bernoulliTrial"
}
}
]
}
},
{
"definition": "Sample_Experiment_2",
"assign": {
"op": "seq",
"seq": [
{
"op": "set",
"var": "rating_filter_boxes",
"value": {
"choices": {
"op": "array",
"values": [
"radio",
"checkbox"
]
},
"unit": {
"op": "get",
"var": "user_guid"
},
"op": "uniformChoice"
}
},
{
"op": "set",
"var": "use_check_boxes_for_job_type_filter",
"value": {
"p": 0.5,
"unit": {
"op": "get",
"var": "user_guid"
},
"op": "bernoulliTrial"
}
}
]
}
},
{
"definition": "default",
"assign": {
"op": "seq",
"seq": [
{
"op": "set",
"var": "srJobsResultsPerPage",
"value": 15
},
{
"op": "set",
"var": "use_pclick",
"value": true
}
]
}
}
],
"default_experiment": "default",
"experiment_sequence": [
{
"action": "add",
"definition": "Speed_NumResults_Test",
"name": "Speed_NR_Ph1",
"segments": 20
}
]
}
121 changes: 121 additions & 0 deletions config/src/test/resources/classpathbackend/ns_test2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{
"namespace": {
"name": "internal.smoke_test",
"unit": "user_guid",
"segments": 100
},
"experiment_definitions": [
{
"definition": "Speed_NumResults_Test",
"assign": {
"op": "seq",
"seq": [
{
"op": "set",
"var": "srJobsResultsPerPage",
"value": {
"choices": {
"op": "array",
"values": [1, 5, 15]
},
"unit": {
"op": "get",
"var": "user_guid"
},
"op": "uniformChoice"
}
},
{
"op": "set",
"var": "useParallelSegmentSearch",
"value": {
"p": 0.5,
"unit": {
"op": "get",
"var": "user_guid"
},
"op": "bernoulliTrial"
}
}
]
}
},
{
"definition": "UI_Button_Test",
"assign": {
"op": "seq",
"seq": [
{
"op": "set",
"var": "rating_filter_boxes",
"value": {
"choices": {
"op": "array",
"values": [
"radio",
"checkbox"
]
},
"unit": {
"op": "get",
"var": "user_guid"
},
"op": "uniformChoice"
}
},
{
"op": "set",
"var": "use_check_boxes_for_job_type_filter",
"value": {
"p": 0.5,
"unit": {
"op": "get",
"var": "user_guid"
},
"op": "bernoulliTrial"
}
}
]
}
},
{
"definition": "default",
"assign": {
"op": "seq",
"seq": [
{
"op": "set",
"var": "srJobsResultsPerPage",
"value": 15
},
{
"op": "set",
"var": "use_pclick",
"value": true
}
]
}
}
],
"default_experiment": "default",
"experiment_sequence": [
{
"action": "add",
"definition": "Speed_NumResults_Test",
"name": "Speed_NR_Ph1",
"segments": 20
},
{
"action": "add",
"definition": "UI_Button_Test",
"name": "UI_Ph1",
"segments": 40
},
{
"action": "add",
"definition": "Speed_NumResults_Test",
"name": "Speed_NR_Ph2",
"segments": 40
}
]
}