work on post views

This commit is contained in:
albert
2010-03-12 12:32:31 -05:00
parent 15c134b270
commit 9f29ffc8c3
18 changed files with 486 additions and 317 deletions

View File

@@ -1,6 +1,4 @@
class SessionsController < ApplicationController
before_filter :member_only, :only => [:destroy]
def new
@user = User.new
end
@@ -9,14 +7,14 @@ class SessionsController < ApplicationController
if User.authenticate(params[:name], params[:password])
@user = User.find_by_name(params[:name])
session[:user_id] = @user.id
redirect_to(params[:url] || posts_path, :notice => "You have logged in")
redirect_to(params[:url] || posts_path, :notice => "You are now logged in.")
else
render :action => "edit", :flash => "Password was incorrect"
redirect_to(new_session_path, :notice => "Password was incorrect.")
end
end
def destroy
session[:user_id] = nil
redirect_to(posts_path, :notice => "You have logged out")
session.delete(:user_id)
redirect_to(posts_path, :notice => "You are now logged out.")
end
end

View File

@@ -96,6 +96,10 @@ class AnonymousUser
false
end
def blacklisted_tags
""
end
%w(banned privileged contributor janitor moderator admin).each do |name, value|
normalized_name = name.downcase.gsub(/ /, "_")

View File

@@ -102,4 +102,8 @@ class PostSet
def to_json
posts.to_json
end
def presenter
@presnter ||= PostSetPresenter.new(self)
end
end

View File

@@ -3,13 +3,13 @@ require 'digest/sha1'
class User < ActiveRecord::Base
class Error < Exception ; end
attr_accessor :password
attr_accessible :password, :password_confirmation, :password_hash, :email, :last_logged_in_at, :last_forum_read_at, :has_mail, :receive_email_notifications, :comment_threshold, :always_resize_images, :favorite_tags, :blacklisted_tags, :name
attr_accessor :password, :old_password
attr_accessible :password, :old_password, :password_confirmation, :password_hash, :email, :last_logged_in_at, :last_forum_read_at, :has_mail, :receive_email_notifications, :comment_threshold, :always_resize_images, :favorite_tags, :blacklisted_tags, :name
validates_length_of :name, :within => 2..20, :on => :create
validates_format_of :name, :with => /\A[^\s;,]+\Z/, :on => :create, :message => "cannot have whitespace, commas, or semicolons"
validates_uniqueness_of :name, :case_sensitive => false, :on => :create
validates_uniqueness_of :email, :case_sensitive => false, :on => :create, :if => lambda {|rec| !rec.email.blank?}
validates_length_of :password, :minimum => 5, :if => lambda {|rec| rec.new_record? || rec.password}
validates_length_of :password, :minimum => 5, :if => lambda {|rec| rec.new_record? || !rec.password.blank?}
validates_inclusion_of :default_image_size, :in => %w(medium large original)
validates_confirmation_of :password
validates_presence_of :email, :if => lambda {|rec| rec.new_record? && Danbooru.config.enable_email_verification?}
@@ -176,6 +176,10 @@ class User < ActiveRecord::Base
include EmailVerificationMethods
include BlacklistMethods
include ForumMethods
def initialize_default_image_size
self.default_image_size = "Medium"
end
def can_update?(object, foreign_key = :user_id)
is_moderator? || is_admin? || object.__send__(foreign_key) == id

View File

@@ -0,0 +1,36 @@
class PostSetPresenter
attr_accessor :post_set
def initialize(post_set)
@post_set = post_set
end
def posts
post_set.posts
end
def tag_list_html
""
end
def wiki_html
""
end
def pagination_html
end
def post_previews_html
html = ""
posts.each do |post|
html << %{<article id="post_#{post.id}" data-id="#{post.id}" data-tags="#{h(post.tag_string)}" data-uploader="#{h(post.uploader_name)}" data-rating="#{post.rating}" data-width="#{post.image_width}" data-height="#{post.image_height}">}
html << %{<a href="/posts/#{post.id}">}
html << %{<img src="#{post.preview_url}>"}
html << %{</a>}
html << %{</article>}
end
html
end
end

View File

