first pass at closet lists, including form

This commit is contained in:
Emi Matchu 2011-07-26 20:27:23 -04:00
parent 605fb88046
commit b86ce67c02
21 changed files with 278 additions and 46 deletions

View file

@ -7,6 +7,10 @@ class ApplicationController < ActionController::Base
redirect_to(login_path) unless user_signed_in?
end
def authorize_user!
raise AccessDenied unless user_signed_in? && current_user.id == params[:user_id].to_i
end
def can_use_image_mode?
user_signed_in? && current_user.image_mode_tester?
end

View file

@ -67,10 +67,6 @@ class ClosetHangersController < ApplicationController
protected
def authorize_user!
raise AccessDenied unless user_signed_in? && current_user.id == params[:user_id].to_i
end
def find_item
@item = Item.find params[:item_id]
end

View file

@ -0,0 +1,19 @@
class ClosetListsController < ApplicationController
before_filter :authorize_user!
def new
@closet_list = current_user.closet_lists.build
end
def create
@closet_list = current_user.closet_lists.build params[:closet_list]
if @closet_list.save
flash[:success] = "Successfully saved \"#{@closet_list.name}\""
redirect_to user_closet_hangers_path(current_user)
else
flash.now[:alert] = "We can't save this list because: #{@closet_list.errors.full_messages.to_sentence}"
render :action => :new
end
end
end

View file

@ -0,0 +1,9 @@
module ClosetListsHelper
def hangers_owned_options
@hangers_owned_options ||= [true, false].map do |owned|
verb = ClosetHanger.verb(:i, owned)
["items I #{verb}", owned]
end
end
end

View file

@ -1,5 +1,6 @@
class ClosetHanger < ActiveRecord::Base
belongs_to :item
belongs_to :list, :class_name => 'ClosetList'
belongs_to :user
attr_accessible :owned, :quantity
@ -11,13 +12,19 @@ class ClosetHanger < ActiveRecord::Base
scope :alphabetical_by_item_name, joins(:item).order(Item.arel_table[:name])
scope :owned_before_wanted, order(arel_table[:owned].desc)
before_validation :set_owned_by_list
def set_owned_by_list
self.owned = list.hangers_owned if list?
end
def verb(subject=:someone)
self.class.verb(subject, owned?)
end
def self.verb(subject, owned, positive=true)
base = (owned) ? 'own' : 'want'
base << 's' if positive && subject != :you
base << 's' if positive && subject != :you && subject != :i
base
end
end

22
app/models/closet_list.rb Normal file
View file

@ -0,0 +1,22 @@
class ClosetList < ActiveRecord::Base
belongs_to :user
has_many :hangers, :class_name => 'ClosetHanger'
attr_accessible :description, :hangers_owned, :name
validates :name, :presence => true, :uniqueness => {:scope => :user_id}
validates :user, :presence => true
validates :hangers_owned, :inclusion => {:in => [true, false], :message => "can't be blank"}
after_save :sync_hangers_owned!
def sync_hangers_owned!
if hangers_owned_changed?
hangers.each do |hanger|
hanger.owned = hangers_owned
hanger.save!
end
end
end
end

View file

@ -5,6 +5,7 @@ class User < ActiveRecord::Base
PreviewTopContributorsCount = 3
has_many :closet_hangers
has_many :closet_lists
has_many :closeted_items, :through => :closet_hangers, :source => :item
has_many :contributions
has_many :outfits

View file

@ -76,7 +76,7 @@ $container_width: 800px
input, button, select, label
cursor: pointer
input[type=text], input[type=password], input[type=search], input[type=number], select
input[type=text], input[type=password], input[type=search], input[type=number], select, textarea
+border-radius(3px)
background: #fff
border: 1px solid $input-border-color
@ -85,6 +85,9 @@ input[type=text], input[type=password], input[type=search], input[type=number],
&:focus, &:active
color: inherit
textarea
font: inherit
a.button, input[type=submit], button
+awesome-button
&.loud

View file

