added notes

This commit is contained in:
Dan Elbert 2016-10-14 12:19:00 -05:00
parent f7ab63e0d3
commit e758af25b2
48 changed files with 433 additions and 28 deletions

View File

@ -6,6 +6,8 @@ gem 'pg', '~> 0.18.4'
gem 'sass-rails', '~> 5.0' gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0' gem 'uglifier', '>= 1.3.0'
gem 'puma'
# See https://github.com/rails/execjs#readme for more supported runtimes # See https://github.com/rails/execjs#readme for more supported runtimes
gem 'therubyracer', platforms: :ruby gem 'therubyracer', platforms: :ruby
@ -27,6 +29,7 @@ group :development, :test do
gem 'guard', '~> 2.14.0' gem 'guard', '~> 2.14.0'
gem 'guard-rspec', require: false gem 'guard-rspec', require: false
gem 'rspec-rails', '~> 3.5.0' gem 'rspec-rails', '~> 3.5.0'
gem 'rails-controller-testing'
gem 'factory_girl_rails', '~> 4.7.0' gem 'factory_girl_rails', '~> 4.7.0'
gem 'database_cleaner', '~> 1.5.3' gem 'database_cleaner', '~> 1.5.3'
# Call 'byebug' anywhere in the code to stop execution and get a debugger console # Call 'byebug' anywhere in the code to stop execution and get a debugger console

View File

@ -126,6 +126,7 @@ GEM
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
slop (~> 3.4) slop (~> 3.4)
puma (3.6.0)
rack (2.0.1) rack (2.0.1)
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
@ -141,6 +142,10 @@ GEM
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 5.0.0) railties (= 5.0.0)
sprockets-rails (>= 2.0.0) sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.1)
actionpack (~> 5.x)
actionview (~> 5.x)
activesupport (~> 5.x)
rails-dom-testing (2.0.1) rails-dom-testing (2.0.1)
activesupport (>= 4.2.0, < 6.0) activesupport (>= 4.2.0, < 6.0)
nokogiri (~> 1.6.0) nokogiri (~> 1.6.0)
@ -240,7 +245,9 @@ DEPENDENCIES
jquery-rails (~> 4.1.1) jquery-rails (~> 4.1.1)
kaminari (~> 0.17.0) kaminari (~> 0.17.0)
pg (~> 0.18.4) pg (~> 0.18.4)
puma
rails (= 5.0.0) rails (= 5.0.0)
rails-controller-testing
rspec-rails (~> 3.5.0) rspec-rails (~> 3.5.0)
sass-rails (~> 5.0) sass-rails (~> 5.0)
sqlite3 sqlite3

View File

@ -0,0 +1,82 @@
class NotesController < ApplicationController
before_action :set_note, only: [:show, :edit, :update, :destroy]
before_action :ensure_valid_user
# GET /notes
# GET /notes.json
def index
@notes = Note.for_user(current_user)
end
# GET /notes/1
# GET /notes/1.json
def show
ensure_owner(@note)
end
# GET /notes/new
def new
@note = Note.new
end
# GET /notes/1/edit
def edit
ensure_owner(@note)
end
# POST /notes
# POST /notes.json
def create
@note = Note.new(note_params)
@note.user = current_user
respond_to do |format|
if @note.save
format.html { redirect_to notes_path, notice: 'Note was successfully created.' }
format.json { render :show, status: :created, location: @note }
else
format.html { render :new }
format.json { render json: @note.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /notes/1
# PATCH/PUT /notes/1.json
def update
ensure_owner(@note) do
respond_to do |format|
if @note.update(note_params)
format.html { redirect_to notes_path, notice: 'Note was successfully updated.' }
format.json { render :show, status: :ok, location: @note }
else
format.html { render :edit }
format.json { render json: @note.errors, status: :unprocessable_entity }
end
end
end
end
# DELETE /notes/1
# DELETE /notes/1.json
def destroy
ensure_owner(@note) do
@note.destroy
respond_to do |format|
format.html { redirect_to notes_url, notice: 'Note was successfully destroyed.' }
format.json { head :no_content }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_note
@note = Note.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def note_params
params.require(:note).permit(:user_id, :content)
end
end

View File

@ -0,0 +1,12 @@
class NoteDecorator < BaseDecorator
def date
v = super
if v && v.respond_to?(:strftime)
v.strftime("%Y-%m-%d")
else
v
end
end
end

View File

@ -26,6 +26,10 @@ module ApplicationHelper
nav_item('About', about_path, 'home') nav_item('About', about_path, 'home')
] ]
if current_user
nav << nav_item('Notes', notes_path, 'notes')
end
if current_user && current_user.admin? if current_user && current_user.admin?
nav << nav_item('Admin', admin_users_path, 'admin/users') nav << nav_item('Admin', admin_users_path, 'admin/users')
end end

