text:
package br.com.devsource.integration.core;

import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.junit.Assert.*;

import java.time.LocalDateTime;
import org.junit.Before;
import org.junit.Test;

/**
 * @author Guilherme Pacheco
 */
public class RecallFactoryTest {

  private LocalDateTime dateTime;

  @Before
  public void setUp() throws Exception {
    dateTime = LocalDateTime.of(2015, 9, 5, 17, 20);
  }
  @Test
  public void testValueOf_Message() throws Exception {
    String msg = "32[{\"m\":\"TESTE\"}]1441473600000";
    Recall recall = RecallFactory.valueOf(msg);
    assertEquals("32", recall.getId());
    assertFalse(recall.isAction());
    assertEquals(singletonMap(Parametro.MESSAGE, "TESTE"), recall.getValues());
    assertEquals(dateTime, recall.getLocalDateTime());
  }
  @Test
  public void testValueOf_Action() throws Exception {
    String msg = "32[ping]1441473600000";
    Recall recall = RecallFactory.valueOf(msg);
    assertEquals("32", recall.getId());
    assertTrue(recall.isAction());
    assertEquals(Action.PING, recall.getAction());
    assertEquals(emptyMap(), recall.getValues());
    assertEquals(dateTime, recall.getLocalDateTime());
  }

}


text:
package com.doubleencore.sample.wear.ongoingnotification;

/**
 * Created by carlos on 7/16/14.
 */
public class Constants {
    public static final String PATH_NOTIFICATION = "/ongoingnotification";
    public static final String PATH_DISMISS = "/dismissnotification";
    public static final String KEY_TITLE = "title";
    public static final String KEY_IMAGE = "image";
}

redpajama_set_name: RedPajamaGithub


text:
package com.hp.application.automation.tools.octane.actions.build;

import com.gargoylesoftware.htmlunit.Page;
import com.hp.octane.integrations.dto.DTOFactory;
import com.hp.octane.integrations.dto.causes.CIEventCauseType;
import com.hp.octane.integrations.dto.parameters.CIParameter;
import com.hp.octane.integrations.dto.parameters.CIParameterType;
import com.hp.octane.integrations.dto.snapshots.SnapshotNode;
import com.hp.octane.integrations.dto.snapshots.CIBuildResult;
import com.hp.octane.integrations.dto.snapshots.CIBuildStatus;
import com.hp.application.automation.tools.octane.actions.Utils;
import hudson.model.*;
import hudson.plugins.parameterizedtrigger.*;
import org.junit.ClassRule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import java.io.IOException;
import java.util.Arrays;
import java.util.UUID;

import static org.junit.Assert.*;

/**
 * Created with IntelliJ IDEA.
 * User: gullery
 * Date: 13/01/15
 * Time: 11:46
 * To change this template use File | Settings | File Templates.
 */

public class BuildActionsFreeStyleTest {
	private static final DTOFactory dtoFactory = DTOFactory.getInstance();

	@ClassRule
	public static final JenkinsRule rule = new JenkinsRule();

	//  Snapshot: free-style, no params, no children
	//
	@Test
	public void testFreeStyleNoParamsNoChildren() throws Exception {
		String projectName = "root-job-" + UUID.randomUUID().toString();
		int retries = 0;
		FreeStyleProject p = rule.createFreeStyleProject(projectName);

		JenkinsRule.WebClient client = rule.createWebClient();
		Page page;
		SnapshotNode snapshot;

		assertEquals(p.getBuilds().toArray().length, 0);
		Utils.buildProject(client, p);
		while ((p.getLastBuild() == null || p.getLastBuild().isBuilding()) && ++retries < 40) {
			Thread.sleep(500);
		}
		assertEquals(p.getBuilds().toArray().length, 1);

		page = client.goTo("nga/api/v1/jobs/" + projectName + "/builds/" + p.getLastBuild().getNumber(), "application/json");
		snapshot = dtoFactory.dtoFromJson(page.getWebResponse().getContentAsString(), SnapshotNode.class);
		assertEquals(projectName, snapshot.getJobCiId());
		assertEquals(projectName, snapshot.getName());
		assertEquals(0, snapshot.getParameters().size());

		assertEquals(0, snapshot.getPhasesInternal().size());
		assertEquals(0, snapshot.getPhasesPostBuild().size());
		assertEquals(1, snapshot.getCauses().size());
		assertEquals(CIEventCauseType.USER, snapshot.getCauses().get(0).getType());
		assertEquals(String.valueOf(p.getLastBuild().getNumber()), snapshot.getNumber());
		assertEquals(CIBuildStatus.FINISHED, snapshot.getStatus());
		assertEquals(CIBuildResult.SUCCESS, snapshot.getResult());
		assertNotNull(snapshot.getStartTime());
		assertNotNull(snapshot.getDuration());
		assertNotNull(snapshot.getEstimatedDuration());
	}