@ -0,0 +1,28 @@
body.closet_lists-new, body.closet_lists-create
form ul.fields
list-style: none
label
float: left
font-weight: bold
margin-right: 1em
li
padding: 0.75em 0
width: 25em
input, textarea, select
clear: both
display: block
margin-top: .25em
width: 80%
textarea
height: 12em
.hint
display: block
font:
size: 85%

View file

@ -7,6 +7,7 @@
@import partials/jquery.jgrowl
@import closet_hangers/index
@import closet_lists/form
@import closet_pages/new
@import contributions/index
@import items

View file

@ -0,0 +1,17 @@
= form_for [@closet_list.user, @closet_list] do |f|
%ul.fields
%li
= f.label :name
%span.hint Like "up for trade" or "NC wishlist"
= f.text_field :name, :required => true
%li
= f.label :hangers_owned, 'This is a list for&hellip;'.html_safe
= f.select :hangers_owned, hangers_owned_options
%li
= f.label :description
%span.hint
Why are these items in a list? What are your terms for trading?
Or you can leave this blank.
= f.text_area :description
= f.submit 'Save list'

View file

@ -0,0 +1,3 @@
- title 'Create an items list'
= render 'form'

View file

@ -1,4 +1,8 @@
OpenneoImpressItems::Application.routes.draw do |map|
get "closet_lists/new"
get "closet_lists/create"
root :to => 'outfits#new'
devise_for :users
@ -38,6 +42,7 @@ OpenneoImpressItems::Application.routes.draw do |map|
resources :users, :path => 'user', :only => [:update] do
resources :contributions, :only => [:index]
resources :closet_hangers, :only => [:index], :path => 'closet'
resources :closet_lists, :only => [:new, :create], :path => 'closet/lists'
resources :items, :only => [] do
resource :closet_hanger, :only => [:create, :update, :destroy]

View file

@ -0,0 +1,21 @@
class CreateClosetLists < ActiveRecord::Migration
def self.up
create_table :closet_lists do |t|
t.string :name
t.text :description
t.integer :user_id
t.boolean :hangers_owned, :null => false
t.timestamps
end
add_column :closet_hangers, :closet_list_id, :integer
end
def self.down
drop_table :closet_lists
remove_column :closet_hangers, :closet_list_id
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20110722180616) do
ActiveRecord::Schema.define(:version => 20110726231143) do
create_table "auth_servers", :force => true do |t|
t.string "short_name", :limit => 10, :null => false
@ -26,7 +26,17 @@ ActiveRecord::Schema.define(:version => 20110722180616) do
t.integer "quantity"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "owned", :default => true, :null => false
t.boolean "owned", :default => true, :null => false
t.integer "closet_list_id"
end
create_table "closet_lists", :force => true do |t|
t.string "name"
t.text "description"
t.integer "user_id"
t.boolean "hangers_owned", :null => false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "contributions", :force => true do |t|

View file