View File

@ -0,0 +1,3 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end

View File

@ -1,4 +1,4 @@
class Ingredient < ActiveRecord::Base class Ingredient < ApplicationRecord
include TokenizedLike include TokenizedLike
belongs_to :user belongs_to :user

View File

@ -1,4 +1,4 @@
class IngredientUnit < ActiveRecord::Base class IngredientUnit < ApplicationRecord
belongs_to :ingredient, inverse_of: :ingredient_units belongs_to :ingredient, inverse_of: :ingredient_units
validates :name, presence: true validates :name, presence: true

View File

@ -1,4 +1,4 @@
class Log < ActiveRecord::Base class Log < ApplicationRecord
belongs_to :recipe belongs_to :recipe
belongs_to :source_recipe, class_name: 'Recipe' belongs_to :source_recipe, class_name: 'Recipe'

10
app/models/note.rb Normal file
View File

@ -0,0 +1,10 @@
class Note < ApplicationRecord
belongs_to :user
scope :for_user, ->(user) { where(user_id: user) }
validates :user_id, presence: true
validates :content, presence: true
end

View File

@ -1,4 +1,4 @@
class Recipe < ActiveRecord::Base class Recipe < ApplicationRecord
has_many :recipe_ingredients, -> { order :sort_order }, inverse_of: :recipe, dependent: :destroy has_many :recipe_ingredients, -> { order :sort_order }, inverse_of: :recipe, dependent: :destroy
has_many :recipe_steps, -> { order :sort_order }, inverse_of: :recipe, dependent: :destroy has_many :recipe_steps, -> { order :sort_order }, inverse_of: :recipe, dependent: :destroy

View File

@ -1,4 +1,4 @@
class RecipeIngredient < ActiveRecord::Base class RecipeIngredient < ApplicationRecord
belongs_to :ingredient belongs_to :ingredient
belongs_to :recipe, inverse_of: :recipe_ingredients belongs_to :recipe, inverse_of: :recipe_ingredients

View File

@ -1,4 +1,4 @@
class RecipeStep < ActiveRecord::Base class RecipeStep < ApplicationRecord
belongs_to :recipe, inverse_of: :recipe_steps belongs_to :recipe, inverse_of: :recipe_steps

View File

@ -1,4 +1,4 @@
class UsdaFood < ActiveRecord::Base class UsdaFood < ApplicationRecord
include TokenizedLike include TokenizedLike
has_many :usda_food_weights has_many :usda_food_weights

View File

@ -1,4 +1,4 @@
class UsdaFoodWeight < ActiveRecord::Base class UsdaFoodWeight < ApplicationRecord
belongs_to :usda_food belongs_to :usda_food

View File

@ -1,4 +1,4 @@
class User < ActiveRecord::Base class User < ApplicationRecord
has_many :recipes, dependent: :nullify has_many :recipes, dependent: :nullify
has_many :ingredients, dependent: :nullify has_many :ingredients, dependent: :nullify

View File

@ -0,0 +1,13 @@
<%= form_for(note) do |f| %>
<%= render partial: 'shared/error_list', locals: {model: @note} %>
<div class="form-group">
<%= f.label :content, 'Note', class: 'control-label' %>
<%= f.text_field :content, class: 'form-control' %>
</div>
<div class="actions">
<%= f.submit class: 'btn btn-primary' %>
<%= link_to 'Back', notes_path, class: 'btn btn-default' %>
</div>
<% end %>

View File

@ -0,0 +1,2 @@
json.extract! note, :id, :content, :created_at
json.url note_url(note, format: :json)