	//  Snapshot: free-style, with params, no children
	//
	@Test
	public void testFreeStyleWithParamsNoChildren() throws Exception {
		String projectName = "root-job-" + UUID.randomUUID().toString();
		int retries = 0;
		FreeStyleProject p = rule.createFreeStyleProject(projectName);
		ParametersDefinitionProperty params = new ParametersDefinitionProperty(Arrays.asList(
				(ParameterDefinition) new BooleanParameterDefinition("ParamA", true, "bool"),
				(ParameterDefinition) new StringParameterDefinition("ParamB", "str", "string"),
				(ParameterDefinition) new TextParameterDefinition("ParamC", "txt", "text"),
				(ParameterDefinition) new ChoiceParameterDefinition("ParamD", new String[]{"one", "two", "three"}, "choice"),
				(ParameterDefinition) new FileParameterDefinition("ParamE", "file param")
		));
		p.addProperty(params);

		JenkinsRule.WebClient client = rule.createWebClient();
		Page page;
		SnapshotNode snapshot;
		CIParameter tmpParam;

		assertEquals(p.getBuilds().toArray().length, 0);
		Utils.buildProjectWithParams(client, p, "ParamA=false&ParamD=two&ParamX=some_string");
		while ((p.getLastBuild() == null || p.getLastBuild().isBuilding()) && ++retries < 40) {
			Thread.sleep(500);
		}
		assertEquals(p.getBuilds().toArray().length, 1);

		page = client.goTo("nga/api/v1/jobs/" + projectName + "/builds/" + p.getLastBuild().getNumber(), "application/json");
		snapshot = dtoFactory.dtoFromJson(page.getWebResponse().getContentAsString(), SnapshotNode.class);
		assertEquals(projectName, snapshot.getJobCiId());
		assertEquals(projectName, snapshot.getName());
		assertEquals(5, snapshot.getParameters().size());
		assertEquals(0, snapshot.getPhasesInternal().size());
		assertEquals(0, snapshot.getPhasesPostBuild().size());
		assertEquals(1, snapshot.getCauses().size());
		assertEquals(CIEventCauseType.USER, snapshot.getCauses().get(0).getType());
		assertEquals(String.valueOf(p.getLastBuild().getNumber()), snapshot.getNumber());
		assertEquals(CIBuildStatus.FINISHED, snapshot.getStatus());
		assertEquals(CIBuildResult.SUCCESS, snapshot.getResult());
		assertNotNull(snapshot.getStartTime());
		assertNotNull(snapshot.getDuration());
		assertNotNull(snapshot.getEstimatedDuration());

		tmpParam = snapshot.getParameters().get(0);
		assertEquals("ParamA", tmpParam.getName());
		assertEquals(CIParameterType.BOOLEAN, tmpParam.getType());
		assertEquals("bool", tmpParam.getDescription());
		assertEquals(true, tmpParam.getDefaultValue());
		assertEquals("false", tmpParam.getValue());
		assertNull(tmpParam.getChoices());

		tmpParam = snapshot.getParameters().get(1);
		assertEquals("ParamB", tmpParam.getName());
		assertEquals(CIParameterType.STRING, tmpParam.getType());
		assertEquals("string", tmpParam.getDescription());
		assertEquals("str", tmpParam.getDefaultValue());
		assertEquals("str", tmpParam.getValue());
		assertNull(tmpParam.getChoices());

		tmpParam = snapshot.getParameters().get(2);
		assertEquals("ParamC", tmpParam.getName());
		assertEquals(CIParameterType.STRING, tmpParam.getType());
		assertEquals("text", tmpParam.getDescription());
		assertEquals("txt", tmpParam.getDefaultValue());
		assertEquals("txt", tmpParam.getValue());

		tmpParam = snapshot.getParameters().get(3);
		assertEquals("ParamD", tmpParam.getName());
		assertEquals(CIParameterType.STRING, tmpParam.getType());
		assertEquals("choice", tmpParam.getDescription());
		assertEquals("one", tmpParam.getDefaultValue());
		assertEquals("two", tmpParam.getValue());

		tmpParam = snapshot.getParameters().get(4);
		assertEquals("ParamE", tmpParam.getName());
		assertEquals(CIParameterType.FILE, tmpParam.getType());
		assertEquals("file param", tmpParam.getDescription());
		assertEquals("", tmpParam.getDefaultValue());
		assertEquals(null, tmpParam.getValue());
	}

	//  Snapshot: free-style, with params, with children
	//
	@Test
	public void testFreeStyleWithParamsWithChildren() throws Exception {
		String projectName = "root-job-" + UUID.randomUUID().toString();
		int retries = 0;
		rule.jenkins.setNumExecutors(10);
		rule.jenkins.setNodes(rule.jenkins.getNodes());
		FreeStyleProject p = rule.createFreeStyleProject(projectName);
		createProjectStructure(p);
		FreeStyleProject lastToBeBuilt = (FreeStyleProject) rule.jenkins.getItem("jobCC");

		ParametersDefinitionProperty params = new ParametersDefinitionProperty(Arrays.asList(
				(ParameterDefinition) new BooleanParameterDefinition("ParamA", true, "bool"),
				(ParameterDefinition) new StringParameterDefinition("ParamB", "str", "string")
		));
		p.addProperty(params);

		JenkinsRule.WebClient client = rule.createWebClient();
		Page page;
		SnapshotNode snapshot;

		assertEquals(p.getBuilds().toArray().length, 0);
		Utils.buildProjectWithParams(client, p, "ParamA=false&ParamC=not_exists");
		while ((lastToBeBuilt.getLastBuild() == null ||
				lastToBeBuilt.getLastBuild().getNumber() < 6 ||
				lastToBeBuilt.getLastBuild().isBuilding()) && retries++ < 100) {
			Thread.sleep(500);
		}
		assertEquals(p.getBuilds().toArray().length, 1);

		page = client.goTo("nga/api/v1/jobs/" + projectName + "/builds/" + p.getLastBuild().getNumber(), "application/json");
		snapshot = dtoFactory.dtoFromJson(page.getWebResponse().getContentAsString(), SnapshotNode.class);
		assertEquals(projectName, snapshot.getJobCiId());
		assertEquals(projectName, snapshot.getName());
		assertEquals(2, snapshot.getParameters().size());
		assertEquals(2, snapshot.getPhasesInternal().size());
		assertEquals(2, snapshot.getPhasesPostBuild().size());
		assertEquals(1, snapshot.getCauses().size());
		assertEquals(CIEventCauseType.USER, snapshot.getCauses().get(0).getType());
		assertEquals(String.valueOf(p.getLastBuild().getNumber()), snapshot.getNumber());
		assertEquals(CIBuildStatus.FINISHED, snapshot.getStatus());
		assertEquals(CIBuildResult.SUCCESS, snapshot.getResult());
		assertNotNull(snapshot.getStartTime());
		assertNotNull(snapshot.getDuration());
		assertNotNull(snapshot.getEstimatedDuration());
	}

