+ );
+ }
+}
+
diff --git a/app/controllers/sales_batches_controller.rb b/app/controllers/sales_batches_controller.rb
index 6ee37063..cca3f507 100644
--- a/app/controllers/sales_batches_controller.rb
+++ b/app/controllers/sales_batches_controller.rb
@@ -9,7 +9,12 @@ def new
def create
@batch = SalesBatch.create(upload_params)
- redirect_to sales_batch_path(@batch)
+
+ if @batch.persisted?
+ redirect_to sales_batches_path
+ else
+ render json: { error: @batch.errors }, status: :unprocessable_entity
+ end
end
def show
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
new file mode 100644
index 00000000..54b106ee
--- /dev/null
+++ b/app/javascript/packs/application.js
@@ -0,0 +1,10 @@
+/* eslint no-console:0 */
+// This file is automatically compiled by Webpack, along with any other files
+// present in this directory. You're encouraged to place your actual application logic in
+// a relevant structure within app/javascript and only use these pack files to reference
+// that code so it'll be compiled.
+//
+// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
+// layout file, like app/views/layouts/application.html.erb
+
+console.log('Hello World from Webpacker')
diff --git a/app/javascript/packs/hello_react.jsx b/app/javascript/packs/hello_react.jsx
new file mode 100644
index 00000000..772fc97e
--- /dev/null
+++ b/app/javascript/packs/hello_react.jsx
@@ -0,0 +1,26 @@
+// Run this example by adding <%= javascript_pack_tag 'hello_react' %> to the head of your layout file,
+// like app/views/layouts/application.html.erb. All it does is render
Hello React
at the bottom
+// of the page.
+
+import React from 'react'
+import ReactDOM from 'react-dom'
+import PropTypes from 'prop-types'
+
+const Hello = props => (
+
Hello {props.name}!
+)
+
+Hello.defaultProps = {
+ name: 'David'
+}
+
+Hello.propTypes = {
+ name: PropTypes.string
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ ReactDOM.render(
+ ,
+ document.body.appendChild(document.createElement('div')),
+ )
+})
diff --git a/app/models/concerns/sales_from_file.rb b/app/models/concerns/sales_from_file.rb
deleted file mode 100644
index a939d893..00000000
--- a/app/models/concerns/sales_from_file.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require 'csv'
-
-module Concerns
- module SalesFromFile
- extend ActiveSupport::Concern
-
- CSV_HEADERS = %w(customer description unit_price amount address supplier)
-
- private
-
- #TODO: Refactor this to use uploader filename
- #TODO: Refactor this to move to Sidekiq
- #TODO: Refactor this to reduce method lines
- def process_data_file!
- filename = self.attachment.file.file
- data = CSV.read(filename, col_sep: "\t")
- data.delete_at(0)
-
- sales_attrs = data.map do |row|
- row.each_with_index.map do |value, index|
- Hash[ CSV_HEADERS[index], value ]
- end.reduce(&:merge)
- end
-
- sales_attrs.each do |sale_attr|
- self.sales.create(sale_attr)
- end
-
- total_revenue = self.sales.map{|sale| sale.amount * sale.unit_price }.reduce(:+)
- self.update_attributes({
- processed: true,
- revenue: total_revenue,
- batch_code: SecureRandom.uuid
- })
- end
- end
-end
diff --git a/app/models/sales_batch.rb b/app/models/sales_batch.rb
index 5ad186c6..c9fb896f 100644
--- a/app/models/sales_batch.rb
+++ b/app/models/sales_batch.rb
@@ -4,20 +4,58 @@
#
# id :integer not null, primary key
# attachment :string
-# processed :boolean default(FALSE)
# revenue :decimal(12, 2) default(0.0)
# created_at :datetime not null
# updated_at :datetime not null
# batch_code :string
+# state :string
#
class SalesBatch < ActiveRecord::Base
- include Concerns::SalesFromFile
-
has_many :sales, dependent: :destroy
-
- validates :batch_code, uniqueness: true, presence: false
+ before_create :generate_batch_code
+ after_create :queue!
+ validates :batch_code, uniqueness: true
mount_uploader :attachment, SalesUploader
- after_create :process_data_file!
+
+ state_machine :state, :initial => :uploaded do
+
+ after_transition on: :queue, do: :queue_on_sidekiq
+ event :queue do
+ transition :uploaded => :queued
+ end
+
+ event :process do
+ transition [:queued, :processing] => :processing
+ end
+
+ event :resolve do
+ transition :processing => :resolved
+ end
+
+ event :reject do
+ transition :processing => :rejected
+ end
+ end
+
+ def total_sales
+ self.sales.count
+ end
+
+ #TODO: Comment this hack on codereview
+ def as_json(options={})
+ options[:methods] = [:total_sales]
+ super
+ end
+
+ private
+
+ def generate_batch_code
+ self.batch_code = SecureRandom.uuid
+ end
+
+ def queue_on_sidekiq
+ SalesBatchWorker.perform_async(self.id)
+ end
end
diff --git a/app/uploaders/sales_uploader.rb b/app/uploaders/sales_uploader.rb
index 7e4c00d7..aa065d64 100644
--- a/app/uploaders/sales_uploader.rb
+++ b/app/uploaders/sales_uploader.rb
@@ -47,5 +47,4 @@ def extension_white_list
# def filename
# "something.jpg" if original_filename
# end
-
end
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 062a7776..b70ec718 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -3,11 +3,13 @@
Splunka
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
- <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
+ <%= javascript_include_tag 'application' %>
<%= csrf_meta_tags %>
+ <%= render partial: 'shareds/navbar' %>
+
Para criar um novo lote de vendas, selecione um arquivo no formato '.txt'
com dados separados por TAB.
A primeira linha do arquivo deve conter os nome das colunas obrigatóriamente:
+
-
- Você pode baixar um arquivo de <%= link_to "exemplo aqui", "/sales.txt" %>.
+ <%= link_to "/sales.txt", class: 'btn btn-success' do %>
+
+ Download de modelo de Arquivo de lote
+ <% end %>
+
+
+
+ <%= f.file_field :attachment, class: 'form-control' %>
+ <%= f.button :send, class: 'btn btn-success' do %>
+
+ Enviar novo arquivo de lote
+ <% end %>
+
<%end%>
-<%= link_to "Voltar para home", root_path %>
+<%= link_to root_path, class: "btn btn-info" do %>
+
+ Voltar
+<% end %>
diff --git a/app/views/shareds/_navbar.html.erb b/app/views/shareds/_navbar.html.erb
new file mode 100644
index 00000000..b640aa3a
--- /dev/null
+++ b/app/views/shareds/_navbar.html.erb
@@ -0,0 +1,45 @@
+
diff --git a/app/workers/sales_batch_worker.rb b/app/workers/sales_batch_worker.rb
new file mode 100644
index 00000000..cb28edc5
--- /dev/null
+++ b/app/workers/sales_batch_worker.rb
@@ -0,0 +1,50 @@
+require 'csv'
+
+class SalesBatchWorker
+ include Sidekiq::Worker
+
+ sidekiq_options queue: :sales
+
+ CSV_HEADERS = %w(customer description unit_price amount address supplier)
+
+ def perform(id)
+ batch = SalesBatch.find(id)
+
+ batch.process!
+
+ tsv = generate_tsv(batch.attachment.file.path)
+
+ generate_sales_attrs(tsv).each do |sale_attr|
+ batch.sales.create(sale_attr)
+ end
+
+ calc_revenue(batch)
+ batch.resolve!
+ # rescue => e
+ # logger.error "Erro de execução de job no sidekiq #{e}"
+ # batch.reject!
+ end
+
+ private
+
+ def calc_revenue(batch)
+ total_revenue = batch.sales.map do |sale|
+ sale.amount * sale.unit_price
+ end.reduce(:+)
+ batch.update_attributes({ revenue: total_revenue })
+ end
+
+ def generate_sales_attrs(tsv)
+ tsv.map do |row|
+ row.each_with_index.map do |value, index|
+ Hash[ CSV_HEADERS[index], value ]
+ end.reduce(&:merge)
+ end
+ end
+
+ def generate_tsv(file_path)
+ data = CSV.read(file_path, col_sep: "\t")
+ data.delete_at(0)
+ data
+ end
+end
diff --git a/bin/webpack b/bin/webpack
new file mode 100755
index 00000000..528233a7
--- /dev/null
+++ b/bin/webpack
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+$stdout.sync = true
+
+require "shellwords"
+
+ENV["RAILS_ENV"] ||= "development"
+RAILS_ENV = ENV["RAILS_ENV"]
+
+ENV["NODE_ENV"] ||= RAILS_ENV
+NODE_ENV = ENV["NODE_ENV"]
+
+APP_PATH = File.expand_path("../", __dir__)
+NODE_MODULES_PATH = File.join(APP_PATH, "node_modules")
+WEBPACK_CONFIG = File.join(APP_PATH, "config/webpack/#{NODE_ENV}.js")
+
+unless File.exist?(WEBPACK_CONFIG)
+ puts "Webpack configuration not found."
+ puts "Please run bundle exec rails webpacker:install to install webpacker"
+ exit!
+end
+
+env = { "NODE_PATH" => NODE_MODULES_PATH.shellescape }
+cmd = [ "#{NODE_MODULES_PATH}/.bin/webpack", "--config", WEBPACK_CONFIG ] + ARGV
+
+Dir.chdir(APP_PATH) do
+ exec env, *cmd
+end
diff --git a/bin/webpack-dev-server b/bin/webpack-dev-server
new file mode 100755
index 00000000..c9672f66
--- /dev/null
+++ b/bin/webpack-dev-server
@@ -0,0 +1,68 @@
+#!/usr/bin/env ruby
+$stdout.sync = true
+
+require "shellwords"
+require "yaml"
+require "socket"
+
+ENV["RAILS_ENV"] ||= "development"
+RAILS_ENV = ENV["RAILS_ENV"]
+
+ENV["NODE_ENV"] ||= RAILS_ENV
+NODE_ENV = ENV["NODE_ENV"]
+
+APP_PATH = File.expand_path("../", __dir__)
+CONFIG_FILE = File.join(APP_PATH, "config/webpacker.yml")
+NODE_MODULES_PATH = File.join(APP_PATH, "node_modules")
+WEBPACK_CONFIG = File.join(APP_PATH, "config/webpack/#{NODE_ENV}.js")
+
+DEFAULT_LISTEN_HOST_ADDR = NODE_ENV == 'development' ? 'localhost' : '0.0.0.0'
+
+def args(key)
+ index = ARGV.index(key)
+ index ? ARGV[index + 1] : nil
+end
+
+begin
+ dev_server = YAML.load_file(CONFIG_FILE)[RAILS_ENV]["dev_server"]
+
+ HOSTNAME = args('--host') || dev_server["host"]
+ PORT = args('--port') || dev_server["port"]
+ HTTPS = ARGV.include?('--https') || dev_server["https"]
+ DEV_SERVER_ADDR = "http#{"s" if HTTPS}://#{HOSTNAME}:#{PORT}"
+ LISTEN_HOST_ADDR = args('--listen-host') || DEFAULT_LISTEN_HOST_ADDR
+
+rescue Errno::ENOENT, NoMethodError
+ $stdout.puts "Webpack dev_server configuration not found in #{CONFIG_FILE}."
+ $stdout.puts "Please run bundle exec rails webpacker:install to install webpacker"
+ exit!
+end
+
+begin
+ server = TCPServer.new(LISTEN_HOST_ADDR, PORT)
+ server.close
+
+rescue Errno::EADDRINUSE
+ $stdout.puts "Another program is running on port #{PORT}. Set a new port in #{CONFIG_FILE} for dev_server"
+ exit!
+end
+
+# Delete supplied host, port and listen-host CLI arguments
+["--host", "--port", "--listen-host"].each do |arg|
+ ARGV.delete(args(arg))
+ ARGV.delete(arg)
+end
+
+env = { "NODE_PATH" => NODE_MODULES_PATH.shellescape }
+
+cmd = [
+ "#{NODE_MODULES_PATH}/.bin/webpack-dev-server", "--progress", "--color",
+ "--config", WEBPACK_CONFIG,
+ "--host", LISTEN_HOST_ADDR,
+ "--public", "#{HOSTNAME}:#{PORT}",
+ "--port", PORT.to_s
+] + ARGV
+
+Dir.chdir(APP_PATH) do
+ exec env, *cmd
+end
diff --git a/bower.json b/bower.json
index 63931ebf..c22c2dd2 100644
--- a/bower.json
+++ b/bower.json
@@ -1,8 +1,6 @@
{
"name": "Splunka",
- "authors": [
- "Paulo Patto "
- ],
+ "authors": [ "Paulo Patto " ],
"description": "",
"main": "",
"license": "MIT",
diff --git a/config/routes.rb b/config/routes.rb
index 15a82e78..07544a95 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,4 +3,7 @@
resources :sales_batches, only: [:new, :create, :show, :index]
resources :sales, only: [:show]
+
+ require 'sidekiq/web'
+ mount Sidekiq::Web => '/sidekiq'
end
diff --git a/config/sidekiq.yml b/config/sidekiq.yml
new file mode 100644
index 00000000..ad1cbaa7
--- /dev/null
+++ b/config/sidekiq.yml
@@ -0,0 +1,23 @@
+default: &default
+ :service: 'redis://localhost:6379'
+ :logfile: ./log/sidekiq.log
+ :queues:
+ - sales
+
+development:
+ <<: *default
+
+test:
+ <<: *default
+
+qa:
+ <<: *default
+
+production:
+ :service: 'redis://localhost:6379'
+ :user: 'admin'
+ :password: 'admin'
+ :logfile: ./log/sidekiq.log
+ :queues:
+ - sales
+
diff --git a/config/sidekiq.yml.example b/config/sidekiq.yml.example
new file mode 100644
index 00000000..ad1cbaa7
--- /dev/null
+++ b/config/sidekiq.yml.example
@@ -0,0 +1,23 @@
+default: &default
+ :service: 'redis://localhost:6379'
+ :logfile: ./log/sidekiq.log
+ :queues:
+ - sales
+
+development:
+ <<: *default
+
+test:
+ <<: *default
+
+qa:
+ <<: *default
+
+production:
+ :service: 'redis://localhost:6379'
+ :user: 'admin'
+ :password: 'admin'
+ :logfile: ./log/sidekiq.log
+ :queues:
+ - sales
+
diff --git a/config/webpack/development.js b/config/webpack/development.js
new file mode 100644
index 00000000..81269f65
--- /dev/null
+++ b/config/webpack/development.js
@@ -0,0 +1,3 @@
+const environment = require('./environment')
+
+module.exports = environment.toWebpackConfig()
diff --git a/config/webpack/environment.js b/config/webpack/environment.js
new file mode 100644
index 00000000..d16d9af7
--- /dev/null
+++ b/config/webpack/environment.js
@@ -0,0 +1,3 @@
+const { environment } = require('@rails/webpacker')
+
+module.exports = environment
diff --git a/config/webpack/production.js b/config/webpack/production.js
new file mode 100644
index 00000000..81269f65
--- /dev/null
+++ b/config/webpack/production.js
@@ -0,0 +1,3 @@
+const environment = require('./environment')
+
+module.exports = environment.toWebpackConfig()
diff --git a/config/webpack/test.js b/config/webpack/test.js
new file mode 100644
index 00000000..81269f65
--- /dev/null
+++ b/config/webpack/test.js
@@ -0,0 +1,3 @@
+const environment = require('./environment')
+
+module.exports = environment.toWebpackConfig()
diff --git a/config/webpacker.yml b/config/webpacker.yml
new file mode 100644
index 00000000..e62a7acd
--- /dev/null
+++ b/config/webpacker.yml
@@ -0,0 +1,56 @@
+# Note: You must restart bin/webpack-dev-server for changes to take effect
+
+default: &default
+ source_path: app/javascript
+ source_entry_path: packs
+ public_output_path: packs
+ cache_path: tmp/cache/webpacker
+
+ # Additional paths webpack should lookup modules
+ # ['app/assets', 'engine/foo/app/assets']
+ resolved_paths: []
+
+ # Reload manifest.json on all requests so we reload latest compiled packs
+ cache_manifest: false
+
+ extensions:
+ - .coffee
+ - .erb
+ - .js
+ - .jsx
+ - .ts
+ - .vue
+ - .sass
+ - .scss
+ - .css
+ - .png
+ - .svg
+ - .gif
+ - .jpeg
+ - .jpg
+
+development:
+ <<: *default
+ compile: true
+
+ dev_server:
+ host: localhost
+ port: 3035
+ hmr: false
+ https: false
+
+test:
+ <<: *default
+ compile: true
+
+ # Compile test packs to a separate directory
+ public_output_path: packs-test
+
+production:
+ <<: *default
+
+ # Production depends on precompilation of packs prior to booting for performance.
+ compile: false
+
+ # Cache manifest.json for performance
+ cache_manifest: true
diff --git a/db/migrate/20160828191211_add_field_state_to_sales_batches.rb b/db/migrate/20160828191211_add_field_state_to_sales_batches.rb
new file mode 100644
index 00000000..1badf710
--- /dev/null
+++ b/db/migrate/20160828191211_add_field_state_to_sales_batches.rb
@@ -0,0 +1,6 @@
+class AddFieldStateToSalesBatches < ActiveRecord::Migration
+ def change
+ add_column :sales_batches, :state, :string
+ remove_column :sales_batches, :processed
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 37a28f16..71d112df 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160826050946) do
+ActiveRecord::Schema.define(version: 20160828191211) do
create_table "sales", force: :cascade do |t|
t.string "customer"
@@ -29,11 +29,11 @@
create_table "sales_batches", force: :cascade do |t|
t.string "attachment"
- t.boolean "processed", default: false
t.decimal "revenue", precision: 12, scale: 2, default: 0.0
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.string "batch_code"
+ t.string "state"
end
add_index "sales_batches", ["batch_code"], name: "index_sales_batches_on_batch_code"
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..374de222
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,23 @@
+version: '2'
+services:
+ db:
+ image: postgres
+ redis:
+ image: redis
+ ports:
+ - "6379":"6379"
+ # worker:
+ # build: .
+ # command: bundle exec sidkiq -C config/sidekiq.yml
+ # volumes:
+ # - .:/opt/splunka
+ web:
+ build: .
+ command: bundle exec rails server -p 3000 -b '0.0.0.0'
+ volumes:
+ - .:/opt/splunka
+ ports:
+ - '3000':'3000'
+ depends_on:
+ - db
+ - redis
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..2d010ffa
--- /dev/null
+++ b/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "splunka",
+ "version": "0.0.1",
+ "description": "My description",
+ "main": "index.js",
+ "directories": {
+ "lib": "lib"
+ },
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/paulopatto/Splunka.git"
+ },
+ "keywords": [
+ "rails",
+ "react"
+ ],
+ "author": "Paulo Patto ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/paulopatto/Splunka/issues"
+ },
+ "homepage": "https://github.com/paulopatto/Splunka#readme",
+ "dependencies": {
+ "@rails/webpacker": "^3.0.1",
+ "babel-preset-react": "^6.24.1",
+ "bower": "^1.8.0",
+ "phantomjs": "^2.1.7",
+ "prop-types": "^15.5.10",
+ "react": "^15.6.1",
+ "react-dom": "^15.6.1",
+ "yarn": "^0.27.5"
+ },
+ "devDependencies": {
+ "webpack-dev-server": "^2.7.1"
+ }
+}
diff --git a/spec/controllers/sales_batches_controller_spec.rb b/spec/controllers/sales_batches_controller_spec.rb
new file mode 100644
index 00000000..0011942f
--- /dev/null
+++ b/spec/controllers/sales_batches_controller_spec.rb
@@ -0,0 +1,57 @@
+require 'rails_helper'
+
+describe SalesBatchesController, type: :controller do
+ describe '#index' do
+ before do
+ get :index
+ end
+
+ it 'returns with :success' do
+ expect(response).to be_success
+ end
+
+ it 'render template :index' do
+ expect(response).to render_template :index
+ end
+
+ context 'when does not have sales batches' do
+ it 'assigns :sales_batches empty' do
+ expect(assigns(:sales_batches)).to be_empty
+ end
+ end
+
+ context 'when have any sales batches' do
+ let(:registers_no) { 3 }
+ before do
+ registers_no.times.each {|x| create(:sales_batch) }
+ get :index
+ end
+
+ it 'assigns :sales_batches with all batches' do
+ expect(assigns(:sales_batches).count).to eq registers_no
+ end
+ end
+ end
+
+ describe '#create' do
+ let(:sales_batches_params) do
+ {
+ attachment: Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec', 'fixtures', 'sales.txt'))
+ }
+ end
+
+
+ before do
+ allow(SalesBatchWorker).to receive(:perform_async)
+ post :create, sales_batch: sales_batches_params
+ end
+
+ it 'batch persisted' do
+ expect(assigns(:batch)).to be_persisted
+ end
+
+ it 'enqued job' do
+ expect(SalesBatchWorker).to have_received(:perform_async)
+ end
+ end
+end
diff --git a/spec/factories/sales_batches_factorie.rb b/spec/factories/sales_batches_factorie.rb
index 87a60ad7..8f2dee83 100644
--- a/spec/factories/sales_batches_factorie.rb
+++ b/spec/factories/sales_batches_factorie.rb
@@ -1,5 +1,6 @@
FactoryGirl.define do
factory :sales_batch do
attachment { Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec', 'fixtures', 'sales.txt')) }
+ batch_code SecureRandom.uuid
end
end
diff --git a/spec/features/uploading_new_sales_batch_spec.rb b/spec/features/uploading_new_sales_batch_spec.rb
new file mode 100644
index 00000000..8095839c
--- /dev/null
+++ b/spec/features/uploading_new_sales_batch_spec.rb
@@ -0,0 +1,15 @@
+require 'rails_helper'
+
+feature 'Uploading and creating new Sales Batch', type: :feature do
+ let(:sales_batch_file_path) { 'spec/fixtures/sales.txt' }
+
+ before do
+ visit root_path
+ attach_file 'sales_batch[attachment]', File.absolute_path(sales_batch_file_path)
+ click_on 'Enviar'
+ end
+
+ it '' do
+
+ end
+end
diff --git a/spec/models/sales_batch_spec.rb b/spec/models/sales_batch_spec.rb
index 460d27d2..c8a6808b 100644
--- a/spec/models/sales_batch_spec.rb
+++ b/spec/models/sales_batch_spec.rb
@@ -4,11 +4,11 @@
#
# id :integer not null, primary key
# attachment :string
-# processed :boolean default(FALSE)
# revenue :decimal(12, 2) default(0.0)
# created_at :datetime not null
# updated_at :datetime not null
# batch_code :string
+# state :string
#
require 'rails_helper'
@@ -18,7 +18,6 @@
is_expected.to have_many :sales
end
-
context 'validates' do
subject { build(:sales_batch) }
@@ -26,8 +25,4 @@
is_expected.to validate_uniqueness_of :batch_code
end
end
-
- context 'callbacks' do
- pending "Warn: Callbacks as anti pattern"
- end
end
diff --git a/spec/support/capybara_support.rb b/spec/support/capybara_support.rb
new file mode 100644
index 00000000..bbcfd251
--- /dev/null
+++ b/spec/support/capybara_support.rb
@@ -0,0 +1,2 @@
+require 'capybara/poltergeist'
+Capybara.javascript_driver = :poltergeist
diff --git a/spec/support/rspec_sidekiq_support.rb b/spec/support/rspec_sidekiq_support.rb
new file mode 100644
index 00000000..a3c4c6e7
--- /dev/null
+++ b/spec/support/rspec_sidekiq_support.rb
@@ -0,0 +1,10 @@
+RSpec::Sidekiq.configure do |config|
+ # Clears all job queues before each example
+ config.clear_all_enqueued_jobs = true # default => true
+
+ # Whether to use terminal colours when outputting messages
+ config.enable_terminal_colours = true # default => true
+
+ # Warn when jobs are not enqueued to Redis but to a job array
+ config.warn_when_jobs_not_processed_by_sidekiq = true # default => true
+end
diff --git a/spec/support/sidekiq_support.rb b/spec/support/sidekiq_support.rb
new file mode 100644
index 00000000..bba06318
--- /dev/null
+++ b/spec/support/sidekiq_support.rb
@@ -0,0 +1,7 @@
+require 'sidekiq/testing'
+
+RSpec.configure do |config|
+ config.before(:each) do
+ Sidekiq::Worker.clear_all
+ end
+end
diff --git a/task.md b/task.md
index 5e5ee7a3..61a764b3 100644
--- a/task.md
+++ b/task.md
@@ -4,10 +4,10 @@ A idéia deste desafio é nos permitir avaliar melhor as habilidades de candidat
Este desafio deve ser feito por você em sua casa. Gaste o tempo que você quiser, porém normalmente você não deve precisar de mais do que algumas horas.
## Instruções de entrega do desafio
-1. Primeiro, faça um fork deste projeto para sua conta no Github (crie uma se você não possuir).
-2. Em seguida, implemente o projeto tal qual descrito abaixo, em seu próprio fork.
-3. Crie as instruções de instalação e execução do aplicativo em seu readme.md
-4. Por fim, envie o link do seu repositorio para avaliarmos seu código
+- 1. Primeiro, faça um fork deste projeto para sua conta no Github (crie uma se você não possuir).
+- 2. Em seguida, implemente o projeto tal qual descrito abaixo, em seu próprio fork.
+- 3. Crie as instruções de instalação e execução do aplicativo em seu readme.md
+- 4. Por fim, envie o link do seu repositorio para avaliarmos seu código
## Descrição do projeto
@@ -17,27 +17,29 @@ Sua tarefa é criar uma interface web que aceite upload de arquivos, normalize o
Sua aplicação web DEVE:
-1. Aceitar (via um formulário) o upload de arquivos text, com dados separados por TAB testar o aplicativo usando o arquivo fornecido. A primeira linha do arquivo tem o nome das colunas. Você pode assumir que as colunas estarão sempre nesta ordem, e que sempre haverá uma linha de cabeçalho. Um arquivo de exemplo chamado 'dados.txt' está incluído neste repositório.
-2. Interpretar ("parsear") o arquivo recebido, normalizar os dados, e salvar corretamente a informação em um banco de dados relacional.
-3. Exibir todos os registros importados, bem como a receita bruta total dos registros contidos no arquivo enviado após o upload + parser.
-4. Se sua vaga é para Ruby e Ruby On Rails, ser escrita obrigatoriamente em: Ruby 2.1+ Rails 4 e SQLite
-5. Se sua vaga é para .Net ser escrita obrigatoriamente em: VB# ou C#, última versão, SQL Server (pode ser express)
-6. Ser simples de configurar e rodar a partir das instruções fornecidas,
-7. funcionando em ambiente compatível com Unix (Linux ou Mac OS X) para Ruby On Rails e Windows para .Net. Ela deve utilizar apenas linguagens e bibliotecas livres ou gratuitas.
-8. Ter um teste de model e controller automatizado para a funcionalidade pedida
-9. Ter uma boa aparecia e ser fácil de usar
+- 1. Aceitar (via um formulário) o upload de arquivos text, com dados separados por TAB testar o aplicativo usando o arquivo fornecido. A primeira linha do arquivo tem o nome das colunas. Você pode assumir que as colunas estarão sempre nesta ordem, e que sempre haverá uma linha de cabeçalho. Um arquivo de exemplo chamado 'dados.txt' está incluído neste repositório.
+- 2. Interpretar ("parsear") o arquivo recebido, normalizar os dados, e salvar corretamente a informação em um banco de dados relacional.
+- 3. Exibir todos os registros importados, bem como a receita bruta total dos registros contidos no arquivo enviado após o upload + parser.
+- 4. Se sua vaga é para Ruby e Ruby On Rails, ser escrita obrigatoriamente em: Ruby 2.1+ Rails 4 e SQLite
+- 5. Se sua vaga é para .Net ser escrita obrigatoriamente em: VB# ou C#, última versão, SQL Server (pode ser express)
+- 6. Ser simples de configurar e rodar a partir das instruções fornecidas,
+- 7. funcionando em ambiente compatível com Unix (Linux ou Mac OS X) para Ruby On Rails e Windows para .Net. Ela deve utilizar apenas linguagens e bibliotecas livres ou gratuitas.
+- 8. Ter um teste de model e controller automatizado para a funcionalidade pedida
+- 9. Ter uma boa aparecia e ser fácil de usar
## Avaliação
Seu projeto será avaliado de acordo com os seguintes critérios.
-1. Sua aplicação atende funcionalmente o que foi pedido
-2. Você documentou a maneira de configurar o ambiente e rodar sua aplicação na maquina do avaliador
-3. Você seguiu as instruções enviadas
-4. Voce segue as boas práticas de programação e entrega para o Cliente
-5. O código escrito é facil de entender e manter
-6. Você se preocupa com o uso do aplicativo pelo Usuário
+- 1. Sua aplicação atende funcionalmente o que foi pedido
+- 2. Você documentou a maneira de configurar o ambiente e rodar sua aplicação na maquina do avaliador
+- 3. Você seguiu as instruções enviadas
+- 4. Voce segue as boas práticas de programação e entrega para o Cliente
+- 5. O código escrito é facil de entender e manter
+- 6. Você se preocupa com o uso do aplicativo pelo Usuário
-Adicionalmente, tentaremos verificar a sua familiarização com as bibliotecas padrões (standard libs), bem como sua experiência com programação orientada a objetos a partir da estrutura de seu projeto, preucupação com o objetivo da aplicação e do seu uso pelo usuário, suporte e manutenção do código por outros desenvolvdores
+Adicionalmente, tentaremos verificar a sua familiarização com as bibliotecas padrões (standard libs),
+bem como sua experiência com programação orientada a objetos a partir da estrutura de seu projeto,
+preucupação com o objetivo da aplicação e do seu uso pelo usuário, suporte e manutenção do código por outros desenvolvdores.
### Referência