@@ -4,16 +4,21 @@
<title><%= yield(:page_title) || Danbooru.config.app_name %></title>
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel="top" title="<%= Danbooru.config.app_name %>" href="/">
<%= csrf_meta_tag %>
<% unless @current_user.blacklisted_tags.blank? %>
<meta name="blacklisted-tags" content="<%= h @current_user.blacklisted_tags %>">
<% end %>
<%= auto_discovery_link_tag :atom, posts_path(:format => "atom", :tags => params[:tags]) %>
<%= stylesheet_link_tag "default" %>
<%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" %>
<%= javascript_include_tag "rails" %>
<%= Danbooru.config.custom_html_header_content %>
<%= yield :html_header_content %>
</head>
<body>
<nav>
<h1><%= link_to(Danbooru.config.app_name, "/") %><%= yield :page_header %></h1>
<ul>
<menu>
<% if @current_user.is_anonymous? %>
<%= nav_link_to("Login", new_session_path) %>
<% else %>
@@ -28,7 +33,7 @@
<%= nav_link_to("Wiki", wiki_page_path(:id => "help:home")) %>
<%= nav_link_to("Forum", forum_topics_path, :class => (@current_user.has_forum_been_updated? ? "forum-updated" : nil)) %>
<%= nav_link_to("&raquo;".html_safe, site_map_path) %>
</ul>
</menu>
<%= yield :secondary_nav_links %>
</nav>
@@ -61,9 +66,9 @@
</div>
<% end %>
<section id="content">
<div id="content">
<%= yield :layout %>
</section>
</div>
<%= yield :page_footer_content %>
</body>

View File

@@ -0,0 +1,97 @@
<% unless @post_set.suggestions.blank? %>
<div class="notice">
Maybe you meant: <%= @post_set.suggestions.map {|x| link_to(x, posts_path(:tags => x), :class => "tag-type-#{Tag.type_name(x)}" )}.to_sentence(:last_word_connector => ", or ", :two_words_connector => " or ") %>
</div>
<% end %>
<aside>
<section id="search-box">
<h1>Search</h1>
<% form_tag(posts_path, :method => "get") do %>
<%= text_field_tag("tags", params[:tags], :size => 20) %>
<%= submit_tag "Go" %>
<% end %>
</section>
<% if @current_user.is_privileged? %>
<section id="mode-box">
<h1>Mode</h1>
<form action="/">
<select name="mode">
<option value="view">View posts</option>
<option value="edit">Edit posts</option>
<option value="add-fav">Add to favorites</option>
<option value="remove-fav">Remove from favorites</option>
<option value="rating-s">Rate safe</option>
<option value="rating-q">Rate questionable</option>
<option value="rating-e">Rate explicit</option>
<option value="vote-up">Vote up</option>
<option value="vote-down">Vote down</option>
<option value="lock-rating">Lock rating</option>
<option value="lock-note">Lock notes</option>
<option value="edit-tag-script">Edit tag script</option>
<option value="apply-tag-script">Apply tag script</option>
<% if @current_user.is_janitor? %>
<option value="approve">Approve post</option>
<% end %>
<option value="flag">Flag post</option>
</select>
</form>
</section>
<% end %>
<section id="blacklist-box">
<h1>Blacklisted</h1>
<%= link_to "Hidden", "#" %>
<ul>
</ul>
</section>
<div id="tag-and-wiki-box">
<menu>
<li><h1>Tags</h1></li>
<li><h1>Wiki</h1></li>
</menu>
<section id="tag-box">
<h2>Tags</h2>
<%= @post_set.presenter.tag_list_html %>
</section>
<section id="wiki-box">
<h2>Wiki</h2>
<%= @post_set.presenter.wiki_html %>
</section>
</div>
</aside>
<section>
<h1>Posts</h1>
<%= @post_set.presenter.post_previews_html %>
</section>
<footer>
<%= @post_set.presenter.pagination_html %>
</footer>
<% content_for(:page_title) do %>
/<%= @post_set.tags %>
<% end %>
<% content_for(:page_header) do %>
/ <%= @post_set.tags %>
<% end %>
<% content_for(:secondary_nav_links) do %>
<menu>
<li>Listing</li>
<li>Upload</li>
<li>Popular</li>
<li>Favorites</li>
<li>Subscriptions</li>
<li>Changes</li>
<li>Approvals</li>
<li>Moderate</li>
<li>Help</li>
</menu>
<% end %>