	private void createProjectStructure(FreeStyleProject project) throws IOException {
		FreeStyleProject jobA = rule.createFreeStyleProject("jobA");
		FreeStyleProject jobB = rule.createFreeStyleProject("jobB");
		FreeStyleProject jobC = rule.createFreeStyleProject("jobC");
		FreeStyleProject jobAA = rule.createFreeStyleProject("jobAA");
		FreeStyleProject jobBB = rule.createFreeStyleProject("jobBB");
		FreeStyleProject jobCC = rule.createFreeStyleProject("jobCC");

		//  jobA
		jobA.getBuildersList().add(Utils.getSleepScript(5));
		jobA.getBuildersList().add(new TriggerBuilder(Arrays.asList(
				new BlockableBuildTriggerConfig("jobAA, jobC", new BlockingBehaviour(
						Result.FAILURE,
						Result.FAILURE,
						Result.UNSTABLE
				), null)
		)));

		//  jobB
		jobB.getBuildersList().add(Utils.getSleepScript(2));
		jobB.getPublishersList().add(new hudson.tasks.BuildTrigger("jobBB, jobC", Result.SUCCESS));

		//  jobC
		jobC.getBuildersList().add(Utils.getSleepScript(5));
		jobC.getPublishersList().add(new hudson.plugins.parameterizedtrigger.BuildTrigger(Arrays.asList(
				new BuildTriggerConfig("jobCC", ResultCondition.ALWAYS, true, null)
		)));

		jobAA.getBuildersList().add(Utils.getSleepScript(2));
		jobBB.getBuildersList().add(Utils.getSleepScript(4));
		jobCC.getBuildersList().add(Utils.getSleepScript(3));

		//  root job config
		project.getBuildersList().add(new TriggerBuilder(Arrays.asList(
				new BlockableBuildTriggerConfig("jobA, jobB", new BlockingBehaviour(
						Result.FAILURE,
						Result.FAILURE,
						Result.UNSTABLE
				), Arrays.asList(new AbstractBuildParameters[0])),
				new BlockableBuildTriggerConfig("jobC", new BlockingBehaviour(
						Result.FAILURE,
						Result.FAILURE,
						Result.UNSTABLE
				), Arrays.asList(new AbstractBuildParameters[0]))
		)));
		project.getPublishersList().add(new hudson.tasks.BuildTrigger("jobA, jobB", Result.SUCCESS));
		project.getPublishersList().add(new hudson.plugins.parameterizedtrigger.BuildTrigger(Arrays.asList(
				new BuildTriggerConfig("jobC", ResultCondition.ALWAYS, true, null)
		)));
	}
}