View File

@ -0,0 +1,11 @@
<div class="row">
<div class="col-xs-12">
<div class="page-header">
<h1>Edit Note</h1>
</div>
<%= render 'form', note: @note %>
</div>
</div>

View File

@ -0,0 +1,47 @@
<div class="row">
<div class="col-xs-12">
<div class="page-header">
<h1>Notes</h1>
</div>
<% if @notes.empty? %>
<p>No Notes</p>
<% else %>
<%= link_to 'New Note', new_note_path, class: 'btn btn-default' %>
<br/>
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>Note</th>
<th>Date</th>
<th></th>
</tr>
</thead>
<tbody>
<% decorate(@notes, NoteDecorator).each do |note| %>
<tr>
<td><%= note.content %></td>
<td><%= note.created_at %></td>
<td>
<%= link_to note, method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-xs btn-danger' do %>
<span class="glyphicon glyphicon-remove"></span>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
<br/>
<%= link_to 'New Note', new_note_path, class: 'btn btn-default' %>
</div>
</div>

View File

@ -0,0 +1 @@
json.array! @notes, partial: 'notes/note', as: :note

View File

@ -0,0 +1,11 @@
<div class="row">
<div class="col-xs-12">
<div class="page-header">
<h1>Create Note</h1>
</div>
<%= render 'form', note: @note %>
</div>
</div>

View File

@ -0,0 +1,15 @@
<div class="row">
<div class="col-xs-12">
<div class="page-header">
<h1>Note</h1>
</div>
<p>
<%= @note.content %>
</p>
<%= link_to 'Back', notes_path, class: 'btn btn-default' %>
</div>
</div>

View File

@ -0,0 +1 @@
json.partial! "notes/note", note: @note

View File

@ -1,5 +1,6 @@
Rails.application.routes.draw do Rails.application.routes.draw do
resources :notes
resources :recipes do resources :recipes do
resources :logs, only: [:new, :create] resources :logs, only: [:new, :create]
end end

View File

@ -1,4 +1,4 @@
class CreateIngredients < ActiveRecord::Migration class CreateIngredients < ActiveRecord::Migration[4.2]
def change def change
create_table :ingredients do |t| create_table :ingredients do |t|

View File

@ -1,4 +1,4 @@
class CreateRecipes < ActiveRecord::Migration class CreateRecipes < ActiveRecord::Migration[4.2]
def change def change
create_table :recipes do |t| create_table :recipes do |t|

View File

@ -1,4 +1,4 @@
class CreateRecipeIngredients < ActiveRecord::Migration class CreateRecipeIngredients < ActiveRecord::Migration[4.2]
def change def change
create_table :recipe_ingredients do |t| create_table :recipe_ingredients do |t|

View File

@ -1,4 +1,4 @@
class CreateRecipeSteps < ActiveRecord::Migration class CreateRecipeSteps < ActiveRecord::Migration[4.2]
def change def change
create_table :recipe_steps do |t| create_table :recipe_steps do |t|

View File

@ -1,4 +1,4 @@
class AddDeletedToRecipes < ActiveRecord::Migration class AddDeletedToRecipes < ActiveRecord::Migration[4.2]
def change def change
add_column :recipes, :deleted, :boolean add_column :recipes, :deleted, :boolean
end end

View File

@ -1,4 +1,4 @@
class CreateUsers < ActiveRecord::Migration class CreateUsers < ActiveRecord::Migration[4.2]
def change def change
create_table :users do |t| create_table :users do |t|
t.string :username t.string :username

View File

@ -1,4 +1,4 @@
class CreateUsdaFood < ActiveRecord::Migration class CreateUsdaFood < ActiveRecord::Migration[4.2]
def change def change
create_table :usda_foods do |t| create_table :usda_foods do |t|

View File

@ -1,4 +1,4 @@
class AddNdbnToIngredients < ActiveRecord::Migration class AddNdbnToIngredients < ActiveRecord::Migration[4.2]
def change def change
change_table :ingredients do |t| change_table :ingredients do |t|
t.string :ndbn, limit: 5, index: true t.string :ndbn, limit: 5, index: true

View File

