<repo_name>jeffstagg/PortfolioRails<file_sep>/config/application.rb
require File.expand_path('../boot', __FILE__)

# Pick the frameworks you want:
require "action_controller/railtie"
require "action_mailer/railtie"
require "sprockets/railtie"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env)

module Portfolio
  class Application < Rails::Application
    config.generators do |g|
      g.test_framework :rspec, :fixture => true
      g.fixture_replacement :factory_girl, :dir => 'spec/factories'
    end
    config.i18n.enforce_available_locales = false

  end
end
<file_sep>/config/environments/development.rb
Portfolio::Application.configure do
  
  config.cache_classes = false
  config.eager_load = false
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false
  config.action_mailer.raise_delivery_errors = false
  config.active_support.deprecation = :log
  config.action_dispatch.best_standards_support = :builtin
  config.assets.compress = false
  config.assets.debug = true

  #devise
  config.action_mailer.default_url_options = { :host => 'localhost:8080' }

  #paperclip
  config.paperclip_defaults = {
    :storage => :fog,
    :fog_credentials => {
      :provider => "Local",
      :local_root => "#{Rails.root}/public",
      :endpoint => 'http://localhost:8080'
    },
    :fog_directory => "/images/screenshots"
  }
end
<file_sep>/app/helpers/application_helper.rb
module ApplicationHelper
	def markdown(text)
		GitHub::Markdown.render_gfm(text).html_safe
	end
end
<file_sep>/app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
	def about
		@about = File.read("#{Rails.root}/app/assets/markdown/about.md")
	end

	def contact
		@contact = File.read("#{Rails.root}/app/assets/markdown/contact.md")
	end
	
	def home
		@home_page = true
	end
end
<file_sep>/spec/factories/projects.rb
FactoryGirl.define do
  factory :project do
  	name 'project1'
  	title 'Project 1'
  	description 'Lorem ipsum and some other such stuff'
  	liveUrl 'http://www.google.com'
  end