text:
Before reporting a bug
---
1. Search issue tracker for similar issues.
2. Try the latest dev branch version of three.js.
3. Refer to the [Migration Guide](https://github.com/mrdoob/three.js/wiki/Migration) when upgrading to the dev version.

How to report a bug
---
1. Specify the revision number of the three.js library where the bug occurred.
2. Specify your browser version, operating system, and graphics card. (for example, Chrome 23.0.1271.95, Windows 7, Nvidia Quadro 2000M)
3. Describe the problem in detail. Explain what happened, and what you expected would happen.
4. Provide a small test-case (http://jsfiddle.net). If a test-case is not possible, provide a link to a live version of your application.
5. If helpful, include a screenshot. Annotate the screenshot for clarity.

How to contribute to three.js
---
1. Make sure you have a GitHub account.
2. Fork the repository on GitHub.
3. Check the [Contribution Guidelines](https://github.com/mrdoob/three.js/wiki/How-to-contribute-to-three.js).
4. Make changes to your clone of the repository.
5. Submit a pull request.


text:
/**
 * @file mean_shift_impl.hpp
 * @author Shangtong Zhang
 *
 * Mean shift clustering implementation.
 */
#ifndef __MLPACK_METHODS_MEAN_SHIFT_MEAN_SHIFT_IMPL_HPP
#define __MLPACK_METHODS_MEAN_SHIFT_MEAN_SHIFT_IMPL_HPP

#include <mlpack/core/kernels/gaussian_kernel.hpp>
#include <mlpack/core/kernels/kernel_traits.hpp>
#include <mlpack/core/metrics/lmetric.hpp>
#include <mlpack/methods/neighbor_search/neighbor_search.hpp>
#include <mlpack/methods/neighbor_search/neighbor_search_stat.hpp>
#include <mlpack/methods/range_search/range_search.hpp>

#include "map"

// In case it hasn't been included yet.
#include "mean_shift.hpp"

namespace mlpack {
namespace meanshift {

/**
  * Construct the Mean Shift object.
  */
template<bool UseKernel, typename KernelType, typename MatType>
MeanShift<UseKernel, KernelType, MatType>::
MeanShift(const double radius,
          const size_t maxIterations,
          const KernelType kernel) :
    radius(radius),
    maxIterations(maxIterations),
    kernel(kernel)
{
  // Nothing to do.
}

template<bool UseKernel, typename KernelType, typename MatType>
void MeanShift<UseKernel, KernelType, MatType>::Radius(double radius)
{
  this->radius = radius;
}

// Estimate radius based on given dataset.
template<bool UseKernel, typename KernelType, typename MatType>
double MeanShift<UseKernel, KernelType, MatType>::
EstimateRadius(const MatType& data, double ratio)
{
  neighbor::AllkNN neighborSearch(data);

  /**
   * For each point in dataset, select nNeighbors nearest points and get
   * nNeighbors distances.  Use the maximum distance to estimate the duplicate
   * threshhold.
   */
  const size_t nNeighbors = size_t(data.n_cols * ratio);
  arma::Mat<size_t> neighbors;
  arma::mat distances;
  neighborSearch.Search(nNeighbors, neighbors, distances);

  // Get max distance for each point.
  arma::rowvec maxDistances = max(distances);

  // Calculate and return the radius.
  return sum(maxDistances) / (double) data.n_cols;
}

// Class to compare two vectors.
template <typename VecType>
class less
{
 public:
  bool operator()(const VecType& first, const VecType& second) const
  {
    for (size_t i = 0; i < first.n_rows; ++i)
    {
      if (first[i] == second[i])
        continue;
      return first(i) < second(i);
    }
    return false;
  }
};

// Generate seeds from given data set.
template<bool UseKernel, typename KernelType, typename MatType>
void MeanShift<UseKernel, KernelType, MatType>::GenSeeds(
    const MatType& data,
    const double binSize,
    const int minFreq,
    MatType& seeds)
{
  typedef arma::colvec VecType;
  std::map<VecType, int, less<VecType> > allSeeds;
  for (size_t i = 0; i < data.n_cols; ++i)
  {
    VecType binnedPoint = arma::floor(data.unsafe_col(i) / binSize);
    if (allSeeds.find(binnedPoint) == allSeeds.end())
      allSeeds[binnedPoint] = 1;
    else
      allSeeds[binnedPoint]++;
  }

  // Remove seeds with too few points.  First we count the number of seeds we
  // end up with, then we add them.
  std::map<VecType, int, less<VecType> >::iterator it;
  size_t count = 0;
  for (it = allSeeds.begin(); it != allSeeds.end(); ++it)
    if (it->second >= minFreq)
      ++count;

  seeds.set_size(data.n_rows, count);
  count = 0;
  for (it = allSeeds.begin(); it != allSeeds.end(); ++it)
  {
    if (it->second >= minFreq)
    {
      seeds.col(count) = it->first;
      ++count;
    }
  }

  seeds *= binSize;
}

// Calculate new centroid with given kernel.
template<bool UseKernel, typename KernelType, typename MatType>
template<bool ApplyKernel>
typename std::enable_if<ApplyKernel, bool>::type
MeanShift<UseKernel, KernelType, MatType>::
CalculateCentroid(const MatType& data,
                  const std::vector<size_t>& neighbors,
                  const std::vector<double>& distances,
                  arma::colvec& centroid)
{
  double sumWeight = 0;
  for (size_t i = 0; i < neighbors.size(); ++i)
  {
    if (distances[i] > 0)
    {
      double dist = distances[i] / radius;
      double weight = kernel.Gradient(dist) / dist;
      sumWeight += weight;
      centroid += weight * data.unsafe_col(neighbors[i]);
    }
  }

  if (sumWeight != 0)
  {
    centroid /= sumWeight;
    return true;
  }
  return false;
}

// Calculate new centroid by mean.
template<bool UseKernel, typename KernelType, typename MatType>
template<bool ApplyKernel>
typename std::enable_if<!ApplyKernel, bool>::type
MeanShift<UseKernel, KernelType, MatType>::
CalculateCentroid(const MatType& data,
                  const std::vector<size_t>& neighbors,
                  const std::vector<double>&, /*unused*/
                  arma::colvec& centroid)
{
  for (size_t i = 0; i < neighbors.size(); ++i)
    centroid += data.unsafe_col(neighbors[i]);

  centroid /= neighbors.size();
  return true;
}

/**
 * Perform Mean Shift clustering on the data set, returning a list of cluster
 * assignments and centroids.
 */
template<bool UseKernel, typename KernelType, typename MatType>
inline void MeanShift<UseKernel, KernelType, MatType>::Cluster(
    const MatType& data,
    arma::Col<size_t>& assignments,
    arma::mat& centroids,
    bool useSeeds)
{
  if (radius <= 0)
  {
    // An invalid radius is given; an estimation is needed.
    Radius(EstimateRadius(data));
  }

  MatType seeds;
  const MatType* pSeeds = &data;
  if (useSeeds)
  {
    GenSeeds(data, radius, 1, seeds);
    pSeeds = &seeds;
  }

  // Holds all centroids before removing duplicate ones.
  arma::mat allCentroids(pSeeds->n_rows, pSeeds->n_cols);

  assignments.set_size(data.n_cols);

  range::RangeSearch<> rangeSearcher(data);
  math::Range validRadius(0, radius);
  std::vector<std::vector<size_t> > neighbors;
  std::vector<std::vector<double> > distances;

  // For each seed, perform mean shift algorithm.
  for (size_t i = 0; i < pSeeds->n_cols; ++i)
  {
    // Initial centroid is the seed itself.
    allCentroids.col(i) = pSeeds->unsafe_col(i);
    for (size_t completedIterations = 0; completedIterations < maxIterations;
         completedIterations++)
    {
      // Store new centroid in this.
      arma::colvec newCentroid = arma::zeros<arma::colvec>(pSeeds->n_rows);

      rangeSearcher.Search(allCentroids.unsafe_col(i), validRadius,
          neighbors, distances);
      if (neighbors[0].size() <= 1)
        break;

      // Calculate new centroid.
      if (!CalculateCentroid(data, neighbors[0], distances[0], newCentroid))
        newCentroid = allCentroids.unsafe_col(i);

      // If the mean shift vector is small enough, it has converged.
      if (metric::EuclideanDistance::Evaluate(newCentroid,
          allCentroids.unsafe_col(i)) < 1e-3 * radius)
      {
        // Determine if the new centroid is duplicate with old ones.
        bool isDuplicated = false;
        for (size_t k = 0; k < centroids.n_cols; ++k)
        {
          const double distance = metric::EuclideanDistance::Evaluate(
              allCentroids.unsafe_col(i), centroids.unsafe_col(k));
          if (distance < radius)
          {
            isDuplicated = true;
            break;
          }
        }

        if (!isDuplicated)
          centroids.insert_cols(centroids.n_cols, allCentroids.unsafe_col(i));

        // Get out of the loop.
        break;
      }

      // Update the centroid.
      allCentroids.col(i) = newCentroid;
    }
  }

  // Assign centroids to each point.
  neighbor::AllkNN neighborSearcher(centroids);
  arma::mat neighborDistances;
  arma::Mat<size_t> resultingNeighbors;
  neighborSearcher.Search(data, 1, resultingNeighbors, neighborDistances);
  assignments = resultingNeighbors.t();
}

} // namespace meanshift
} // namespace mlpack

#endif


text:
<?php

namespace Pkr\BuzzBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Pkr\BuzzBundle\Entity\Category;
use Pkr\BuzzBundle\Form\CategoryType;

/**
 * Category controller.
 *
 * @Route("/category")
 */
class CategoryController extends Controller
{
    /**
     * Lists all Category entities.
     *
     * @Route("/", name="category")
     * @Template()
     */
    public function indexAction()
    {
        $em = $this->getDoctrine()->getEntityManager();

        $entities = $em->getRepository('PkrBuzzBundle:Category')->findAll();

        return array('entities' => $entities);
    }

    /**
     * Displays a form to create a new Category entity.
     *
     * @Route("/new", name="category_new")
     * @Template()
     */
    public function newAction()
    {
        $entity = new Category();
        $form   = $this->createForm(new CategoryType(), $entity);

        return array(
            'entity' => $entity,
            'form'   => $form->createView()
        );
    }

    /**
     * Creates a new Category entity.
     *
     * @Route("/create", name="category_create")
     * @Method("post")
     * @Template("PkrBuzzBundle:Category:new.html.twig")
     */
    public function createAction()
    {
        $entity  = new Category();
        $request = $this->getRequest();
        $form    = $this->createForm(new CategoryType(), $entity);
        $form->bindRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getEntityManager();
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('category'));
        }

        return array (
            'entity' => $entity,
            'form'   => $form->createView()
        );
    }

    /**
     * Displays a form to edit an existing Category entity.
     *
     * @Route("/{id}/edit", name="category_edit")
     * @Template()
     */
    public function editAction($id)
    {
        $em = $this->getDoctrine()->getEntityManager();

        $entity = $em->getRepository('PkrBuzzBundle:Category')->find($id);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find Category entity.');
        }

        $editForm = $this->createForm(new CategoryType(), $entity);
        $deleteForm = $this->createDeleteForm($id);

        return array (
            'entity'      => $entity,
            'edit_form'   => $editForm->createView(),
            'delete_form' => $deleteForm->createView(),
        );
    }

    /**
     * Edits an existing Category entity.
     *
     * @Route("/{id}/update", name="category_update")
     * @Method("post")
     * @Template("PkrBuzzBundle:Category:edit.html.twig")
     */
    public function updateAction($id)
    {
        $em = $this->getDoctrine()->getEntityManager();

        $entity = $em->getRepository('PkrBuzzBundle:Category')->find($id);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find Category entity.');
        }

        $editForm   = $this->createForm(new CategoryType(), $entity);
        $deleteForm = $this->createDeleteForm($id);

        $request = $this->getRequest();

        $editForm->bindRequest($request);

        if ($editForm->isValid()) {
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('category'));
        }

        return array (
            'entity'      => $entity,
            'edit_form'   => $editForm->createView(),
            'delete_form' => $deleteForm->createView(),
        );
    }

    /**
     * Deletes a Category entity.
     *
     * @Route("/{id}/delete", name="category_delete")
     * @Method("post")
     */
    public function deleteAction($id)
    {
        $form = $this->createDeleteForm($id);
        $request = $this->getRequest();

        $form->bindRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getEntityManager();
            $entity = $em->getRepository('PkrBuzzBundle:Category')->find($id);

            if (!$entity) {
                throw $this->createNotFoundException('Unable to find Category entity.');
            }

            $em->remove($entity);
            $em->flush();
        }

        return $this->redirect($this->generateUrl('category'));
    }

    private function createDeleteForm($id)
    {
        return $this->createFormBuilder(array('id' => $id))
            ->add('id', 'hidden')
            ->getForm()
        ;
    }
}