@ -1,4 +1,4 @@
class AddFieldsToIngredient < ActiveRecord::Migration class AddFieldsToIngredient < ActiveRecord::Migration[4.2]
def change def change
change_table :ingredients do |t| change_table :ingredients do |t|
t.decimal :water, precision: 10, scale: 2 t.decimal :water, precision: 10, scale: 2

View File

@ -1,4 +1,4 @@
class ChangeIngredients < ActiveRecord::Migration class ChangeIngredients < ActiveRecord::Migration[4.2]
def change def change
change_table :recipe_ingredients do |t| change_table :recipe_ingredients do |t|
t.remove :custom_density t.remove :custom_density

View File

@ -1,4 +1,4 @@
class AddUserIdToIngredient < ActiveRecord::Migration class AddUserIdToIngredient < ActiveRecord::Migration[4.2]
def change def change
add_column :ingredients, :user_id, :integer add_column :ingredients, :user_id, :integer
end end

View File

@ -1,4 +1,4 @@
class CreateUsdaFoodWeights < ActiveRecord::Migration class CreateUsdaFoodWeights < ActiveRecord::Migration[4.2]
def change def change
create_table :usda_food_weights do |t| create_table :usda_food_weights do |t|
t.integer :usda_food_id, index: true, null: false t.integer :usda_food_id, index: true, null: false

View File

@ -1,4 +1,4 @@
class ChangeAmountColumn < ActiveRecord::Migration class ChangeAmountColumn < ActiveRecord::Migration[4.2]
def change def change
change_column :usda_food_weights, :amount, :decimal, precision: 7, scale: 3 change_column :usda_food_weights, :amount, :decimal, precision: 7, scale: 3
end end

View File

@ -1,4 +1,4 @@
class ChangeYieldsCol < ActiveRecord::Migration class ChangeYieldsCol < ActiveRecord::Migration[4.2]
def change def change
change_column :recipes, :yields, :string change_column :recipes, :yields, :string
end end

View File

@ -1,4 +1,4 @@
class AddNutrientsToUsdaFoods < ActiveRecord::Migration class AddNutrientsToUsdaFoods < ActiveRecord::Migration[4.2]
def change def change
change_table 'usda_foods' do |t| change_table 'usda_foods' do |t|
t.integer :calcium t.integer :calcium

View File

@ -1,4 +1,4 @@
class AddNutrientsToIngredients < ActiveRecord::Migration class AddNutrientsToIngredients < ActiveRecord::Migration[4.2]
def change def change
change_table 'ingredients' do |t| change_table 'ingredients' do |t|
t.integer :calcium t.integer :calcium

View File

@ -1,4 +1,4 @@
class AddIngredientUnits < ActiveRecord::Migration class AddIngredientUnits < ActiveRecord::Migration[4.2]
def change def change
create_table :ingredient_units do |t| create_table :ingredient_units do |t|
t.integer :ingredient_id, null: false, index: true t.integer :ingredient_id, null: false, index: true

View File

@ -1,4 +1,4 @@
class CreateLogs < ActiveRecord::Migration class CreateLogs < ActiveRecord::Migration[4.2]
def change def change
create_table :logs do |t| create_table :logs do |t|
t.integer :user_id t.integer :user_id

View File

@ -0,0 +1,10 @@
class CreateNotes < ActiveRecord::Migration[5.0]
def change
create_table :notes do |t|
t.integer :user_id, null: false, index: true
t.text :content, null: false
t.timestamps
end
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160928212209) do ActiveRecord::Schema.define(version: 20161014161544) do
create_table "ingredient_units", force: :cascade do |t| create_table "ingredient_units", force: :cascade do |t|
t.integer "ingredient_id", null: false t.integer "ingredient_id", null: false
@ -65,6 +65,14 @@ ActiveRecord::Schema.define(version: 20160928212209) do
t.text "notes" t.text "notes"
end end
create_table "notes", force: :cascade do |t|
t.integer "user_id", null: false
t.text "content", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_notes_on_user_id"
end
create_table "recipe_ingredients", force: :cascade do |t| create_table "recipe_ingredients", force: :cascade do |t|
t.integer "ingredient_id" t.integer "ingredient_id"
t.integer "recipe_id" t.integer "recipe_id"

View File