View File

@@ -19,10 +19,18 @@
<aside>
<h2>Help</h2>
<ul>
<menu>
<li><%= link_to "I don't have an account", new_user_path %></li>
<li><%= link_to "I forgot my password", reset_password_info_path %></li>
<li><%= link_to "I forgot my login", login_reminder_info_path %></li>
<li><%= link_to "I want to delete my account", delete_account_info_path %></li>
</ul>
</menu>
</aside>
<% content_for(:page_title) do %>
login
<% end %>
<% content_for(:page_header) do %>
/ login
<% end %>

View File

@@ -1,3 +1,70 @@
<% form_for @user do |f| %>
<fieldset>
<legend>Basic</legend>
<p>
<%= f.label :email %>
<%= f.text_field :email %>
</p>
<p>
<%= f.label :receive_email_notifications, "Email notifications" %>
<%= f.check_box :receive_email_notifications %>
</p>
<p>
<%= f.label :comment_threshold %>
<%= f.text_field :comment_threshold, :size => 2 %>
</p>
<p>
<%= f.label :always_resize_images %>
<%= f.check_box :always_resize_images %>
</p>
<p>
<%= f.label :default_image_size %>
<%= f.select :default_image_size, %w(Medium Large Original) %>
</p>
<p>
<%= f.label :favorite_tags %>
<%= f.text_area :favorite_tags, :rows => 5 %>
</p>
<p>
<%= f.label :blacklisted_tags %>
<%= f.text_area :blacklisted_tags, :rows => 5 %>
</p>
</fieldset>
<fieldset>
<legend>Special</legend
<p>For security purposes, changing the following settings requires you to re-enter your password. You can leave the new password field blank to keep your current one.</p>
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :password, "New password" %>
<%= f.text_field :password %>
</p>
<p>
<%= f.label :old_password %>
<%= f.text_field :old_password %>
</p>
</fieldset>
<%= submit_tag "Submit" %>
<% end %>
<% content_for(:page_title) do %>
/<%= @user.name %>/Settings
<% end %>
<% content_for(:page_header) do %>
/ <%= @user.name %> / Settings
<% end %>

View File

@@ -1,11 +1,14 @@
<h1><%= @user.name %></h1>
<nav>
<ul>
<li><%= link_to "Settings", edit_user_path(@user) %></li>
<li><%= link_to "Log out", session_path(0), :method => :delete %></li>
</ul>
</nav>
<% content_for(:page_title) do %>
users/<%= @user.name %>
<% end %>
<% content_for(:page_header) do %>
/ <%= @user.name %>
<% end %>
<% end %>

View File

@@ -56,5 +56,5 @@ Danbooru::Application.routes.draw do |map|
match "/user_maintenance/login_reminder" => "user_maintenance#login_reminder", :as => "login_reminder_info"
match "/user_maintenance/reset_password" => "user_maintenance#reset_password", :as => "reset_password_info"
root :to => "post#index"
root :to => "posts#index"
end

View File