text:
/**
 * @interface
 */
WebInspector.ProjectSearchConfig = function() {}

WebInspector.ProjectSearchConfig.prototype = {
    /**
     * @return {string}
     */
    query: function() { },

    /**
     * @return {boolean}
     */
    ignoreCase: function() { },

    /**
     * @return {boolean}
     */
    isRegex: function() { },

    /**
     * @return {!Array.<string>}
     */
    queries: function() { },

    /**
     * @param {string} filePath
     * @return {boolean}
     */
    filePathMatchesFileQuery: function(filePath) { }
}

/**
 * @constructor
 * @param {string} parentPath
 * @param {string} name
 * @param {string} originURL
 * @param {string} url
 * @param {!WebInspector.ResourceType} contentType
 */
WebInspector.FileDescriptor = function(parentPath, name, originURL, url, contentType)
{
    this.parentPath = parentPath;
    this.name = name;
    this.originURL = originURL;
    this.url = url;
    this.contentType = contentType;
}

/**
 * @interface
 * @extends {WebInspector.EventTarget}
 */
WebInspector.ProjectDelegate = function() { }

WebInspector.ProjectDelegate.Events = {
    FileAdded: "FileAdded",
    FileRemoved: "FileRemoved",
}

WebInspector.ProjectDelegate.prototype = {
    /**
     * @return {string}
     */
    type: function() { },

    /**
     * @return {string}
     */
    displayName: function() { },

    /**
     * @return {string}
     */
    url: function() { },

    /**
     * @param {string} path
     * @param {function(?Date, ?number)} callback
     */
    requestMetadata: function(path, callback) { },

    /**
     * @param {string} path
     * @param {function(?string)} callback
     */
    requestFileContent: function(path, callback) { },

    /**
     * @return {boolean}
     */
    canSetFileContent: function() { },

    /**
     * @param {string} path
     * @param {string} newContent
     * @param {function(?string)} callback
     */
    setFileContent: function(path, newContent, callback) { },

    /**
     * @return {boolean}
     */
    canRename: function() { },

    /**
     * @param {string} path
     * @param {string} newName
     * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
     */
    rename: function(path, newName, callback) { },

    /**
     * @param {string} path
     * @param {function()=} callback
     */
    refresh: function(path, callback) { },

    /**
     * @param {string} path
     */
    excludeFolder: function(path) { },

    /**
     * @param {string} path
     * @param {?string} name
     * @param {string} content
     * @param {function(?string)} callback
     */
    createFile: function(path, name, content, callback) { },

    /**
     * @param {string} path
     */
    deleteFile: function(path) { },

    remove: function() { },

    /**
     * @param {string} path
     * @param {string} query
     * @param {boolean} caseSensitive
     * @param {boolean} isRegex
     * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
     */
    searchInFileContent: function(path, query, caseSensitive, isRegex, callback) { },

    /**
     * @param {!WebInspector.ProjectSearchConfig} searchConfig
     * @param {!Array.<string>} filesMathingFileQuery
     * @param {!WebInspector.Progress} progress
     * @param {function(!Array.<string>)} callback
     */
    findFilesMatchingSearchRequest: function(searchConfig, filesMathingFileQuery, progress, callback) { },

    /**
     * @param {!WebInspector.Progress} progress
     */
    indexContent: function(progress) { }
}

/**
 * @constructor
 * @extends {WebInspector.Object}
 * @param {!WebInspector.Workspace} workspace
 * @param {string} projectId
 * @param {!WebInspector.ProjectDelegate} projectDelegate
 */