@ -0,0 +1,154 @@
require 'rails_helper'
RSpec.describe NotesController, type: :controller do
render_views
let(:user) {
create(:user)
}
let(:valid_attributes) {
{content: 'text'}
}
let(:invalid_attributes) {
{content: ''}
}
let(:valid_session) { {user_id: user.id} }
describe "GET #index" do
it "assigns all user notes as @notes" do
note = create(:note, user: user)
create(:note)
get :index, params: {}, session: valid_session
expect(assigns(:notes)).to eq([note])
end
end
describe "GET #show" do
it "assigns the requested note as @note" do
note = create(:note, user: user)
get :show, params: {id: note.to_param}, session: valid_session
expect(assigns(:note)).to eq(note)
end
end
describe "GET #new" do
it "assigns a new note as @note" do
get :new, params: {}, session: valid_session
expect(assigns(:note)).to be_a_new(Note)
end
end
describe "GET #edit" do
it "assigns the requested note as @note" do
note = create(:note, user: user)
get :edit, params: {id: note.to_param}, session: valid_session
expect(assigns(:note)).to eq(note)
end
it 'redirects if note is not owned' do
note = create(:note)
get :edit, params: {id: note.to_param}, session: valid_session
expect(response).to redirect_to(root_path)
end
end
describe "POST #create" do
context "with valid params" do
it "creates a new Note" do
expect {
post :create, params: {note: valid_attributes}, session: valid_session
}.to change(Note, :count).by(1)
end
it "assigns a newly created note as @note" do
post :create, params: {note: valid_attributes}, session: valid_session
expect(assigns(:note)).to be_a(Note)
expect(assigns(:note)).to be_persisted
end
it "redirects to the created note" do
post :create, params: {note: valid_attributes}, session: valid_session
expect(response).to redirect_to(Note.last)
end
end
context "with invalid params" do
it "assigns a newly created but unsaved note as @note" do
post :create, params: {note: invalid_attributes}, session: valid_session
expect(assigns(:note)).to be_a_new(Note)
end
it "re-renders the 'new' template" do
post :create, params: {note: invalid_attributes}, session: valid_session
expect(response).to render_template("new")
end
end
end
describe "PUT #update" do
context "with valid params" do
let(:new_attributes) {
{content: 'new stuff'}
}
it "updates the requested note" do
note = create(:note, user: user)
put :update, params: {id: note.to_param, note: new_attributes}, session: valid_session
note.reload
expect(note.content).to eq 'new stuff'
end
it "assigns the requested note as @note" do
note = create(:note, user: user)
put :update, params: {id: note.to_param, note: valid_attributes}, session: valid_session
expect(assigns(:note)).to eq(note)
end
it "redirects to the note" do
note = create(:note, user: user)
put :update, params: {id: note.to_param, note: valid_attributes}, session: valid_session
expect(response).to redirect_to(note)
end
it 'redirects if note is not owned' do
note = create(:note)
put :update, params: {id: note.to_param, note: valid_attributes}, session: valid_session
expect(response).to redirect_to(root_path)
end
end
context "with invalid params" do
it "assigns the note as @note" do
note = create(:note, user: user)
put :update, params: {id: note.to_param, note: invalid_attributes}, session: valid_session
expect(assigns(:note)).to eq(note)
end
it "re-renders the 'edit' template" do
note = create(:note, user: user)
put :update, params: {id: note.to_param, note: invalid_attributes}, session: valid_session
expect(response).to render_template("edit")
end
end
end
describe "DELETE #destroy" do
it "destroys the requested note" do
note = create(:note, user: user)
expect {
delete :destroy, params: {id: note.to_param}, session: valid_session
}.to change(Note, :count).by(-1)
end
it "redirects to the notes list" do
note = create(:note, user: user)
delete :destroy, params: {id: note.to_param}, session: valid_session
expect(response).to redirect_to(notes_url)
end
end
end

6
spec/factories/notes.rb Normal file
View File

@ -0,0 +1,6 @@
FactoryGirl.define do
factory :note do
user
content "MyText"
end
end

4
spec/models/note_spec.rb Normal file
View File

@ -0,0 +1,4 @@
require 'rails_helper'
RSpec.describe Note, type: :model do
end