@@ -1,278 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Ruby on Rails: Welcome aboard</title>
<style type="text/css" media="screen">
body {
margin: 0;
margin-bottom: 25px;
padding: 0;
background-color: #f0f0f0;
font-family: "Lucida Grande", "Bitstream Vera Sans", "Verdana";
font-size: 13px;
color: #333;
}
h1 {
font-size: 28px;
color: #000;
}
a {color: #03c}
a:hover {
background-color: #03c;
color: white;
text-decoration: none;
}
#page {
background-color: #f0f0f0;
width: 750px;
margin: 0;
margin-left: auto;
margin-right: auto;
}
#content {
float: left;
background-color: white;
border: 3px solid #aaa;
border-top: none;
padding: 25px;
width: 500px;
}
#sidebar {
float: right;
width: 175px;
}
#footer {
clear: both;
}
#header, #about, #getting-started {
padding-left: 75px;
padding-right: 30px;
}
#header {
background-image: url("images/rails.png");
background-repeat: no-repeat;
background-position: top left;
height: 64px;
}
#header h1, #header h2 {margin: 0}
#header h2 {
color: #888;
font-weight: normal;
font-size: 16px;
}
#about h3 {
margin: 0;
margin-bottom: 10px;
font-size: 14px;
}
#about-content {
background-color: #ffd;
border: 1px solid #fc0;
margin-left: -11px;
}
#about-content table {
margin-top: 10px;
margin-bottom: 10px;
font-size: 11px;
border-collapse: collapse;
}
#about-content td {
padding: 10px;
padding-top: 3px;
padding-bottom: 3px;
}
#about-content td.name {color: #555}
#about-content td.value {color: #000}
#about-content ul {
padding: 0;
list-style-type: none;
}
#about-content.failure {
background-color: #fcc;
border: 1px solid #f00;
}
#about-content.failure p {
margin: 0;
padding: 10px;
}
#getting-started {
border-top: 1px solid #ccc;
margin-top: 25px;
padding-top: 15px;
}
#getting-started h1 {
margin: 0;
font-size: 20px;
}
#getting-started h2 {
margin: 0;
font-size: 14px;
font-weight: normal;
color: #333;
margin-bottom: 25px;
}
#getting-started ol {
margin-left: 0;
padding-left: 0;
}
#getting-started li {
font-size: 18px;
color: #888;
margin-bottom: 25px;
}
#getting-started li h2 {
margin: 0;
font-weight: normal;
font-size: 18px;
color: #333;
}
#getting-started li p {
color: #555;
font-size: 13px;
}
#search {
margin: 0;
padding-top: 10px;
padding-bottom: 10px;
font-size: 11px;
}
#search input {
font-size: 11px;
margin: 2px;
}
#search-text {width: 170px}
#sidebar ul {
margin-left: 0;
padding-left: 0;
}
#sidebar ul h3 {
margin-top: 25px;
font-size: 16px;
padding-bottom: 10px;
border-bottom: 1px solid #ccc;
}
#sidebar li {
list-style-type: none;
}
#sidebar ul.links li {
margin-bottom: 5px;
}
</style>
<script type="text/javascript" src="javascripts/prototype.js"></script>
<script type="text/javascript" src="javascripts/effects.js"></script>
<script type="text/javascript">
function about() {
if (Element.empty('about-content')) {
new Ajax.Updater('about-content', 'rails/info/properties', {
method: 'get',
onFailure: function() {Element.classNames('about-content').add('failure')},
onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
});
} else {
new Effect[Element.visible('about-content') ?
'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
}
}
window.onload = function() {
$('search-text').value = '';
$('search').onsubmit = function() {
$('search-text').value = 'site:rubyonrails.org ' + $F('search-text');
}
}
</script>
</head>
<body>
<div id="page">
<div id="sidebar">
<ul id="sidebar-items">
<li>
<form id="search" action="http://www.google.com/search" method="get">
<input type="hidden" name="hl" value="en" />
<input type="text" id="search-text" name="q" value="site:rubyonrails.org " />
<input type="submit" value="Search" /> the Rails site
</form>
</li>
<li>
<h3>Join the community</h3>
<ul class="links">
<li><a href="http://www.rubyonrails.org/">Ruby on Rails</a></li>
<li><a href="http://weblog.rubyonrails.org/">Official weblog</a></li>
<li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
</ul>
</li>
<li>
<h3>Browse the documentation</h3>
<ul class="links">
<li><a href="http://api.rubyonrails.org/">Rails API</a></li>
<li><a href="http://stdlib.rubyonrails.org/">Ruby standard library</a></li>
<li><a href="http://corelib.rubyonrails.org/">Ruby core</a></li>
<li><a href="http://guides.rubyonrails.org/">Rails Guides</a></li>
</ul>
</li>
</ul>
</div>
<div id="content">
<div id="header">
<h1>Welcome aboard</h1>
<h2>You&rsquo;re riding Ruby on Rails!</h2>
</div>
<div id="about">
<h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
<div id="about-content" style="display: none"></div>
</div>
<div id="getting-started">
<h1>Getting started</h1>
<h2>Here&rsquo;s how to get rolling:</h2>
<ol>
<li>
<h2>Use <code>rails generate</code> to create your models and controllers</h2>
<p>To see all available options, run it without parameters.</p>
</li>
<li>
<h2>Set up a default route and remove or rename this file</h2>
<p>Routes are set up in config/routes.rb.</p>
</li>
<li>
<h2>Create your database</h2>
<p>Run <code>rake db:migrate</code> to create your database. If you're not using SQLite (the default), edit <code>config/database.yml</code> with your username and password.</p>
</li>
</ol>
</div>
</div>
<div id="footer">&nbsp;</div>
</div>
</body>
</html>

View File

@@ -1,17 +0,0 @@
function submit_quick_edit(e) {
e.stopPropagation();
$("#quick-edit").hide();
$.post("/posts.js", $("#quick-edit form").serialize());
}
$(document).ready(function() {
$("#quick-edit form").submit(submit_quick_edit);
$("#post_tag_string").keydown(function(e) {
if (e.keyCode != 13)
return;
submit_quick_edit(e);
e.stopPropagation();
})
$("#mode-box select").click()
PostModeMenu.init();
});

View File

@@ -1,8 +1,8 @@
// Post.init_blacklisted();
// Cookie.setup();
Cookie.setup();
$(document).ready(function() {
$("#hide-upgrade-account-link").click(function() {
$("#upgrade-account").hide();
// Cookie.put('hide-upgrade-account', '1', 7);
Cookie.put('hide-upgrade-account', '1', 7);
});
});

View File

@@ -0,0 +1,58 @@
Cookie = {
put: function(name, value, days) {
if (days == null) {
days = 365;
}
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
document.cookie = name + "=" + encodeURIComponent(value) + expires + "; path=/";
},
raw_get: function(name) {
var nameEq = name + "=";
var ca = document.cookie.split(";");
for (var i = 0; i < ca.length; ++i) {
var c = ca[i];
while (c.charAt(0) == " ") {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEq) == 0) {
return c.substring(nameEq.length, c.length);
}
}
return "";
},
get: function(name) {
return this.unescape(this.raw_get(name));
},
remove: function(name) {
Cookie.put(name, "", -1);
},
unescape: function(val) {
return decodeURIComponent(val.replace(/\+/g, " "));
},
setup: function() {
if (location.href.match(/^\/(comment|pool|note|post)/) && this.get("tos") != "1") {
// Setting location.pathname in Safari doesn't work, so manually extract the domain.
var domain = location.href.match(/^(http:\/\/[^\/]+)/)[0];
location.href = domain + "/static/terms_of_service?url=" + location.href;
return;
}
if (this.get("hide-upgrade-account") != "1") {
if ($("upgrade-account")) {
$("upgrade-account").show();
}
}
}
}