WebInspector.Project = function(workspace, projectId, projectDelegate)
{
    /** @type {!Map.<string, !{uiSourceCode: !WebInspector.UISourceCode, index: number}>} */
    this._uiSourceCodesMap = new Map();
    /** @type {!Array.<!WebInspector.UISourceCode>} */
    this._uiSourceCodesList = [];
    this._workspace = workspace;
    this._projectId = projectId;
    this._projectDelegate = projectDelegate;
    this._url = this._projectDelegate.url();
    this._displayName = this._projectDelegate.displayName();
    projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileAdded, this._fileAdded, this);
    projectDelegate.addEventListener(WebInspector.ProjectDelegate.Events.FileRemoved, this._fileRemoved, this);
}

/**
 * @enum {string}
 */
WebInspector.Project.Events = {
    DisplayNameUpdated: "DisplayNameUpdated"
};

WebInspector.Project.prototype = {
    /**
     * @return {string}
     */
    id: function()
    {
        return this._projectId;
    },

    /**
     * @return {string}
     */
    type: function()
    {
        return this._projectDelegate.type();
    },

    /**
     * @return {string}
     */
    displayName: function()
    {
        return this._displayName;
    },

    /**
     * @param {string} displayName
     */
    setDisplayName: function(displayName)
    {
        if (this._displayName === displayName)
            return;
        this._displayName = displayName;
        this.dispatchEventToListeners(WebInspector.Project.Events.DisplayNameUpdated);
    },

    /**
     * @return {string}
     */
    url: function()
    {
        return this._url;
    },

    /**
     * @return {boolean}
     */
    isServiceProject: function()
    {
        return this._projectDelegate.type() === WebInspector.projectTypes.Debugger || this._projectDelegate.type() === WebInspector.projectTypes.Formatter || this._projectDelegate.type() === WebInspector.projectTypes.Service;
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _fileAdded: function(event)
    {
        var fileDescriptor = /** @type {!WebInspector.FileDescriptor} */ (event.data);
        var path = fileDescriptor.parentPath ? fileDescriptor.parentPath + "/" + fileDescriptor.name : fileDescriptor.name;
        var uiSourceCode = this.uiSourceCode(path);
        if (uiSourceCode)
            return;

        uiSourceCode = new WebInspector.UISourceCode(this, fileDescriptor.parentPath, fileDescriptor.name, fileDescriptor.originURL, fileDescriptor.url, fileDescriptor.contentType);

        this._uiSourceCodesMap.set(path, {uiSourceCode: uiSourceCode, index: this._uiSourceCodesList.length});
        this._uiSourceCodesList.push(uiSourceCode);
        this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeAdded, uiSourceCode);
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _fileRemoved: function(event)
    {
        var path = /** @type {string} */ (event.data);
        this._removeFile(path);
    },

    /**
     * @param {string} path
     */
    _removeFile: function(path)
    {
        var uiSourceCode = this.uiSourceCode(path);
        if (!uiSourceCode)
            return;

        var entry = this._uiSourceCodesMap.get(path);
        var movedUISourceCode = this._uiSourceCodesList[this._uiSourceCodesList.length - 1];
        this._uiSourceCodesList[entry.index] = movedUISourceCode;
        var movedEntry = this._uiSourceCodesMap.get(movedUISourceCode.path());
        movedEntry.index = entry.index;
        this._uiSourceCodesList.splice(this._uiSourceCodesList.length - 1, 1);
        this._uiSourceCodesMap.delete(path);
        this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeRemoved, entry.uiSourceCode);
    },

    _remove: function()
    {
        this._projectDelegate.removeEventListener(WebInspector.ProjectDelegate.Events.FileAdded, this._fileAdded, this);
        this._projectDelegate.removeEventListener(WebInspector.ProjectDelegate.Events.FileRemoved, this._fileRemoved, this);
        this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectRemoved, this);
        this._uiSourceCodesMap = new Map();
        this._uiSourceCodesList = [];
    },

    /**
     * @return {!WebInspector.Workspace}
     */
    workspace: function()
    {
        return this._workspace;
    },

    /**
     * @param {string} path
     * @return {?WebInspector.UISourceCode}
     */
    uiSourceCode: function(path)
    {
        var entry = this._uiSourceCodesMap.get(path);
        return entry ? entry.uiSourceCode : null;
    },

    /**
     * @param {string} originURL
     * @return {?WebInspector.UISourceCode}
     */
    uiSourceCodeForOriginURL: function(originURL)
    {
        for (var i = 0; i < this._uiSourceCodesList.length; ++i) {
            var uiSourceCode = this._uiSourceCodesList[i];
            if (uiSourceCode.originURL() === originURL)
                return uiSourceCode;
        }
        return null;
    },

    /**
     * @return {!Array.<!WebInspector.UISourceCode>}
     */
    uiSourceCodes: function()
    {
        return this._uiSourceCodesList;
    },

    /**
     * @param {!WebInspector.UISourceCode} uiSourceCode
     * @param {function(?Date, ?number)} callback
     */
    requestMetadata: function(uiSourceCode, callback)
    {
        this._projectDelegate.requestMetadata(uiSourceCode.path(), callback);
    },

    /**
     * @param {!WebInspector.UISourceCode} uiSourceCode
     * @param {function(?string)} callback
     */
    requestFileContent: function(uiSourceCode, callback)
    {
        this._projectDelegate.requestFileContent(uiSourceCode.path(), callback);
    },

    /**
     * @return {boolean}
     */
    canSetFileContent: function()
    {
        return this._projectDelegate.canSetFileContent();
    },

    /**
     * @param {!WebInspector.UISourceCode} uiSourceCode
     * @param {string} newContent
     * @param {function(?string)} callback
     */
    setFileContent: function(uiSourceCode, newContent, callback)
    {
        this._projectDelegate.setFileContent(uiSourceCode.path(), newContent, onSetContent.bind(this));

        /**
         * @param {?string} content
         * @this {WebInspector.Project}
         */
        function onSetContent(content)
        {
            this._workspace.dispatchEventToListeners(WebInspector.Workspace.Events.UISourceCodeContentCommitted, { uiSourceCode: uiSourceCode, content: newContent });
            callback(content);
        }
    },

    /**
     * @return {boolean}
     */
    canRename: function()
    {
        return this._projectDelegate.canRename();
    },

    /**
     * @param {!WebInspector.UISourceCode} uiSourceCode
     * @param {string} newName
     * @param {function(boolean, string=, string=, string=, !WebInspector.ResourceType=)} callback
     */
    rename: function(uiSourceCode, newName, callback)
    {
        if (newName === uiSourceCode.name()) {
            callback(true, uiSourceCode.name(), uiSourceCode.networkURL(), uiSourceCode.originURL(), uiSourceCode.contentType());
            return;
        }

        this._projectDelegate.rename(uiSourceCode.path(), newName, innerCallback.bind(this));

        /**
         * @param {boolean} success
         * @param {string=} newName
         * @param {string=} newURL
         * @param {string=} newOriginURL
         * @param {!WebInspector.ResourceType=} newContentType
         * @this {WebInspector.Project}
         */
        function innerCallback(success, newName, newURL, newOriginURL, newContentType)
        {
            if (!success || !newName) {
                callback(false);
                return;
            }
            var oldPath = uiSourceCode.path();
            var newPath = uiSourceCode.parentPath() ? uiSourceCode.parentPath() + "/" + newName : newName;
            var value = /** @type {!{uiSourceCode: !WebInspector.UISourceCode, index: number}} */ (this._uiSourceCodesMap.get(oldPath));
            this._uiSourceCodesMap.set(newPath, value);
            this._uiSourceCodesMap.delete(oldPath);
            callback(true, newName, newURL, newOriginURL, newContentType);
        }
    },

    /**
     * @param {string} path
     * @param {function()=} callback
     */
    refresh: function(path, callback)
    {
        this._projectDelegate.refresh(path, callback);
    },

    /**
     * @param {string} path
     */
    excludeFolder: function(path)
    {
        this._projectDelegate.excludeFolder(path);
        var uiSourceCodes = this._uiSourceCodesList.slice();
        for (var i = 0; i < uiSourceCodes.length; ++i) {
            var uiSourceCode = uiSourceCodes[i];
            if (uiSourceCode.path().startsWith(path.substr(1)))
                this._removeFile(uiSourceCode.path());
        }
    },

    /**
     * @param {string} path
     * @param {?string} name
     * @param {string} content
     * @param {function(?string)} callback
     */
    createFile: function(path, name, content, callback)
    {
        this._projectDelegate.createFile(path, name, content, innerCallback);

        function innerCallback(filePath)
        {
            callback(filePath);
        }
    },

    /**
     * @param {string} path
     */
    deleteFile: function(path)
    {
        this._projectDelegate.deleteFile(path);
    },

    remove: function()
    {
        this._projectDelegate.remove();
    },

    /**
     * @param {!WebInspector.UISourceCode} uiSourceCode
     * @param {string} query
     * @param {boolean} caseSensitive
     * @param {boolean} isRegex
     * @param {function(!Array.<!WebInspector.ContentProvider.SearchMatch>)} callback
     */
    searchInFileContent: function(uiSourceCode, query, caseSensitive, isRegex, callback)
    {
        this._projectDelegate.searchInFileContent(uiSourceCode.path(), query, caseSensitive, isRegex, callback);
    },

    /**
     * @param {!WebInspector.ProjectSearchConfig} searchConfig
     * @param {!Array.<string>} filesMathingFileQuery
     * @param {!WebInspector.Progress} progress
     * @param {function(!Array.<string>)} callback
     */
    findFilesMatchingSearchRequest: function(searchConfig, filesMathingFileQuery, progress, callback)
    {
        this._projectDelegate.findFilesMatchingSearchRequest(searchConfig, filesMathingFileQuery, progress, callback);
    },

    /**
     * @param {!WebInspector.Progress} progress
     */
    indexContent: function(progress)
    {
        this._projectDelegate.indexContent(progress);
    },

    __proto__: WebInspector.Object.prototype
}

