Skip to content

Commit

Permalink
Merge pull request #1 from ltfschoen/feature/csv
Browse files Browse the repository at this point in the history
Feature / CSV Upload and Display - Tests pass. Updated controller, models, views.
  • Loading branch information
ltfschoen authored Mar 7, 2017
2 parents e0e1d5b + 7b9dd8d commit 3905bd2
Show file tree
Hide file tree
Showing 15 changed files with 180 additions and 4 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platform: :mri
gem 'rspec-rails', '~> 3.5.2'
gem 'rails-controller-testing', '~> 1.0.1'
end

group :development do
Expand Down
5 changes: 5 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ GEM
bundler (>= 1.3.0, < 2.0)
railties (= 5.0.2)
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.2)
activesupport (>= 4.2.0, < 6.0)
nokogiri (~> 1.6)
Expand Down Expand Up @@ -180,6 +184,7 @@ DEPENDENCIES
pg (~> 0.18)
puma (~> 3.0)
rails (~> 5.0.0, >= 5.0.0.1)
rails-controller-testing (~> 1.0.1)
rspec-rails (~> 3.5.2)
sass-rails (~> 5.0)
spring
Expand Down
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@
* [Setup - Legacy Initial Steps](#part-2000)
* [Setup - Replace Unit Test with RSpec](#part-3000)
* [Setup - Git Repo](#part-4000)
* [Setup - Git Releases and Tags](#part-5000)

---

## Goals

* [X] - Import pre-populated CSV into database from web form.
* [X] - Present populated data from database in table view
* [ ] - Use AJAX and apply basic filters on table so data updated without refreshing whole page.

## System Requirements and Info<a id="part-1000"></a>
* Show System Setup
`rails about`
Expand All @@ -24,6 +31,7 @@
* Rack - 2.0.1
* Node.js - 7.7.1 (V8 runtime)
* PostgreSQL - 9.6.2
* RSpec - 3.5.4
* OS - macOS El Capitan

* Show Codebase Stats
Expand All @@ -38,6 +46,11 @@
* Open PostgreSQL Database console automatically http://guides.rubyonrails.org/command_line.html
`rails dbconsole`

* Show database table contents
```
select * from products;
```
## Documentation Links <a id="part-1500"></a>
* Testing
Expand Down Expand Up @@ -101,8 +114,7 @@
* Migrate into PostgreSQL Database
```
rake db:create
rake db:migrate RAILS_ENV=development
rake db:create db:migrate RAILS_ENV=development
```
* Launch the Rails server in separate Terminal tab automatically and opens it in web browser after 10 seconds using Shell Script:
Expand Down Expand Up @@ -149,4 +161,9 @@
`git pull --rebase origin master`
* Force push to remote branch to overwrite existing history
`git push -f origin master`
`git push -f origin master`
## Setup - Git Release and Tags <a id="part-5000"></a>
* Create New Release https://github.com/ltfschoen/rails_csv_app/releases/new
* Pre-Release (non-production) i.e. v0.1
3 changes: 3 additions & 0 deletions app/assets/javascripts/products.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
3 changes: 3 additions & 0 deletions app/assets/stylesheets/products.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Place all the styles related to the Products controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
17 changes: 17 additions & 0 deletions app/controllers/products_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class ProductsController < ApplicationController
def index
@products = Product.all
end

def import
# Validate inputs with block
begin
file = params[:file]
file_path = file.path
Product.import(file_path)
redirect_to root_url, notice: "Products imported."
rescue
redirect_to root_url, notice: "Invalid CSV file format."
end
end
end
18 changes: 18 additions & 0 deletions app/models/product.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Product < ApplicationRecord
require 'csv'

def self.import(file_path)
CSV.foreach(file_path, headers: true) do |row|

product_hash = row.to_hash
product = Product.where(id: product_hash["id"])

if product.count == 1
# Prevent CSV updates from changing the database comments attribute
product.first.update_attributes(product_hash.expect("comments"))
else
Product.create!(product_hash)
end
end
end
end
2 changes: 2 additions & 0 deletions app/views/products/import.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Products#import</h1>
<p>Find me in app/views/products/import.html.erb</p>
30 changes: 30 additions & 0 deletions app/views/products/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<%= flash[:notice] %>
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Comments</th>
</tr>
</thead>
<tbody>
<% @products.each do |product| %>
<tr>
<td><%= product.id %></td>
<td><%= product.name %></td>
<td><%= product.price %></td>
<td><%= product.quantity %></td>
<td><%= product.comments %></td>
</tr>
<% end %>
</tbody>
</table>
<div>
<h3>Import a CSV File</h3>
<%= form_tag import_products_path, multipart: true do %>
<%= file_field_tag :file %>
<%= submit_tag "Import CSV" %>
<% end %>
</div>
5 changes: 5 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Rails.application.routes.draw do
resources :products do
collection { post :import }
end

root to: "products#index"
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
12 changes: 12 additions & 0 deletions db/migrate/20170307044647_create_products.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CreateProducts < ActiveRecord::Migration[5.0]
def change
create_table :products do |t|
t.string :name
t.integer :quantity
t.decimal :price, precision: 12, scale: 2
t.string :comments

t.timestamps
end
end
end
11 changes: 10 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,18 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 0) do
ActiveRecord::Schema.define(version: 20170307044647) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

create_table "products", force: :cascade do |t|
t.string "name"
t.integer "quantity"
t.decimal "price", precision: 12, scale: 2
t.string "comments"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

end
5 changes: 5 additions & 0 deletions products.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id,name,quantity,price,comments
1,Guitar,20,199.99,none
2,Trumpet,5,299.99,none
3,Piano,3,699.99,none
4,Clarinet,10,59.99,none
26 changes: 26 additions & 0 deletions spec/controllers/products_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'rails_helper'

RSpec.describe ProductsController, type: :controller do

describe "GET #index" do
it "assigns @products" do
product = Product.create
get :index
expect(assigns(:products)).to eq([product])
end

it "renders the index template" do
get :index
expect(response).to render_template("index")
end
end

describe "GET #import" do
it "redirects upon CSV import success to root url with success message" do
end

it "redirects upon CSV import exception to root url with error message" do
end
end

end
23 changes: 23 additions & 0 deletions spec/models/product_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'rails_helper'

RSpec.describe Product, type: :model do

describe 'Class' do
subject { Product }

it { should respond_to(:import) }

let(:data) { "id,name,quantity,price,comments\r1,Guitar,20,199.99,none" }

describe "#import" do
it "should create a new record if id does not exist" do
File.stub(:open).with("filename", {:universal_newline=>false, :headers=>true}) {
StringIO.new(data)
}
Product.import("filename")
expect(Product.find_by(name: 'Guitar').price).to eq 199.99
end
end
end

end

0 comments on commit 3905bd2

Please sign in to comment.