@ -97,7 +97,7 @@ input, button, select, label {
}
/* line 79, ../../../app/stylesheets/_layout.sass */
input[type=text], body.pets-bulk #bulk-pets-form textarea, input[type=password], input[type=search], input[type=number], select {
input[type=text], body.pets-bulk #bulk-pets-form textarea, input[type=password], input[type=search], input[type=number], select, textarea {
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
background: white;
@ -106,11 +106,16 @@ input[type=text], body.pets-bulk #bulk-pets-form textarea, input[type=password],
padding: 0.25em;
}
/* line 85, ../../../app/stylesheets/_layout.sass */
input[type=text]:focus, body.pets-bulk #bulk-pets-form textarea:focus, input[type=text]:active, body.pets-bulk #bulk-pets-form textarea:active, input[type=password]:focus, input[type=password]:active, input[type=search]:focus, input[type=search]:active, input[type=number]:focus, input[type=number]:active, select:focus, select:active {
input[type=text]:focus, body.pets-bulk #bulk-pets-form textarea:focus, input[type=text]:active, body.pets-bulk #bulk-pets-form textarea:active, input[type=password]:focus, input[type=password]:active, input[type=search]:focus, input[type=search]:active, input[type=number]:focus, input[type=number]:active, select:focus, select:active, textarea:focus, textarea:active {
color: inherit;
}
/* line 88, ../../../app/stylesheets/_layout.sass */
textarea {
font: inherit;
}
/* line 91, ../../../app/stylesheets/_layout.sass */
a.button, input[type=submit], button {
/* http://www.zurb.com/blog_uploads/0000/0617/buttons-03.html */
-moz-border-radius: 5px;
@ -141,7 +146,7 @@ a.button:hover, input[type=submit]:hover, button:hover {
a.button:active, input[type=submit]:active, button:active {
top: 1px;
}
/* line 90, ../../../app/stylesheets/_layout.sass */
/* line 93, ../../../app/stylesheets/_layout.sass */
a.button.loud, input[type=submit].loud, button.loud {
background: #ff5c00 url('/images/alert-overlay.png?1296599919') repeat-x;
font-size: 125%;
@ -157,21 +162,21 @@ a.button:after {
content: " >>";
}
/* line 96, ../../../app/stylesheets/_layout.sass */
/* line 99, ../../../app/stylesheets/_layout.sass */
ul.buttons {
margin-bottom: 1em;
}
/* line 98, ../../../app/stylesheets/_layout.sass */
/* line 101, ../../../app/stylesheets/_layout.sass */
ul.buttons li {
list-style: none;
margin: 0 0.5em;
}
/* line 101, ../../../app/stylesheets/_layout.sass */
/* line 104, ../../../app/stylesheets/_layout.sass */
ul.buttons li, ul.buttons li form {
display: inline;
}
/* line 104, ../../../app/stylesheets/_layout.sass */
/* line 107, ../../../app/stylesheets/_layout.sass */
#footer {
clear: both;
font-size: 75%;
@ -179,65 +184,65 @@ ul.buttons li, ul.buttons li form {
padding-top: 2em;
text-align: center;
}
/* line 110, ../../../app/stylesheets/_layout.sass */
/* line 113, ../../../app/stylesheets/_layout.sass */
#footer ul, #footer div {
display: inline;
margin: 0 1em;
}
/* line 113, ../../../app/stylesheets/_layout.sass */
/* line 116, ../../../app/stylesheets/_layout.sass */
#footer li, #footer div ul {
display: inline;
margin: 0 0.5em;
}
/* line 117, ../../../app/stylesheets/_layout.sass */
/* line 120, ../../../app/stylesheets/_layout.sass */
.success, .alert, .warning {
margin-bottom: 1em;
padding: 0.25em 0.5em;
text-align: center;
}
/* line 122, ../../../app/stylesheets/_layout.sass */
/* line 125, ../../../app/stylesheets/_layout.sass */
.success {
background: #e6efc2;
border: 1px solid #c6d880;
color: #264409;
}
/* line 125, ../../../app/stylesheets/_layout.sass */
/* line 128, ../../../app/stylesheets/_layout.sass */
.alert {
background: #fbe3e4;
border: 1px solid #fbc2c4;
color: #8a1f11;
}
/* line 128, ../../../app/stylesheets/_layout.sass */
/* line 131, ../../../app/stylesheets/_layout.sass */
.warning {
background: #fff6bf;
border: 1px solid #ffd324;
color: #514721;
}
/* line 131, ../../../app/stylesheets/_layout.sass */
/* line 134, ../../../app/stylesheets/_layout.sass */
#userbar {
font-family: Delicious, Helvetica, Arial, Verdana, sans-serif;
position: absolute;
right: 0;
top: 0;
}
/* line 136, ../../../app/stylesheets/_layout.sass */
/* line 139, ../../../app/stylesheets/_layout.sass */
#userbar > * {
display: inline;
margin: 0 0.25em;
}
/* line 140, ../../../app/stylesheets/_layout.sass */
/* line 143, ../../../app/stylesheets/_layout.sass */
#userbar-image-mode {
font-weight: bold;
margin-right: 1em;
text-decoration: none;
}
/* line 144, ../../../app/stylesheets/_layout.sass */
/* line 147, ../../../app/stylesheets/_layout.sass */
#userbar-image-mode img {
bottom: -2px;
height: 16px;
@ -245,25 +250,25 @@ ul.buttons li, ul.buttons li form {
width: 16px;
}
/* line 147, ../../../app/stylesheets/_layout.sass */
/* line 150, ../../../app/stylesheets/_layout.sass */
#userbar-log-in {
text-decoration: none;
}
/* line 149, ../../../app/stylesheets/_layout.sass */
/* line 152, ../../../app/stylesheets/_layout.sass */
#userbar-log-in img {
margin-bottom: -4px;
margin-right: 0.25em;
}
/* line 153, ../../../app/stylesheets/_layout.sass */
/* line 156, ../../../app/stylesheets/_layout.sass */
#userbar-log-in span {
text-decoration: underline;
}
/* line 155, ../../../app/stylesheets/_layout.sass */
/* line 158, ../../../app/stylesheets/_layout.sass */
#userbar-log-in:hover span {
text-decoration: none;
}
/* line 158, ../../../app/stylesheets/_layout.sass */
/* line 161, ../../../app/stylesheets/_layout.sass */
.object {
display: -moz-inline-box;
-moz-box-orient: vertical;
@ -278,32 +283,32 @@ ul.buttons li, ul.buttons li form {
vertical-align: top;
width: 100px;
}
/* line 166, ../../../app/stylesheets/_layout.sass */
/* line 169, ../../../app/stylesheets/_layout.sass */
.object a {
text-decoration: none;
}
/* line 168, ../../../app/stylesheets/_layout.sass */
/* line 171, ../../../app/stylesheets/_layout.sass */
.object a img {
-moz-opacity: 0.75;
-webkit-opacity: 0.75;
-o-opacity: 0.75;
-khtml-opacity: 0.75;
}
/* line 170, ../../../app/stylesheets/_layout.sass */
/* line 173, ../../../app/stylesheets/_layout.sass */
.object img {
display: block;
height: 80px;
margin: 0 auto;
width: 80px;
}
/* line 175, ../../../app/stylesheets/_layout.sass */
/* line 178, ../../../app/stylesheets/_layout.sass */
.object:hover img, .object a:hover img {
-moz-opacity: 1;
-webkit-opacity: 1;
-o-opacity: 1;
-khtml-opacity: 1;
}
/* line 181, ../../../app/stylesheets/_layout.sass */
/* line 184, ../../../app/stylesheets/_layout.sass */
.object .nc-icon, .object .closeted-icons {
-moz-opacity: 1;
-webkit-opacity: 1;
@ -314,7 +319,7 @@ ul.buttons li, ul.buttons li form {
position: absolute;
top: 64px;
}
/* line 187, ../../../app/stylesheets/_layout.sass */
/* line 190, ../../../app/stylesheets/_layout.sass */
.object .nc-icon:hover, .object .closeted-icons:hover {
-moz-opacity: 0.5;
-webkit-opacity: 0.5;
@ -322,32 +327,32 @@ ul.buttons li, ul.buttons li form {
-khtml-opacity: 0.5;
background: transparent;
}
/* line 191, ../../../app/stylesheets/_layout.sass */
/* line 194, ../../../app/stylesheets/_layout.sass */
.object .nc-icon, .object .closeted-icons img {
display: inline;
height: 16px;
width: 16px;
}
/* line 196, ../../../app/stylesheets/_layout.sass */
/* line 199, ../../../app/stylesheets/_layout.sass */
.object .nc-icon {
right: 18px;
}
/* line 199, ../../../app/stylesheets/_layout.sass */
/* line 202, ../../../app/stylesheets/_layout.sass */
.object .closeted-icons {
left: 18px;
}
/* line 202, ../../../app/stylesheets/_layout.sass */
/* line 205, ../../../app/stylesheets/_layout.sass */
dt {
font-weight: bold;
}
/* line 205, ../../../app/stylesheets/_layout.sass */
/* line 208, ../../../app/stylesheets/_layout.sass */
dd {
margin: 0 0 1.5em 1em;
}
/* line 208, ../../../app/stylesheets/_layout.sass */
/* line 211, ../../../app/stylesheets/_layout.sass */
#home-link {
font-family: Delicious, Helvetica, Arial, Verdana, sans-serif;
font-size: 175%;
@ -358,21 +363,21 @@ dd {
position: absolute;
top: 0;
}
/* line 218, ../../../app/stylesheets/_layout.sass */
/* line 221, ../../../app/stylesheets/_layout.sass */
#home-link:hover {
background: #eeffee;
text-decoration: none;
}
/* line 221, ../../../app/stylesheets/_layout.sass */
/* line 224, ../../../app/stylesheets/_layout.sass */
#home-link span:before {
content: "<< ";
}
/* line 225, ../../../app/stylesheets/_layout.sass */
/* line 228, ../../../app/stylesheets/_layout.sass */
.pagination a, .pagination span {
margin: 0 0.5em;
}
/* line 227, ../../../app/stylesheets/_layout.sass */
/* line 230, ../../../app/stylesheets/_layout.sass */
.pagination .current {
font-weight: bold;
}
@ -863,6 +868,38 @@ body.closet_hangers-index.current-user.js .closet-hangers-group.hidden header .s
display: block;
}
/* line 3, ../../../app/stylesheets/closet_lists/_form.sass */
body.closet_lists-new form ul.fields, body.closet_lists-create form ul.fields {
list-style: none;
}
/* line 6, ../../../app/stylesheets/closet_lists/_form.sass */
body.closet_lists-new form ul.fields label, body.closet_lists-create form ul.fields label {
float: left;
font-weight: bold;
margin-right: 1em;
}
/* line 11, ../../../app/stylesheets/closet_lists/_form.sass */
body.closet_lists-new form ul.fields li, body.closet_lists-create form ul.fields li {
padding: 0.75em 0;
width: 25em;
}
/* line 15, ../../../app/stylesheets/closet_lists/_form.sass */
body.closet_lists-new form ul.fields input, body.closet_lists-new form ul.fields textarea, body.closet_lists-new form ul.fields select, body.closet_lists-create form ul.fields input, body.closet_lists-create form ul.fields textarea, body.closet_lists-create form ul.fields select {
clear: both;
display: block;
margin-top: 0.25em;
width: 80%;
}
/* line 21, ../../../app/stylesheets/closet_lists/_form.sass */
body.closet_lists-new form ul.fields textarea, body.closet_lists-create form ul.fields textarea {
height: 12em;
}
/* line 24, ../../../app/stylesheets/closet_lists/_form.sass */
body.closet_lists-new form ul.fields .hint, body.closet_lists-create form ul.fields .hint {
display: block;
font-size: 85%;
}
/* line 3, ../../../app/stylesheets/closet_pages/_new.sass */
body.closet_pages-new #title, body.closet_pages-create #title {
float: left;

View file

@ -0,0 +1,19 @@
require 'spec_helper'
describe ClosetListsController do
describe "GET 'new'" do
it "should be successful" do
get 'new'
response.should be_success
end
end
describe "GET 'create'" do
it "should be successful" do
get 'create'
response.should be_success
end
end
end

View file

@ -0,0 +1,15 @@
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the ClosetListsHelper. For example:
#
# describe ClosetListsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe ClosetListsHelper do
pending "add some examples to (or delete) #{__FILE__}"
end

View file

@ -0,0 +1,5 @@
require 'spec_helper'
describe ClosetList do
pending "add some examples to (or delete) #{__FILE__}"
end

View file

@ -0,0 +1,5 @@
require 'spec_helper'
describe "closet_lists/create.html.erb" do
pending "add some examples to (or delete) #{__FILE__}"
end

View file

@ -0,0 +1,5 @@
require 'spec_helper'
describe "closet_lists/new.html.erb" do
pending "add some examples to (or delete) #{__FILE__}"
end