/**
 * @enum {string}
 */
WebInspector.projectTypes = {
    Debugger: "debugger",
    Formatter: "formatter",
    Network: "network",
    Snippets: "snippets",
    FileSystem: "filesystem",
    ContentScripts: "contentscripts",
    Service: "service"
}

/**
 * @constructor
 * @extends {WebInspector.Object}
 * @param {!WebInspector.FileSystemMapping} fileSystemMapping
 */
WebInspector.Workspace = function(fileSystemMapping)
{
    this._fileSystemMapping = fileSystemMapping;
    /** @type {!Object.<string, !WebInspector.Project>} */
    this._projects = {};
    this._hasResourceContentTrackingExtensions = false;
}

WebInspector.Workspace.Events = {
    UISourceCodeAdded: "UISourceCodeAdded",
    UISourceCodeRemoved: "UISourceCodeRemoved",
    UISourceCodeContentCommitted: "UISourceCodeContentCommitted",
    ProjectAdded: "ProjectAdded",
    ProjectRemoved: "ProjectRemoved"
}

WebInspector.Workspace.prototype = {
    /**
     * @return {!Array.<!WebInspector.UISourceCode>}
     */
    unsavedSourceCodes: function()
    {
        /**
         * @param {!WebInspector.UISourceCode} sourceCode
         * @return {boolean}
         */
        function filterUnsaved(sourceCode)
        {
            return sourceCode.isDirty();
        }

        var unsavedSourceCodes = [];
        var projects = this.projectsForType(WebInspector.projectTypes.FileSystem);
        for (var i = 0; i < projects.length; ++i)
            unsavedSourceCodes = unsavedSourceCodes.concat(projects[i].uiSourceCodes().filter(filterUnsaved));

        return unsavedSourceCodes;
    },

    /**
     * @param {string} projectId
     * @param {string} path
     * @return {?WebInspector.UISourceCode}
     */
    uiSourceCode: function(projectId, path)
    {
        var project = this._projects[projectId];
        return project ? project.uiSourceCode(path) : null;
    },

    /**
     * @param {string} originURL
     * @return {?WebInspector.UISourceCode}
     */
    uiSourceCodeForOriginURL: function(originURL)
    {
        var projects = this.projectsForType(WebInspector.projectTypes.Network);
        projects = projects.concat(this.projectsForType(WebInspector.projectTypes.ContentScripts));
        for (var i = 0; i < projects.length; ++i) {
            var project = projects[i];
            var uiSourceCode = project.uiSourceCodeForOriginURL(originURL);
            if (uiSourceCode)
                return uiSourceCode;
        }
        return null;
    },

    /**
     * @param {string} type
     * @return {!Array.<!WebInspector.UISourceCode>}
     */
    uiSourceCodesForProjectType: function(type)
    {
        var result = [];
        for (var projectName in this._projects) {
            var project = this._projects[projectName];
            if (project.type() === type)
                result = result.concat(project.uiSourceCodes());
        }
        return result;
    },

    /**
     * @param {string} projectId
     * @param {!WebInspector.ProjectDelegate} projectDelegate
     * @return {!WebInspector.Project}
     */
    addProject: function(projectId, projectDelegate)
    {
        var project = new WebInspector.Project(this, projectId, projectDelegate);
        this._projects[projectId] = project;
        this.dispatchEventToListeners(WebInspector.Workspace.Events.ProjectAdded, project);
        return project;
    },

    /**
     * @param {string} projectId
     */
    removeProject: function(projectId)
    {
        var project = this._projects[projectId];
        if (!project)
            return;
        delete this._projects[projectId];
        project._remove();
    },

    /**
     * @param {string} projectId
     * @return {!WebInspector.Project}
     */
    project: function(projectId)
    {
        return this._projects[projectId];
    },

    /**
     * @return {!Array.<!WebInspector.Project>}
     */
    projects: function()
    {
        return Object.values(this._projects);
    },

    /**
     * @param {string} type
     * @return {!Array.<!WebInspector.Project>}
     */
    projectsForType: function(type)
    {
        function filterByType(project)
        {
            return project.type() === type;
        }
        return this.projects().filter(filterByType);
    },

    /**
     * @return {!Array.<!WebInspector.UISourceCode>}
     */
    uiSourceCodes: function()
    {
        var result = [];
        for (var projectId in this._projects) {
            var project = this._projects[projectId];
            result = result.concat(project.uiSourceCodes());
        }
        return result;
    },

    /**
     * @param {boolean} hasExtensions
     */
    setHasResourceContentTrackingExtensions: function(hasExtensions)
    {
        this._hasResourceContentTrackingExtensions = hasExtensions;
    },

    /**
     * @return {boolean}
     */
    hasResourceContentTrackingExtensions: function()
    {
        return this._hasResourceContentTrackingExtensions;
    },

    __proto__: WebInspector.Object.prototype
}