end<file_sep>/app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
  
  include AngularController
  
  before_filter :set_headers
  before_filter :authenticate_user!, except: [:show, :index]

  before_action :set_project, only: [:edit, :update, :destroy]

  protect_from_forgery with: :null_session

  respond_to :json


  # GET /projects
  # GET /projects.json
  def index
    generate_project_list
    @project = @projects.first

    respond_to do |format|
      format.html
      format.json { render json: @projects }
    end
  end

  # GET /projects/1
  # GET /projects/1.json
  def show
    generate_project_list
    @project = (get_project_by_name) ?
      get_project_by_name :
      @projects.first

    
    respond_to do |format|
      format.html
      format.json { render json: @project }
    end
  end

  # GET /projects/new
  def new
    @project = Project.new

    respond_to do |format|
      format.html
      format.json { render json: @project }
    end
  end

  # GET /projects/1/edit
  def edit
    #@project = Project.find(params[:id])
  end

  # POST /projects
  # POST /projects.json
  def create
    @project = Project.new(project_params)

    respond_to do |format|
      if @project.save
        format.html { redirect_to @project, notice: 'Project was successfully created.' }
        format.json { render json: @project, status: :created, location: @project }
      else
        format.html { render action: 'new' }
        format.json { render json: @project.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /projects/1
  # PATCH/PUT /projects/1.json
  def update
    respond_to do |format|
      if @project.update(project_params)
        format.html { redirect_to @project, notice: 'Project was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @project.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /projects/1
  # DELETE /projects/1.json
  def destroy
    @project.destroy
    respond_to do |format|
      format.html { redirect_to projects_url }
      format.json { head :no_content }
    end
  end

  def options
    set_headers
    # this will send an empty request to the clien with 200 status code (OK, can proceed)
    render :text => '', :content_type => 'text/plain'
  end

  private
    # Set CORS
    def set_headers
      headers['Access-Control-Allow-Origin'] = '*'
      headers['Access-Control-Expose-Headers'] = 'Etag'
      headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD'
      headers['Access-Control-Allow-Headers'] = '*, x-requested-with, Content-Type, If-Modified-Since, If-None-Match'
      headers['Access-Control-Max-Age'] = '86400'
    end

    # Use callbacks to share common setup or constraints between actions.
    def set_project
      @project = Project.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def project_params
      params.require(:project).permit(:name, :description, :title, :screenshot, :tags, :liveUrl)
    end

    def generate_project_list
      @projects = Project.all.order('id ASC')
    end

    def get_project_by_name
      @project = (params[:name]) ? 
        Project.where(:name => params[:name]).first : 
        @projects.first
    end
end
<file_sep>/app/assets/markdown/about.md
## About
<div class="profile"></div>

### Who is Jeff?

I'm a full-stack web developer based in the United States, who prides himself in writing clean, beautiful code. I love taking a beautiful design and turning it into an interactive web experience. Most of my work has been geared toward the adventure lifestyle industries, including ski resorts and other destinations in the hospitality industry. As an active participant in adventure sports (and a personal user of the websites I build), I know what it takes to get the right information into the hands of the users who need it most. I've been developing websites and software since my early teens, and my curiosity and joy in building things that "just work" has not stopped growing.

### Why build websites?

If there's one thing I truly love, it's software development. Setting up a productive development environment for others to code in is just something I get a lot of joy out of. If your tools are not working together to keep your team productive and making sure your users do not experience a bug, then you are putting your company at a risk. I enjoy test-driven development technologies, and do whatever it takes to ensure that the processes to develop, build, and deploy tools is easy and efficient. I love working on anything and everything, including setting up a server, managing a database, building back-end code to communicate with the database and send to the front-end, and building interactive front-end tools to display the data in a way the user can easily understand.

### Why the adventure sports industry?

Growing up as a geek, I was never very good at team sports. While I enjoyed working with a team to get good results, my heart just wasn't into it. I wanted the freedom to do whatever I wanted, and had a deep-seeded urge for heavy doses of adrenaline. Unfortunately, the team sports available to me were just not fast enough. So as soon as I came of age, I immediately sought out a life of adventure. I sold or donated all but 2 bags of clothing and moved to Burlington, Vermont to pursue snowboarding while I went to college for software engineering. After living on the east coast for several years, I needed bigger, taller mountains. It was time to go to the Rockies.

Moving to Colorado was the next logical step. I again sold most of my belongings, packed up a truck and headed to the mountains to follow my dreams. I moved to the Vail area to see how long I could make it as a ski bum, where I found a design agency that loved my code and passion for the adventure lifestyle. After a few years working with others into the same lifestyle, I could never look back.

During this time, I also fell in love with skydiving and BASE jumping. This led me to expand my horizons in where I live, which led to me living a mobile lifestyle. Now I travel the United States with my dog in an RV, visiting dropzones, ski resorts, and BASE jumping locations. I work from my mobile home/office, and love every minute of it. When I'm not building websites and tools online, you'll find me skydiving, BASE jumping, or sliding around on snow. While I've spent plenty of time overcoming my own fears, I now help others with theirs as a skydiving instructor, or taking passengers for their first jump on a tandem skydive.<file_sep>/config/initializers/devise.rb
Devise.setup do |config|
  config.secret_key = '<KEY>'
  config.mailer_sender = '<EMAIL>'

  require 'devise/orm/mongoid'
  config.case_insensitive_keys = [ :email ]
  config.strip_whitespace_keys = [ :email ]
  config.skip_session_storage = [:http_auth]
  config.stretches = Rails.env.test? ? 1 : 10
  config.reconfirmable = true
  config.password_length = <PASSWORD>
  config.maximum_attempts = 5
  config.reset_password_within = <PASSWORD>
  config.sign_out_via = :delete
end
<file_sep>/app/models/screenshot.rb
class Screenshot
  include Mongoid::Document
  include Mongoid::Paperclip

  embedded_in :project, :inverse_of => :screenshots

  has_mongoid_attached_file :attachment,
  	:path => ':attachment/:id/:style.:extension',  	
  	:storage => :s3,
  	:styles => {
  		:thumb => '150*150#',
  		:medium => '800*600>',
  		:large => '280*800>'
  	}

end
<file_sep>/config/routes.rb
Portfolio::Application.routes.draw do

  #TODO: Enable authentication until I have a user up and ready
  devise_for :users, :skip => :registrations

  get 'resume' => "resume#index"
  get 'about' => 'static_pages#about'
  get 'contact' => 'static_pages#contact'
  #get '/projects/new' => 'projects#new'
  #get 'projects(/:name)' => 'projects#show'

  get 'projects' => 'projects#index'
  get 'projects(/:name)' => 'projects#show'

  resources :projects

  #image uploads
  get "/images/*path" => "images#serve"
  
  #resources :projects

  root :to => 'static_pages#home'
  
end
<file_sep>/config/environments/production.rb
Portfolio::Application.configure do
  
  config.cache_classes = true
  config.eager_load = true

  config.consider_all_requests_local       = false
  config.action_controller.perform_caching = true
  config.serve_static_assets = true
  config.assets.js_compressor = :uglifier
  config.assets.compile = false
  config.assets.digest = true
  config.assets.version = '1.0'
  config.action_dispatch.x_sendfile_header = nil #heroku hack

  #change back to :info for log_level
  config.log_level = :debug
  config.i18n.fallbacks = true
  config.active_support.deprecation = :notify
  config.log_formatter = ::Logger::Formatter.new

  #Paperclip / Amazon Web Services
  config.paperclip_defaults = {
    :storage => :fog,
    :fog_credentials => {
      :provider => "AWS",
      :aws_access_key_id => ENV['AWS_ACCESS_KEY_ID'],
      :aws_secret_access_key => ENV['AWS_SECRET_ACCESS_KEY'],
      :region => 'us-west-2'
    },
    :fog_directory => ENV['S3_BUCKET_NAME']
  }
end
<file_sep>/spec/models/user_spec.rb
require 'spec_helper'
require 'shoulda-matchers'

describe User, :type => :model do

  before (:each) do
  	@user = FactoryGirl.create(:user)
  end
  
  it "should have an accessible email" do
  	@user.email.should_not == nil
  end

end<file_sep>/app/assets/markdown/contact.md
## Contact Me

I can be contacted in the following ways:

> [<i class="fa fa-envelope"></i> <EMAIL>](mailto:<EMAIL>)  
> [<i class="fa fa-phone"></i> 303.656.9244](tel:+13036569244)  
> [<i class="fa fa-github-alt"></i> github.com/JeffStagg](http://www.github.com/jeffstagg)  
> [<i class="fa fa-linkedin"></i> linkedin.com/in/JeffStagg](http://www.linkedin.com/in/jeffstagg)  
> [<i class="fa fa-home"></i> JeffStagg.com](http://jeffstagg.com)<file_sep>/features/step_definitions/static_pages_steps.rb
When(/^I go to the about page$/) do
  visit ('/about')
end

Then(/^I should see 'About' as the title$/) do
  expect(page).to have_selector('h1', text: 'About')
end
<file_sep>/spec/support/matchers/screenshot_matcher.rb
# An RSpec matcher used to test the screenshot file attachment
# of the Project model. It tests whether the Paperclip fields
# for file attachments exist or not.

RSpec::Matchers.define :have_screenshot_attached_file do |expected|
   match do |actual|
      actual.screenshot_file_name != nil &&
      actual.screenshot_content_type != nil &&
      actual.screenshot_file_size != nil &&
      actual.screenshot_updated_at != nil
   end

   failure_message_for_should do |actual|
      "expected that #{actual} would have a screenshot file attachment"
   end

   failure_message_for_should_not do |actual|
      "expected that #{actual} would not have a screenshot file attachment"
   end
end<file_sep>/app/controllers/resume_controller.rb
class ResumeController < ApplicationController
  def index
  	@resume = Resume.new
  	@resume = File.read("#{Rails.root}/app/assets/markdown/resume.md")

  	respond_to do |format|
      format.html
      format.json
    end
  end
end
<file_sep>/app/controllers/image_controller.rb
class ImagesController < ApplicationController

    def serve
      path = env["PATH_INFO"].gsub("/images/", "/app/assets/images/")
      send_file( "#{Rails.root}#{path}",
        :disposition => 'inline',
        :type => 'image/jpeg',
        :x_sendfile => true )
    end

end<file_sep>/features/step_definitions/projects_steps.rb
Given(/^I have projects named (.+)$/) do |names|
  names.split(', ').each do |name|
  	Project.create!(:name => name)
  end
end

When(/^I go to the list of projects$/) do
  visit projects_path
end

Then(/^I should see "(.*?)"$/) do |name|
  expect(page).to have_selector('li', text: name)
end

When(/^I go to the page '\/projects\/(.*?)'$/) do |name|
  visit '/projects/' + name
end

Then(/^I should not see "(.*?)"$/) do |name|
  response.should_not have_selector('tr td', text: name)
end

Then(/^I should see the project name "(.*?)"$/) do |name|
  response.should_not have_selector('strong', text: 'Name: ' + name)
end<file_sep>/spec/controllers/projects_controller_spec.rb
require 'spec_helper'

describe ProjectsController do

  login_user

  def valid_session
    controller.stub!(:signed_in?).and_return(true)
  end

  let!(:project) { FactoryGirl.create :project }
  let!(:projects) { 
  	(2..5).each do |n| 
  	  FactoryGirl.create(:project, :name => 'project%d' % n)
    end }

  describe "GET index" do
    it "should get index" do
      get :index
      assert_response :success
    end

    it "should return list of projects" do
    	get :index
      assigns(:projects).count.should == 5
    end

    it "should return the first project as default" do
    	get :index
    	assigns(:project).title.should == "Project 1"
    end
  end

  describe "GET show" do

    it "should show project by name" do
    	get :show, name: "project2"
    	assigns(:project).name.should == "project2"
    end

    it "should return the first project if name not found" do
    	get :show, name: "project123"
    	assigns(:project).name.should == "project1"
    end

  end

  describe "POST new" do
    it "can create a project" do
      post :create, {:project => project.attributes}, valid_session
      assigns(:project).name.should == "project1"
    end

    it "can have a screenshot" do
      project = FactoryGirl.create(:project,
        :screenshot => File.new(Rails.root + 'spec/support/images/test-image.jpg'))
      Project.stub(:new).and_return(project)
      post :create, {:project => project.attributes}, valid_session

      assigns(:project).name.should == "project1"
      assigns(:project).should have_screenshot_attached_file
    end
  end

  describe "UPDATE"

end<file_sep>/app/controllers/concerns/angular_controller.rb
module AngularController
  extend ActiveSupport::Concern

  included do
    respond_to :html, :json
  end
end<file_sep>/README.md
# Portfolio

This is the source for the interactive portfolio of <NAME>.

## Setup

Your environment will need the following installed:

- Ruby 2.0  
- Rails 4.0  
- Bundler  
- NodeJS
- MongoDB

You can use my [Ruby 2 / Rails 4 Vagrant Box](https://github.com/jeffstagg/VagrantBoxes/tree/master/ruby2-rails4) for development. Download the Portfolio source code into the Source directory of the Vagrant environment, and edit the config file to your liking. Run the following code to boot into the vagrant box:

		vagrant up  
		vagrant ssh  
		gem install rails  
		gem install nodejs

Next you'll need to run Bundle to install the necessary gems, so run:

		bundle install

Configure your system's development environment to use your MongoDB settings.

You are now ready to develop by running:

		rails s

When you are ready to end development, run:

		vagrant suspend

Or if you are finished development completely and are ready to delete the Vagrant box, run:

		vagrant destroy

Happy hacking!<file_sep>/spec/models/project_spec.rb
require 'spec_helper'

describe Project, :type => :model do

  let!(:project) { FactoryGirl.create :project }
  
  it "is valid with name, title, description, live url" do
    project.should be_valid
  end

  it "is invalid without a name" do
  	project.name = nil
  	project.should_not be_valid
  	project.name = ""
  	project.should_not be_valid
  end

  it "is invalid without a title" do
  	project.title = nil
  	project.should_not be_valid
  	project.title = ""
  	project.should_not be_valid
  end

  it "is invalid without a description" do
  	project.description = nil
  	project.should_not be_valid
  	project.description = ""
  	project.should_not be_valid
  end

  it "is invalid without a live url" do
  	project.liveUrl = nil
  	project.should_not be_valid
  	project.liveUrl = ""
  	project.should_not be_valid
  end

  


end<file_sep>/app/assets/markdown/resume.md
### Profile

> I deliver interactive web solutions, with a passion for the extreme sports industry. I enjoy the full spectrum of developing and maintaining web applications, from setting up servers and databases and building back-end, server-side code to tweaking front-end javascript functionality and developing user interfaces.

------

### Skills

> **Server-Side Development**  
> C#, ASP.Net, .Net MVC, Ruby, Rails, Sinatra

> **Client-Side Development**  
> HTML5, CSS3, Javascript, jQuery, AngularJS

> **Databases**  
> MongoDB, SQL Server, MySQL

-------

### Experience

> **Freelance**  
> *Freelance Web Developer* : *(2012 - Present)*  
> Freelance web developer specializing in C# / ASP.Net, Ruby on Rails, HTML / CSS / JS. Available for short- or long-term remote projects.

> **Resort Technology Partners / Active Network**  
> *Web Developer* : *(2010 - 2012) Avon, CO*  
> I develop web applications for ski resorts and other destination areas using C# / ASP.net and the open-source Umbraco CMS. Our team wins several web awards a year, serves many of the top ski resorts in North America, and leads the way in web interaction for resort destinations.

> **Urban Lending Solutions**  
> *Software Support Engineer* : *(2010 - 2010) Boulder, CO*  
> Developed tools in C# / .Net for current software support team to interact with SQL Server database and build PDF documents. Built a tool to reorganize hundreds of thousands of misplaced files containing pertinent, confidential information. Set up automation processes to ease workload of support team building SSIS packages. Created and deployed SSRS reports to display data errors and bugs in current software.

> **Pragmatic Technologies**  
> *Software Developer Intern* : *(2009 - 2010) Burlington, VT*  
> **Ringmaster Software**  
> *Software Developer Intern* : *(2008 - 2009) Burlington, VT*  

-------

### Education

> **Camplain College**  
> *Bachelor of Science - Software Engineering* :  *(2006 - 2010)*  
> Graduated *Cum Laude*
> President of *Software Engineering Club* 2008 - 2009  

-------

### Other

> **Maryland Skydiving Center**  
> *Tandem / IAD Skydiving Instructor* : *(2013 - Present) Cumberland, MD*  
> Take passengers along for the ride of their life on tandem skydives. Teach adults to become licensed skydivers, and coach experienced jumpers to hone freefall and canopy skills.

-------

### Contact

> [<i class="fa fa-envelope"></i> <EMAIL>](mailto:<EMAIL>)  
> [<i class="fa fa-phone"></i> 303.656.9244](tel:+13036569244)  
> [<i class="fa fa-github-alt"></i> github.com/JeffStagg](http://www.github.com/jeffstagg)  
> [<i class="fa fa-linkedin"></i> linkedin.com/in/JeffStagg](http://www.linkedin.com/in/jeffstagg)  
> [<i class="fa fa-home"></i> JeffStagg.com](http://jeffstagg.com)<file_sep>/app/models/project.rb
class Project
  include Mongoid::Document
  include Mongoid::Paperclip

  field :name, type: String
  field :title, type: String
  field :description, type: String
  field :tags, type: String
  field :liveUrl, type: String
  
  has_mongoid_attached_file :screenshot,
  	:path => ':id/:style.:extension',
  	:styles => {
  		:thumb => '300*200>',
  		:medium => '800*600>',
  		:large => '1280*800>'
  	}
  validates_attachment_content_type :screenshot, :content_type => %w(image/jpeg image/jpg image/png)

  validates_presence_of :name
  validates_presence_of :title
  validates_presence_of :description
  validates_presence_of :liveUrl
end

<file_sep>/spec/factories/users.rb
FactoryGirl.define do
  factory :user do
  	email '<EMAIL>'
  	password '<PASSWORD>'
  	password_confirmation '<PASSWORD>'
  end
end
<file_sep>/app/helpers/projects_helper.rb
module ProjectsHelper
	#TODO: remove if not in use
	def short_description(project)
		project.description.split(" ").each_with_object("") {|x,ob| break ob unless (ob.length + " ".length + x.length <= 40);ob << (" " + x)}.strip + "..."
	end
end
<file_sep>/Gemfile
source 'https://rubygems.org'

#system

gem 'rails', '4.0.1'
gem 'mongoid', github: 'mongoid/mongoid'

#admin
gem 'devise', github: 'plataformatec/devise'

#sass
gem 'sass-rails', '~> 4.0.0'
gem 'compass-rails'
gem 'font-awesome-sass'

#js
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.0.0'
gem 'jquery-rails'
gem 'jbuilder', '~> 1.2'

#html
gem 'turbolinks'
gem 'slim'

#markdown transformations
gem 'redcarpet'
gem 'github-markdown'

#image uploads
gem 'mongoid-paperclip', :require => 'mongoid_paperclip', :github => 'meskyanichi/mongoid-paperclip'
gem 'aws-sdk', '~> 1.3.4', :require => 'aws-sdk'
gem 'fog'
gem 'unf'

group :development do
  gem 'guard-livereload', require: false
end

group :development, :test do
	gem 'rspec-rails', '~> 2.0'
	gem 'guard-rspec'
	gem 'capybara'

	gem 'spork', '>= 1.0rc0'
	gem 'spork-rails', github: 'A-gen/spork-rails'
	gem 'guard-spork'
	
	gem 'database_cleaner'
	gem 'factory_girl_rails'
	gem 'shoulda-matchers'
end