View File

@@ -0,0 +1,168 @@
PostModeMenu = {
init: function() {
this.original_background_color = $(document.body).css("background-color")
if (Cookie.get("mode") == "") {
Cookie.put("mode", "view");
$("#mode-box select").val("view");
} else {
$("#mode-box select").val(Cookie.get("mode"));
}
this.change();
},
change: function() {
var s = $("#mode-box select").val();
Cookie.put("mode", s, 7);
if (s == "view") {
$(document.body).css({"background-color": this.original_background_color});
} else if (s == "edit") {
$(document.body).css({"background-color": "#3A3"});
} else if (s == "add-fav") {
$(document.body).css({"background-color": "#FFA"});
} else if (s == "remove-fav") {
$(document.body).css({"background-color": "#FFA"});
} else if (s == "rating-q") {
$(document.body).css({"background-color": "#AAA"});
} else if (s == "rating-s") {
$(document.body).css({"background-color": "#6F6"});
} else if (s == "rating-e") {
$(document.body).css({"background-color": "#F66"});
} else if (s == "vote-down") {
$(document.body).css({"background-color": "#FAA"});
} else if (s == "vote-up") {
$(document.body).css({"background-color": "#AFA"});
} else if (s == "lock-rating") {
$(document.body).css({"background-color": "#AA3"});
} else if (s == "lock-note") {
$(document.body).css({"background-color": "#3AA"});
} else if (s == "approve") {
$(document.body).css({"background-color": "#26A"});
} else if (s == "unapprove") {
$(document.body).css({"background-color": "#F66"});
} else if (s == "add-to-pool") {
$(document.body).css({"background-color": "#26A"});
} else if (s == "apply-tag-script") {
$(document.body).css({"background-color": "#A3A"});
} else if (s == "edit-tag-script") {
$(document.body).css({"background-color": "#FFF"});
var script = Cookie.get("tag-script");
script = prompt("Enter a tag script", script);
if (script) {
Cookie.put("tag-script", script);
$("#mode-box select").val("apply-tag-script");
} else {
$("#mode-box select").val("view");
}
this.change();
} else {
$(document.body).css({"background-color": "#AFA"});
}
},
click: function(post_id) {
var s = $("#mode-box select").val();
if (s.value == "view") {
return true;
} else if (s.value == "add-fav") {
Favorite.create(post_id);
} else if (s.value == "remove-fav") {
Favorite.destroy(post_id);
} else if (s.value == "edit") {
// TODO
} else if (s.value == 'vote-down') {
PostVote.create("down", post_id);
} else if (s.value == 'vote-up') {
PostVote.create("up", post_id);
} else if (s.value == 'rating-q') {
Post.update(post_id, {"post[rating]": "questionable"});
} else if (s.value == 'rating-s') {
Post.update(post_id, {"post[rating]": "safe"});
} else if (s.value == 'rating-e') {
Post.update(post_id, {"post[rating]": "explicit"});
} else if (s.value == 'lock-rating') {
Post.update(post_id, {"post[is_rating_locked]": "1"});
} else if (s.value == 'lock-note') {
Post.update(post_id, {"post[is_note_locked]": "1"});
} else if (s.value == 'unapprove') {
Unapproval.create(post_id);
} else if (s.value == "approve") {
Post.update(post_id, {"post[is_pending]": "0"});
} else if (s.value == 'add-to-pool') {
Pool.add_post(post_id, 0);
} else if (s.value == "apply-tag-script") {
var tag_script = Cookie.get("tag-script");
TagScript.run(post_id, tag_script);
}
return false
}
}
TagScript = {
parse: function(script) {
return script.match(/\[.+?\]|\S+/g);
},
test: function(tags, predicate) {
var split_pred = predicate.match(/\S+/g);
var is_true = true;
split_pred.each(function(x) {
if (x[0] == "-") {
if (tags.include(x.substr(1, 100))) {
is_true = false
throw $break
}
} else {
if (!tags.include(x)) {
is_true = false
throw $break
}
}
})
return is_true
},
process: function(tags, command) {
if (command.match(/^\[if/)) {
var match = command.match(/\[if\s+(.+?)\s*,\s*(.+?)\]/)
if (TagScript.test(tags, match[1])) {
return TagScript.process(tags, match[2])
} else {
return tags
}
} else if (command == "[reset]") {
return []
} else if (command[0] == "-") {
return tags.reject(function(x) {return x == command.substr(1, 100)})
} else {
tags.push(command)
return tags
}
},
run: function(post_id, tag_script) {
var commands = TagScript.parse(tag_script)
var post = Post.posts.get(post_id)
var old_tags = post.tags.join(" ")
commands.each(function(x) {
post.tags = TagScript.process(post.tags, x)
})
Post.update(post_id, {"post[old_tags]": old_tags, "post[tags]": post.tags.join(" ")})
}
}
$(document).ready(function() {
$("#mode-box select").click(PostModeMenu.change)
PostModeMenu.init();
});

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env sh
mkdir -p public/javascripts/compiled/posts
# Create the base script that's used everywhere
cat public/javascripts/src/rails.js > public/javascripts/compiled/base.js
cat public/javascripts/src/cookie.js > public/javascripts/compiled/base.js
cat public/javascripts/src/application.js >> public/javascripts/compiled/base.js
# Create post/index.js
cat public/javascripts/compiled/base.js > public/javascripts/compiled/posts/index.js
cat public/javascripts/src/posts/index.js >> public/javascripts/compiled/posts/index.js