/**
 * @type {!WebInspector.Workspace}
 */
WebInspector.workspace;


text:
layout: page
title: Projects
permalink: /projects/
---

Below is a listing of personal projects I have hosted here on github.
Many of these are recovered from old archives so may no longer run if you attempt to do so.

**[Eve Pricing]**
**Tools**: Ruby, Sinatra, MongoDB
Eve Pricing is a project I put together to help a friendcome up with potential costs for crafting simple items in Eve Online.
It pulls item pricing for specific regions in the game from [EVE-Central](http://eve-central.com) periodically. Current cost and history for each item is available including a comparative price between the current market price of the item and the total price to purchase the components of the item.

**Notes**:
After several months, the site's responsiveness slowed down drastically.
It was never fully examined as the site was not really be used at that point.
There were several potential points to be examined including the periodic script to update all the item pricing, the database structure itself, and the VM it was running on.
This project is my first projct using MonogoDB and an ORM to talk to the database. I felt it could have been the database growth combined with the structure causing slow look up times whenever an item's history data was involved.

**[Gilded Rose Kata]**
**Tools**: Ruby, RSpec
This is a refactoring project converted to Ruby and refactored.
The original source, ruby conversion source, my refactored code, and my rationale on the changes I made are discussed in the Readme.

**[Keyboard Macro Simulator]**
**Tools**: Ruby
This project roughly simulates the macro keys found on gaming keyboards.
I wrote it after becoming frustrated with how often the software to generate a macro with my keyboard would crash when attempting to make a macro.
The project is designed to keep the same requirements the macros on a keyboard would have, namely only sending keystrokes to the active window.

**Notes**:
The program has long keypress delays (a full second) and must be told what macro to run via the console, then the program to be controlled needs to be set to the active window.
To feel more like keyboard macros, it could be made to intercept keystrokes and watch for specific combinations to execute macros.
Breaking the macros away from specific methods and into configuration files would make it more expandable and less specific to a particular game.

**[Rcraft]**
**Tools**: Ruby
Rcraft is a ruby wrapper around a MineCraft server.
It runs the minecraft server and intercepts I/O from it, primarily receiving notices and player chat.
Player chat is parsed and used to tell if any commands are issued.
Server commands can be issued without giving players access to full admin rights.
Primarily, this allows for giving players resources and items while in survival mode.
Item requests are more friendly than the normal server command as items can be requested by name rather than the item id, though a fallback to the item id is in place.

I wrote this when running a minecraft server for some friends.
As a wrapper, instead of a mod, this gave item access to them no matter what mods we placed on the server itself. It worked just as well for a vanilla server as it did for a modded server.

**Notes**:
Minecraft has changed considerably since this was last updated so this will no longer work without updating item names and ids at the very least.

**[URL Shortener With Sinatra]**
**Tools**: Ruby, Sinatra, ActiveRecord
This project is a basic url shortener written with Ruby and Sinatra.
It follows a screencast discussing how to do so with some modifications to overcome some errors in the code.  The screencast's page still exists but the video itself appears broken.
I heavily commented it for some friends interested in it to get an idea of what was going on.

The program uses base62 via the alphadecimal gem to convert the row's id to a short string of characters comprised of upper and lower case characters and numbers.  It avoids multiple entries with the same url by returning the existing entry if the url is added again.

[Eve Pricing]: https://github.com/makkura/eve_pricing
[Gilded Rose Kata]: https://github.com/makkura/gilded_rose_kata
[Keyboard Macro Simulator]: https://github.com/makkura/keyboard_macros
[Rcraft]: https://github.com/makkura/rcraft
[URL Shortener With Sinatra]: https://github.com/makkura/short_url_sinatra
[WoW Horn]: https://github.com/makkura/wow_horn