Commit 3b19a49d by Tran Hoang Viet

Merge branch 'vietth_feat_search' into 'develop'

VietTH: Feature search

See merge request !4
parents b07e1f6f 26aab3c8
...@@ -12,3 +12,4 @@ ...@@ -12,3 +12,4 @@
!/log/.keep !/log/.keep
/tmp /tmp
/public/uploads/ /public/uploads/
/config/database.yml
--color
--require spec_helper
AllCops:
Exclude:
- 'vendor/**/*'
- 'spec/**/*'
RunRailsCops: true
DisplayCopNames: true
Style/AccessModifierIndentation:
Enabled: false
Style/AccessorMethodName:
Enabled: false
Style/Alias:
Enabled: false
Style/AlignArray:
Enabled: false
Style/AlignHash:
Enabled: false
Style/AlignParameters:
Enabled: false
Style/AndOr:
Enabled: true
Style/ArrayJoin:
Enabled: false
Style/AsciiComments:
Enabled: false
Style/AsciiIdentifiers:
Enabled: false
Style/Attr:
Enabled: false
Style/BeginBlock:
Enabled: false
Style/BarePercentLiterals:
Enabled: false
Style/BlockComments:
Enabled: false
Style/BlockEndNewline:
Enabled: false
Style/BlockDelimiters:
Enabled: false
Style/Blocks:
Enabled: false
Style/BracesAroundHashParameters:
Enabled: false
Style/CaseEquality:
Enabled: true
Style/CaseIndentation:
Enabled: false
Style/CharacterLiteral:
Enabled: false
Style/ClassAndModuleCamelCase:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false
Style/ClassCheck:
Enabled: false
Style/ClassMethods:
Enabled: false
Style/ClassVars:
Enabled: false
Style/ColonMethodCall:
Enabled: false
Style/CommentAnnotation:
Enabled: false
Style/CommentIndentation:
Enabled: false
Style/ConstantName:
Enabled: false
Style/DefWithParentheses:
Enabled: false
Style/DeprecatedHashMethods:
Enabled: false
Style/Documentation:
Enabled: false
Style/DotPosition:
Enabled: false
Style/DoubleNegation:
Enabled: false
Style/EachWithObject:
Enabled: false
Style/ElseAlignment:
Enabled: false
Style/EmptyElse:
Enabled: false
Style/EmptyLineBetweenDefs:
Enabled: false
Style/EmptyLines:
Enabled: false
Style/EmptyLinesAroundAccessModifier:
Enabled: false
Style/EmptyLinesAroundBlockBody:
Enabled: false
Style/EmptyLinesAroundClassBody:
Enabled: false
Style/EmptyLinesAroundModuleBody:
Enabled: false
Style/EmptyLinesAroundMethodBody:
Enabled: false
Style/EmptyLiteral:
Enabled: false
Style/EndBlock:
Enabled: false
Style/EndOfLine:
Enabled: false
Style/EvenOdd:
Enabled: false
Style/FileName:
Enabled: false
Style/FirstParameterIndentation:
Enabled: false
Style/FlipFlop:
Enabled: false
Style/For:
Enabled: false
Style/FormatString:
Enabled: false
Style/GlobalVars:
Enabled: true
Style/GuardClause:
Enabled: false
Style/HashSyntax:
Enabled: false
Style/IfUnlessModifier:
Enabled: false
Style/IfWithSemicolon:
Enabled: false
Style/IndentationConsistency:
Enabled: false
Style/IndentationWidth:
Enabled: false
Style/IndentArray:
Enabled: false
Style/IndentHash:
Enabled: false
Style/InfiniteLoop:
Enabled: false
Style/Lambda:
Enabled: false
Style/LambdaCall:
Enabled: false
Style/LeadingCommentSpace:
Enabled: false
Style/LineEndConcatenation:
Enabled: false
Style/MethodCallParentheses:
Enabled: false
Style/MethodDefParentheses:
Enabled: false
Style/MethodName:
Enabled: false
Style/ModuleFunction:
Enabled: false
Style/MultilineBlockChain:
Enabled: false
Style/MultilineBlockLayout:
Enabled: false
Style/MultilineIfThen:
Enabled: false
Style/MultilineOperationIndentation:
Enabled: false
Style/MultilineTernaryOperator:
Enabled: false
Style/NegatedIf:
Enabled: false
Style/NegatedWhile:
Enabled: false
Style/NestedTernaryOperator:
Enabled: false
Style/Next:
Enabled: false
Style/NilComparison:
Enabled: false
Style/NonNilCheck:
Enabled: false
Style/Not:
Enabled: false
Style/NumericLiterals:
Enabled: false
Style/OneLineConditional:
Enabled: false
Style/OpMethod:
Enabled: false
Style/ParenthesesAroundCondition:
Enabled: false
Style/PercentLiteralDelimiters:
Enabled: false
Style/PercentQLiterals:
Enabled: false
Style/PerlBackrefs:
Enabled: false
Style/PredicateName:
Enabled: false
Style/Proc:
Enabled: false
Style/RaiseArgs:
Enabled: false
Style/RedundantBegin:
Enabled: false
Style/RedundantException:
Enabled: false
Style/RedundantReturn:
Enabled: false
Style/RedundantSelf:
Enabled: false
Style/RegexpLiteral:
Enabled: false
Style/RescueModifier:
Enabled: false
Style/SelfAssignment:
Enabled: false
Style/Semicolon:
Enabled: false
Style/SignalException:
Enabled: false
Style/SingleLineBlockParams:
Enabled: false
Style/SingleLineMethods:
Enabled: false
Style/SingleSpaceBeforeFirstArg:
Enabled: false
Style/SpaceAfterColon:
Enabled: false
Style/SpaceAfterComma:
Enabled: false
Style/SpaceAfterControlKeyword:
Enabled: false
Style/SpaceAfterMethodName:
Enabled: false
Style/SpaceAfterNot:
Enabled: false
Style/SpaceAfterSemicolon:
Enabled: false
Style/SpaceBeforeBlockBraces:
Enabled: false
Style/SpaceBeforeComma:
Enabled: false
Style/SpaceBeforeComment:
Enabled: false
Style/SpaceBeforeSemicolon:
Enabled: false
Style/SpaceInsideBlockBraces:
Enabled: false
Style/SpaceAroundBlockParameters:
Enabled: false
Style/SpaceAroundEqualsInParameterDefault:
Enabled: false
Style/SpaceAroundOperators:
Enabled: false
Style/SpaceBeforeModifierKeyword:
Enabled: false
Style/SpaceInsideBrackets:
Enabled: false
Style/SpaceInsideHashLiteralBraces:
Enabled: false
Style/SpaceInsideParens:
Enabled: false
Style/SpaceInsideRangeLiteral:
Enabled: false
Style/SpecialGlobalVars:
Enabled: false
Style/StringLiterals:
Enabled: false
Style/StringLiteralsInInterpolation:
Enabled: false
Style/StructInheritance:
Enabled: false
Style/SymbolProc:
Enabled: false
Style/Tab:
Enabled: false
Style/TrailingBlankLines:
Enabled: false
Style/TrailingComma:
Enabled: false
Style/TrailingWhitespace:
Enabled: false
Style/TrivialAccessors:
Enabled: false
Style/UnlessElse:
Enabled: false
Style/UnneededCapitalW:
Enabled: false
Style/UnneededPercentQ:
Enabled: false
Style/UnneededPercentX:
Enabled: false
Style/VariableInterpolation:
Enabled: false
Style/VariableName:
Enabled: false
Style/WhenThen:
Enabled: false
Style/WhileUntilDo:
Enabled: false
Style/WhileUntilModifier:
Enabled: false
Style/WordArray:
Enabled: false
Lint/DeprecatedClassMethods:
Enabled: false
Lint/StringConversionInInterpolation:
Enabled: false
Metrics/AbcSize:
Enabled: false
Metrics/BlockNesting:
Enabled: false
Metrics/ClassLength:
Enabled: false
Metrics/CyclomaticComplexity:
Enabled: false
Metrics/LineLength:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/ParameterLists:
Enabled: false
Metrics/PerceivedComplexity:
Enabled: false
\ No newline at end of file
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano3/unicorn'
# Load custom tasks from `lib/capistrano/tasks' if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
\ No newline at end of file
source 'https://rubygems.org' source 'http://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
...@@ -43,6 +43,13 @@ group :development, :test do ...@@ -43,6 +43,13 @@ group :development, :test do
gem 'spring' gem 'spring'
gem 'pry-rails', '~> 0.3.4' gem 'pry-rails', '~> 0.3.4'
gem 'letter_opener', '~> 1.4.1' gem 'letter_opener', '~> 1.4.1'
gem 'rspec-rails', '~> 3.3.2'
gem 'factory_girl_rails', '~> 4.5.0'
gem 'database_cleaner', '~> 1.4.1'
gem 'faker', '~> 1.4.3'
gem 'shoulda-matchers', '~> 2.8.0'
end end
# template # template
...@@ -70,3 +77,17 @@ gem 'sidekiq', '~> 3.4.1' ...@@ -70,3 +77,17 @@ gem 'sidekiq', '~> 3.4.1'
# pagination # pagination
gem 'kaminari', '~> 0.16.3' gem 'kaminari', '~> 0.16.3'
gem 'rsolr', '~> 1.0.12'
gem 'seed_migration', '~> 1.0.5'
gem 'rails_12factor', '~> 0.0.3'
group :development do
gem 'capistrano-rails'
gem 'capistrano-rvm'
gem 'capistrano3-unicorn'
gem 'capistrano-sidekiq', '~> 0.5.2'
end
gem 'rubocop', '~> 0.32.1'
\ No newline at end of file
GEM GEM
remote: https://rubygems.org/ remote: http://rubygems.org/
specs: specs:
actionmailer (4.2.3) actionmailer (4.2.3)
actionpack (= 4.2.3) actionpack (= 4.2.3)
...@@ -38,12 +38,33 @@ GEM ...@@ -38,12 +38,33 @@ GEM
tzinfo (~> 1.1) tzinfo (~> 1.1)
addressable (2.3.8) addressable (2.3.8)
arel (6.0.0) arel (6.0.0)
ast (2.0.0)
astrolabe (1.3.0)
parser (>= 2.2.0.pre.3, < 3.0)
bcrypt (3.1.10) bcrypt (3.1.10)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
builder (3.2.2) builder (3.2.2)
byebug (5.0.0) byebug (5.0.0)
columnize (= 0.9.0) columnize (= 0.9.0)
capistrano (3.4.0)
i18n
rake (>= 10.0.0)
sshkit (~> 1.3)
capistrano-bundler (1.1.4)
capistrano (~> 3.1)
sshkit (~> 1.2)
capistrano-rails (1.1.3)
capistrano (~> 3.1)
capistrano-bundler (~> 1.1)
capistrano-rvm (0.1.2)
capistrano (~> 3.0)
sshkit (~> 1.2)
capistrano-sidekiq (0.5.3)
capistrano
sidekiq
capistrano3-unicorn (0.2.1)
capistrano (~> 3.1, >= 3.1.0)
carrierwave (0.10.0) carrierwave (0.10.0)
activemodel (>= 3.2.0) activemodel (>= 3.2.0)
activesupport (>= 3.2.0) activesupport (>= 3.2.0)
...@@ -59,8 +80,10 @@ GEM ...@@ -59,8 +80,10 @@ GEM
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.9.1.1) coffee-script-source (1.9.1.1)
colorize (0.7.7)
columnize (0.9.0) columnize (0.9.0)
connection_pool (2.2.0) connection_pool (2.2.0)
database_cleaner (1.4.1)
debug_inspector (0.0.2) debug_inspector (0.0.2)
devise (3.5.1) devise (3.5.1)
bcrypt (~> 3.0) bcrypt (~> 3.0)
...@@ -69,6 +92,7 @@ GEM ...@@ -69,6 +92,7 @@ GEM
responders responders
thread_safe (~> 0.1) thread_safe (~> 0.1)
warden (~> 1.2.3) warden (~> 1.2.3)
diff-lcs (1.2.5)
draper (2.1.0) draper (2.1.0)
actionpack (>= 3.0) actionpack (>= 3.0)
activemodel (>= 3.0) activemodel (>= 3.0)
...@@ -77,6 +101,13 @@ GEM ...@@ -77,6 +101,13 @@ GEM
erubis (2.7.0) erubis (2.7.0)
excon (0.45.3) excon (0.45.3)
execjs (2.5.2) execjs (2.5.2)
factory_girl (4.5.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.5.0)
factory_girl (~> 4.5.0)
railties (>= 3.0.0)
faker (1.4.3)
i18n (~> 0.5)
figaro (1.1.1) figaro (1.1.1)
thor (~> 0.14) thor (~> 0.14)
font-awesome-sass (4.3.2.1) font-awesome-sass (4.3.2.1)
...@@ -129,9 +160,15 @@ GEM ...@@ -129,9 +160,15 @@ GEM
multi_json (1.11.2) multi_json (1.11.2)
multi_xml (0.5.5) multi_xml (0.5.5)
mysql2 (0.3.18) mysql2 (0.3.18)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
net-ssh (2.9.2)
nokogiri (1.6.6.2) nokogiri (1.6.6.2)
mini_portile (~> 0.6.0) mini_portile (~> 0.6.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
parser (2.2.2.6)
ast (>= 1.1, < 3.0)
powerpack (0.1.1)
pry (0.10.1) pry (0.10.1)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
...@@ -162,11 +199,17 @@ GEM ...@@ -162,11 +199,17 @@ GEM
rails-deprecated_sanitizer (>= 1.0.1) rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.2) rails-html-sanitizer (1.0.2)
loofah (~> 2.0) loofah (~> 2.0)
rails_12factor (0.0.3)
rails_serve_static_assets
rails_stdout_logging
rails_serve_static_assets (0.0.4)
rails_stdout_logging (0.0.3)
railties (4.2.3) railties (4.2.3)
actionpack (= 4.2.3) actionpack (= 4.2.3)
activesupport (= 4.2.3) activesupport (= 4.2.3)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.0.0)
raindrops (0.14.0) raindrops (0.14.0)
rake (10.4.2) rake (10.4.2)
rdoc (4.2.0) rdoc (4.2.0)
...@@ -177,6 +220,32 @@ GEM ...@@ -177,6 +220,32 @@ GEM
request_store (1.1.0) request_store (1.1.0)
responders (2.1.0) responders (2.1.0)
railties (>= 4.2.0, < 5) railties (>= 4.2.0, < 5)
rsolr (1.0.12)
builder (>= 2.1.2)
rspec-core (3.3.1)
rspec-support (~> 3.3.0)
rspec-expectations (3.3.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-mocks (3.3.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0)
rspec-rails (3.3.2)
actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3)
rspec-core (~> 3.3.0)
rspec-expectations (~> 3.3.0)
rspec-mocks (~> 3.3.0)
rspec-support (~> 3.3.0)
rspec-support (3.3.0)
rubocop (0.32.1)
astrolabe (~> 1.3)
parser (>= 2.2.2.5, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4)
ruby-progressbar (1.7.5)
ruby_parser (3.7.0) ruby_parser (3.7.0)
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
sass (3.4.15) sass (3.4.15)
...@@ -189,8 +258,11 @@ GEM ...@@ -189,8 +258,11 @@ GEM
sdoc (0.4.1) sdoc (0.4.1)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0) rdoc (~> 4.0)
seed_migration (1.0.5)
settingslogic (2.0.9) settingslogic (2.0.9)
sexp_processor (4.6.0) sexp_processor (4.6.0)
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
sidekiq (3.4.1) sidekiq (3.4.1)
celluloid (~> 0.16.0) celluloid (~> 0.16.0)
connection_pool (>= 2.1.1) connection_pool (>= 2.1.1)
...@@ -205,6 +277,10 @@ GEM ...@@ -205,6 +277,10 @@ GEM
actionpack (>= 3.0) actionpack (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0) sprockets (>= 2.8, < 4.0)
sshkit (1.7.1)
colorize (>= 0.7.0)
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
subexec (0.2.3) subexec (0.2.3)
thor (0.19.1) thor (0.19.1)
thread_safe (0.3.5) thread_safe (0.3.5)
...@@ -243,10 +319,17 @@ PLATFORMS ...@@ -243,10 +319,17 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
byebug byebug
capistrano-rails
capistrano-rvm
capistrano-sidekiq (~> 0.5.2)
capistrano3-unicorn
carrierwave (~> 0.10.0) carrierwave (~> 0.10.0)
coffee-rails (~> 4.1.0) coffee-rails (~> 4.1.0)
database_cleaner (~> 1.4.1)
devise (~> 3.5.1) devise (~> 3.5.1)
draper (~> 2.1.0) draper (~> 2.1.0)
factory_girl_rails (~> 4.5.0)
faker (~> 1.4.3)
figaro (~> 1.1.1) figaro (~> 1.1.1)
font-awesome-sass font-awesome-sass
haml-rails (~> 0.9) haml-rails (~> 0.9)
...@@ -259,9 +342,15 @@ DEPENDENCIES ...@@ -259,9 +342,15 @@ DEPENDENCIES
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
quiet_assets quiet_assets
rails (= 4.2.3) rails (= 4.2.3)
rails_12factor (~> 0.0.3)
rsolr (~> 1.0.12)
rspec-rails (~> 3.3.2)
rubocop (~> 0.32.1)
sass-rails (~> 5.0) sass-rails (~> 5.0)
sdoc (~> 0.4.0) sdoc (~> 0.4.0)
seed_migration (~> 1.0.5)
settingslogic (~> 2.0.9) settingslogic (~> 2.0.9)
shoulda-matchers (~> 2.8.0)
sidekiq (~> 3.4.1) sidekiq (~> 3.4.1)
spring spring
turbolinks turbolinks
......
...@@ -22,6 +22,12 @@ label.error{ ...@@ -22,6 +22,12 @@ label.error{
} }
} }
#message{
.alert{
margin-top: 20px;
}
}
#content{ #content{
.products-list-detail{ .products-list-detail{
.product-item{ .product-item{
......
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
layout Proc.new { |controller| controller.devise_controller? ? 'devise' : 'application' } layout Proc.new { |controller| controller.devise_controller? ? 'devise' : 'application' }
# Prevent CSRF attacks by raising an exception. # Prevent CSRF attacks by raising an exception.
...@@ -14,4 +15,8 @@ class ApplicationController < ActionController::Base ...@@ -14,4 +15,8 @@ class ApplicationController < ActionController::Base
def add_breadcrumb_home def add_breadcrumb_home
add_breadcrumb('Home', root_path) add_breadcrumb('Home', root_path)
end end
def record_not_found
redirect_to(root_path, alert: "Resource not found")
end
end end
\ No newline at end of file
...@@ -7,7 +7,7 @@ class CategoriesController < ApplicationController ...@@ -7,7 +7,7 @@ class CategoriesController < ApplicationController
private private
def set_category def set_category
@category = Category.find_by(id: params[:id]) @category = Category.find(params[:id])
end end
end end
\ No newline at end of file
module CartsManagement extend ActiveSupport::Concern module OrdersManagement extend ActiveSupport::Concern
included do included do
def add_to_cart product def add_to_cart product
cart_item = { order_item = {
'id' => product.id, 'id' => product.id,
'title' => product.title, 'title' => product.title,
'price' => product.price, 'price' => product.price,
'quantity' => 1 'quantity' => 1
} }
unless session['cart_info']['items'].include?(cart_item) unless session['cart_info']['items'].include?(order_item)
session['cart_info']['items'].push(cart_item) session['cart_info']['items'].push(order_item)
session['cart_info']['total'] += 1 session['cart_info']['total'] += 1
end end
end end
......
class OrdersController < ApplicationController
include OrdersManagement
before_action :authenticate_user!
def index
@order = Order.new
end
def create
if order_service.create(order_params)
clear_cart
redirect_to(root_path, notice: 'Checkout is successful.')
else
render :index
end
end
private
def order_params
params.require(:order).permit(:total, order_items_attributes: [:product_id, :quantity])
end
def order_service
@order_service ||= OrderService.new(current_user, params)
end
end
\ No newline at end of file
class ProductsController < ApplicationController class ProductsController < ApplicationController
include CartsManagement include OrdersManagement
before_action :set_product, only: [:show, :add_cart] before_action :set_product, only: [:show, :add_cart]
before_action :set_categories, only: [:new] before_action :set_categories, only: [:new]
...@@ -14,9 +14,9 @@ class ProductsController < ApplicationController ...@@ -14,9 +14,9 @@ class ProductsController < ApplicationController
def create def create
@product = Product.new(product_params) @product = Product.new(product_params)
if @product.save if @product.save
redirect_to root_path redirect_to(root_path)
else else
render :new render(:new)
end end
end end
...@@ -26,13 +26,18 @@ class ProductsController < ApplicationController ...@@ -26,13 +26,18 @@ class ProductsController < ApplicationController
end end
def add_cart def add_cart
add_to_cart @product add_to_cart(@product)
redirect_to @product redirect_to(@product, notice: 'Product is added to cart.')
end
def search
query = params[:query].present? ? {title: params[:query]} : '*'
@products = Product.search(query, page: params[:page])
end end
private private
def set_product def set_product
@product = Product.find_by(id: params[:id]) @product = Product.find(params[:id])
end end
def set_categories def set_categories
......
class CartMailer < ActionMailer::Base
default from: 'demo@example.com'
def send_checkout(email, cart)
@cart = cart
@cart_items = @cart.cart_items.includes(:product)
mail(to: email, subject: "Order number #{cart.id}")
end
end
\ No newline at end of file
class OrderMailer < ActionMailer::Base
default from: 'alicuche@gmail.com'
def send_checkout(email, order)
@order = order
@order_items = @order.order_items.includes(:product)
mail(to: email, subject: "Order number #{order.id}")
end
end
\ No newline at end of file
class Cart < ActiveRecord::Base
belongs_to :user
has_many :cart_items, dependent: :destroy
accepts_nested_attributes_for :cart_items
end
class Category < ActiveRecord::Base class Category < ActiveRecord::Base
default_scope ->{ order(:title) } default_scope { order(:title) }
JSON_DEFAULT = %i(id title) JSON_DEFAULT = %i(id title)
has_many :products, dependent: :destroy has_many :products, dependent: :destroy
enum category_type: %i(system amazon) enum category_type: %i(system amazon)
end end
module Solr extend ActiveSupport::Concern
included do
after_save :push_data_to_solr
after_destroy :remove_from_solr
# callbacks
def push_data_to_solr
self.class.rsolr.add(self.slice(*self.class.solr_fields))
self.class.rsolr.commit
end
def remove_from_solr
self.class.rsolr.delete_by_id(self[self.class.primary_key])
self.class.rsolr.commit
end
end
module ClassMethods
attr_accessor :solr_per_page, :solr_page, :solr_total_count, :solr_fields
def solr_options(fields = {})
@solr_per_page = fields[:per_page] || 5
@solr_fields = fields[:fields] || []
end
def search(query, options = {})
return unless query.is_a?(Hash)
@solr_page = options[:page] || 1
query = query.inject('') { |str, item| str << "#{item.first}:#{item.last}" << ' ' }
results = rsolr.paginate(
solr_page,
solr_per_page,
'select',
params: {q: query, fl: self.primary_key}
)
@solr_total_count = results['response']['numFound'].to_i
parse(results['response'])
end
def parse(results)
ids = results['docs'].map { |item| item[self.primary_key].to_i }
self.where(self.primary_key => ids)
end
def rsolr
@rsolr_service ||= RSolr.connect(url: Settings.solr_url)
end
def kaminari_paginate
Kaminari.paginate_array([], total_count: solr_total_count).page(solr_page).per(solr_per_page)
end
def clear_solr_index
rsolr.delete_by_query '*'
rsolr.commit
end
end
end
\ No newline at end of file
class Order < ActiveRecord::Base
belongs_to :user
has_many :order_items, dependent: :destroy
accepts_nested_attributes_for :order_items
end
class CartItem < ActiveRecord::Base class OrderItem < ActiveRecord::Base
belongs_to :cart belongs_to :order
belongs_to :product belongs_to :product
end end
class Product < ActiveRecord::Base class Product < ActiveRecord::Base
include Solr
# scopes # scopes
scope :recommended, ->{ self.offset(rand(self.count)).limit(Settings.limit_product_recommended) } scope :recommended, ->{ self.offset(rand(self.count)).limit(Settings.limit_product_recommended) }
scope :newest, ->{ self.order(created_at: :desc).limit(Settings.limit_product_newest) } scope :newest, ->{ self.order(created_at: :desc).limit(Settings.limit_product_newest) }
...@@ -14,4 +16,6 @@ class Product < ActiveRecord::Base ...@@ -14,4 +16,6 @@ class Product < ActiveRecord::Base
enum product_type: %i(system amazon) enum product_type: %i(system amazon)
mount_uploader :image, ImageUploader mount_uploader :image, ImageUploader
solr_options per_page: 2, fields: [:id, :title]
end end
class User < ActiveRecord::Base class User < ActiveRecord::Base
has_many :carts with_options dependent: :nullify do |assoc|
assoc.has_many :orders
assoc.has_many :products
end
# Include default devise modules. Others available are: # Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable # :confirmable, :lockable, :timeoutable and :omniauthable
......
class BaseService
attr_accessor :current_user, :params
def initialize user = nil, params = {}
@current_user = user
@params = params
end
end
\ No newline at end of file
class CartsController < ApplicationController class OrderService < BaseService
include CartsManagement
before_action :authenticate_user! def create(order_params)
order = current_user.orders.build(order_params)
def index
@cart = Cart.new
end
def create
cart = current_user.carts.build(cart_params)
product_ids = [] product_ids = []
quantites = {} quantites = {}
cart_params['cart_items_attributes'].each do |item| order_params['order_items_attributes'].each do |item|
id = item.last['product_id'] id = item.last['product_id']
product_ids << id product_ids << id
quantites[id] = item.last['quantity'] quantites[id] = item.last['quantity']
...@@ -22,24 +15,19 @@ class CartsController < ApplicationController ...@@ -22,24 +15,19 @@ class CartsController < ApplicationController
total = products.inject(0) do |sum, product| total = products.inject(0) do |sum, product|
sum + (product.price.to_i * quantites[product.id.to_s].to_i) sum + (product.price.to_i * quantites[product.id.to_s].to_i)
end end
cart.total = total order.total = total
if cart.save if order.save
clear_cart send_checkout_email(order)
self.send_checkout_email(cart) {status: true}
redirect_to root_path
else else
render :index {status: false}
end end
end end
def send_checkout_email(cart)
CartMailer.send_checkout(current_user.email, cart).deliver_later
end
private private
def cart_params def send_checkout_email(order)
params.require(:cart).permit(:total, cart_items_attributes: [:product_id, :quantity]) OrderMailer.send_checkout(current_user.email, order).deliver_later
end end
end end
\ No newline at end of file
#cart-info #cart-info
%h3.title Category %h3.title Category
= "Total: #{pluralize(session_cart[:total], 'item')}" = "Total: #{pluralize(session_cart[:total], 'item')}"
= link_to 'Checkout', carts_path, class: 'btn btn-default pull-right' = link_to 'Checkout', orders_path, class: 'btn btn-default pull-right'
\ No newline at end of file \ No newline at end of file
#message
- if notice
.alert.alert-success
= notice
- if alert
.alert.alert-danger
= alert
\ No newline at end of file
.input-group = form_tag search_products_path, method: :get do
%input.form-control{placeholder: "Search for..."} .input-group
= text_field_tag :query, params[:query], class: 'form-control', placeholder: 'Search for title...'
%span.input-group-btn %span.input-group-btn
%button.btn.btn-default{type: "button"} Go! %button.btn.btn-default{type: "submit"} Go!
\ No newline at end of file
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
= render '/layouts/header' = render '/layouts/header'
#wrapper.container-fluid #wrapper.container-fluid
= render '/layouts/message'
.row .row
.col-md-6.search-wrapper .col-md-6.search-wrapper
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
= render '/layouts/header' = render '/layouts/header'
#wrapper.container-fluid #wrapper.container-fluid
= render '/layouts/message'
.row .row
.col-md-6.search-wrapper .col-md-6.search-wrapper
......
%p= "Order ##{@cart.id}" %p= "Order ##{@order.id}"
%p= "Total: #{@cart.total}" %p= "Total: #{@order.total}"
%table %table
%thead %thead
...@@ -9,13 +9,13 @@ ...@@ -9,13 +9,13 @@
%td Price %td Price
%td Quantity %td Quantity
%tbody %tbody
- @cart_items.each_with_index do |cart_item, index| - @order_items.each_with_index do |order_item, index|
%tr %tr
%td.number.text-right %td.number.text-right
= index + 1 = index + 1
%td.title %td.title
= link_to cart_item.product.title, product_url(cart_item.product) = link_to order_item.product.title, product_url(order_item.product)
%td.price %td.price
= cart_item.product.price = order_item.product.price
%td.quantity %td.quantity
= cart_item.quantity = order_item.quantity
\ No newline at end of file \ No newline at end of file
#checkout-cart #checkout-cart
= form_for @cart do |f| = form_for @order do |f|
%table.table %table.table
%thead %thead
%tr %tr
...@@ -8,17 +8,17 @@ ...@@ -8,17 +8,17 @@
%td Price %td Price
%td Quantity %td Quantity
%tbody %tbody
- session_cart[:items].each_with_index do |cart_item, index| - session_cart[:items].each_with_index do |order_item, index|
%tr %tr
%td.number.text-right %td.number.text-right
= index + 1 = index + 1
%td.title %td.title
= link_to cart_item[:title], product_path(cart_item[:id]) = link_to order_item[:title], product_path(order_item[:id])
= hidden_field_tag "cart[cart_items_attributes][#{index}][product_id]", cart_item[:id] = hidden_field_tag "order[order_items_attributes][#{index}][product_id]", order_item[:id]
%td.price %td.price
= cart_item[:price] = order_item[:price]
%td.quantity %td.quantity
= text_field_tag "cart[cart_items_attributes][#{index}][quantity]", cart_item[:quantity] = text_field_tag "order[order_items_attributes][#{index}][quantity]", order_item[:quantity]
%tfoot %tfoot
%tr %tr
%td.text-right{colspan: 4} %td.text-right{colspan: 4}
......
%h3.title
Search result for:
= params[:query]
.module
- if @products.present?
#category-items.products-list-detail
= render @products
.pagination
= paginate @products.kaminari_paginate
\ No newline at end of file
...@@ -24,5 +24,7 @@ module VenShop ...@@ -24,5 +24,7 @@ module VenShop
config.active_record.raise_in_transactional_callbacks = true config.active_record.raise_in_transactional_callbacks = true
config.active_job.queue_adapter = :sidekiq config.active_job.queue_adapter = :sidekiq
config.autoload_paths += Dir["#{config.root}/app/services/**/*.rb"]
end end
end end
...@@ -2,3 +2,5 @@ ...@@ -2,3 +2,5 @@
AWS_ACCESS: AKIAJ77C4CTZOP7TUVWQ AWS_ACCESS: AKIAJ77C4CTZOP7TUVWQ
AWS_SECRET: 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 AWS_SECRET: cYJYb/MLGV0M6oi1+DjlliL1cfxmh78tKXnT6ZmX
AWS_TAG: zigexn6400-22 AWS_TAG: zigexn6400-22
SOLR_URL: http://localhost:8080/solr/venshop
\ No newline at end of file
# MySQL. Versions 5.0+ are recommended.
#
# Install the MYSQL driver
# gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
# gem 'mysql2'
#
# And be sure to use new-style password hashing:
# http://dev.mysql.com/doc/refman/5.0/en/old-client.html
#
default: &default
adapter: mysql2
encoding: utf8
pool: 5
username: root
password: 123456
#socket: /var/run/mysqld/mysqld.sock
development:
<<: *default
database: venshop_development
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: venshop_test
# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
# production:
# url: <%= ENV['DATABASE_URL'] %>
#
production:
<<: *default
database: venshop_vietth
password: 123456
...@@ -15,7 +15,7 @@ default: &default ...@@ -15,7 +15,7 @@ default: &default
pool: 5 pool: 5
username: root username: root
password: 123456 password: 123456
socket: /var/run/mysqld/mysqld.sock #socket: /var/run/mysqld/mysqld.sock
development: development:
<<: *default <<: *default
...@@ -47,8 +47,12 @@ test: ...@@ -47,8 +47,12 @@ test:
# production: # production:
# url: <%= ENV['DATABASE_URL'] %> # url: <%= ENV['DATABASE_URL'] %>
# #
staging:
<<: *default
database: venshop_staging
password: 123456
production: production:
<<: *default <<: *default
database: venshop_production database: venshop_vietth
username: venShop
password: <%= ENV['VENSHOP_DATABASE_PASSWORD'] %> password: <%= ENV['VENSHOP_DATABASE_PASSWORD'] %>
# config valid only for current version of Capistrano
lock '3.4.0'
set :application, 'vietth'
set :pty, false
# set :branch, 'master'
ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
set :use_sudo, false
set :bundle_binstubs, nil
set :linked_files, fetch(:linked_files, []).push('config/database.yml')
set :linked_files, fetch(:linked_files, []).push('config/application.yml')
set :linked_dirs, fetch(:linked_dirs, []).push(
'log',
'tmp/pids',
'tmp/cache',
'tmp/sockets',
'vendor/bundle',
'public/system',
'public/uploads'
)
after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
task :restart do
on roles(:app) do
within current_path do
if test("[ -f #{fetch(:unicorn_pid)} ]")
execute :kill, capture(:cat, fetch(:unicorn_pid))
end
with rails_env: fetch(:rails_env), wait: 3 do
execute :bundle, "exec rake seed:migrate"
config_file = "#{current_path}/config/unicorn/#{fetch(:rails_env)}.rb"
execute :bundle, "exec unicorn -c #{config_file} -D"
end
end
invoke('sidekiq:restart')
end
end
end
after 'deploy:publishing', 'deploy:restart'
set :port, 22
set :user, 'ventura'
set :deploy_via, :remote_cache
set :use_sudo, false
set :repo_url, 'git@gitlab.zigexn.vn:vietth/VietTH-VenShop.git'
server '192.168.1.204',
roles: [:web, :app, :db],
port: fetch(:port),
user: fetch(:user),
primary: true
set :deploy_to, "/web/training/#{fetch(:application)}"
set :ssh_options, {
forward_agent: true,
auth_methods: %w(publickey),
user: fetch(:user)
}
set :rails_env, :production
set :conditionally_migrate, true
\ No newline at end of file
set :port, 22
set :user, 'alicuche'
set :deploy_via, :remote_cache
set :use_sudo, false
server 'alicuche.koding.io',
roles: [:web, :app, :db],
port: fetch(:port),
user: fetch(:user),
primary: true
set :deploy_to, "/home/alicuche/Web/venshop"
set :ssh_options, {
forward_agent: true,
auth_methods: %w(publickey),
user: fetch(:user)
}
set :rails_env, :staging
set :conditionally_migrate, true
\ No newline at end of file
...@@ -39,7 +39,16 @@ Rails.application.configure do ...@@ -39,7 +39,16 @@ Rails.application.configure do
# Raises error for missing translations # Raises error for missing translations
# config.action_view.raise_on_missing_translations = true # config.action_view.raise_on_missing_translations = true
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } # config.action_mailer.delivery_method = :letter_opener
config.action_mailer.smtp_settings = {
config.action_mailer.delivery_method = :letter_opener :address => "smtp.mandrillapp.com",
:port => 587,
:user_name => 'alicuche',
:password => 'alicuche1989'
}
# ActionMailer Config
config.action_mailer.default_url_options = { :host => 'localhost', port: 3000 }
config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = false
end end
...@@ -76,4 +76,17 @@ Rails.application.configure do ...@@ -76,4 +76,17 @@ Rails.application.configure do
# Do not dump schema after migrations. # Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false config.active_record.dump_schema_after_migration = false
config.action_mailer.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => "gmail.com",
:user_name => "alicuche@gmail.com",
:password => "syghbwuxfzfkgmch",
:authentication => :login,
:enable_starttls_auto => true
}
# ActionMailer Config
config.action_mailer.default_url_options = { :host => '192.168.1.204', port: 3005 }
config.action_mailer.delivery_method = :smtp
end end
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# Code is not reloaded between requests.
config.cache_classes = true
# Eager load code on boot. This eager loads most of Rails and
# your application in memory, allowing both threaded web servers
# and those relying on copy on write to perform better.
# Rake tasks automatically ignore this option for performance.
config.eager_load = true
# Full error reports are disabled and caching is turned on.
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Enable Rack::Cache to put a simple HTTP cache in front of your application
# Add `rack-cache` to your Gemfile before enabling this.
# For large-scale production use, consider using a caching reverse proxy like
# NGINX, varnish or squid.
# config.action_dispatch.rack_cache = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present?
# Compress JavaScripts and CSS.
config.assets.js_compressor = :uglifier
# config.assets.css_compressor = :sass
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = false
# Asset digests allow you to set far-future HTTP expiration dates on all assets,
# yet still be able to expire them through the digest params.
config.assets.digest = true
# `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
# Specifies the header that your server uses for sending files.
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
# Use the lowest log level to ensure availability of diagnostic information
# when problems arise.
config.log_level = :debug
# Prepend all log lines with the following tags.
# config.log_tags = [ :subdomain, :uuid ]
# Use a different logger for distributed setups.
# config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = 'http://assets.example.com'
# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
# config.action_mailer.raise_delivery_errors = false
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found).
config.i18n.fallbacks = true
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
# Use default logging formatter so that PID and timestamp are not suppressed.
config.log_formatter = ::Logger::Formatter.new
# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
config.action_mailer.smtp_settings = {
:address => "smtp.gmail.com",
:port => 587,
:domain => "gmail.com",
:user_name => "alicuche@gmail.com",
:password => "syghbwuxfzfkgmch",
:authentication => :login,
:enable_starttls_auto => true
}
# ActionMailer Config
config.action_mailer.default_url_options = { :host => 'uakk111d5a2e.alicuche.koding.io', port: 80 }
config.action_mailer.delivery_method = :smtp
end
...@@ -6,7 +6,7 @@ Devise.setup do |config| ...@@ -6,7 +6,7 @@ Devise.setup do |config|
# confirmation, reset password and unlock tokens in the database. # confirmation, reset password and unlock tokens in the database.
# Devise will use the `secret_key_base` on Rails 4+ applications as its `secret_key` # Devise will use the `secret_key_base` on Rails 4+ applications as its `secret_key`
# by default. You can change it below and use your own secret key. # by default. You can change it below and use your own secret key.
# config.secret_key = '0b9650b3a0239f3503be39e87a6d183934750b8d58d8ba86657170d33ee1be55432c0047d32342832211f9788e0cdbb86fb4dd5066b4e1c8a508a91284b2f1bd' config.secret_key = '0b9650b3a0239f3503be39e87a6d183934750b8d58d8ba86657170d33ee1be55432c0047d32342832211f9788e0cdbb86fb4dd5066b4e1c8a508a91284b2f1bd'
# ==> Mailer Configuration # ==> Mailer Configuration
# Configure the e-mail address which will be shown in Devise::Mailer, # Configure the e-mail address which will be shown in Devise::Mailer,
......
...@@ -6,9 +6,13 @@ Rails.application.routes.draw do ...@@ -6,9 +6,13 @@ Rails.application.routes.draw do
member do member do
get :add_cart get :add_cart
end end
collection do
get :search
end
end end
resources :categories resources :categories
resources :carts resources :orders
end end
\ No newline at end of file
...@@ -20,3 +20,6 @@ test: ...@@ -20,3 +20,6 @@ test:
# instead read values from the environment. # instead read values from the environment.
production: production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
staging:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
...@@ -5,9 +5,16 @@ defaults: &defaults ...@@ -5,9 +5,16 @@ defaults: &defaults
development: development:
<<: *defaults <<: *defaults
solr_url: http://localhost:8080/solr/venshop
test: test:
<<: *defaults <<: *defaults
solr_url: http://localhost:8080/solr/venshop_test
production: production:
<<: *defaults <<: *defaults
solr_url: http://localhost:8080/solr/venshop
staging:
<<: *defaults
solr_url: http://localhost:8080/solr/venshop
\ No newline at end of file
root = "/web/training/vietth/current"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"
listen "/tmp/unicorn.vietth.sock"
worker_processes 2
timeout 30
preload_app true
###########################################
before_fork do |server, worker|
Signal.trap 'TERM' do
Process.kill 'QUIT', Process.pid
end
defined?(ActiveRecord::Base) &&
ActiveRecord::Base.connection.disconnect!
end
after_fork do |server, worker|
Signal.trap 'TERM' do
end
defined?(ActiveRecord::Base) &&
ActiveRecord::Base.establish_connection
end
# Force the bundler gemfile environment variable to
# reference the capistrano "current" symlink
before_exec do |_|
ENV['BUNDLE_GEMFILE'] = File.join(root, 'Gemfile')
end
root = "/home/alicuche/Web/venshop/current"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"
listen "/tmp/unicorn.venshop.sock"
worker_processes 2
timeout 30
preload_app true
###########################################
before_fork do |server, worker|
Signal.trap 'TERM' do
Process.kill 'QUIT', Process.pid || -1
end
defined?(ActiveRecord::Base) &&
ActiveRecord::Base.connection.disconnect!
end
after_fork do |server, worker|
Signal.trap 'TERM' do
end
defined?(ActiveRecord::Base) &&
ActiveRecord::Base.establish_connection
end
# Force the bundler gemfile environment variable to
# reference the capistrano "current" symlink
before_exec do |_|
ENV['BUNDLE_GEMFILE'] = File.join(root, 'Gemfile')
end
class InitUser < SeedMigration::Migration
def up
User.create(email: 'system@demo.com', password: '12345678')
end
def down
end
end
class CreateCarts < ActiveRecord::Migration class CreateOrders < ActiveRecord::Migration
def change def change
create_table :carts do |t| create_table :orders do |t|
t.timestamps null: false t.timestamps null: false
t.references :user, index: true, null: false t.references :user, index: true, null: false
t.decimal :total, default: 0 t.decimal :total, default: 0
......
class CreateCartItems < ActiveRecord::Migration class CreateOrderItems < ActiveRecord::Migration
def change def change
create_table :cart_items do |t| create_table :order_items do |t|
t.timestamps null: false t.timestamps null: false
t.references :cart, index: true t.references :order, index: true
t.references :product, index: true t.references :product, index: true
t.integer :quantity, default: 1 t.integer :quantity, default: 1
t.decimal :price, default: 0 t.decimal :price, default: 0
......
# This migration comes from seed_migration (originally 20140310150145)
class CreateDataMigrations < ActiveRecord::Migration
def up
create_table SeedMigration.migration_table_name do |t|
t.string :version
t.integer :runtime
t.datetime :migrated_on
end
end
def down
drop_table SeedMigration.migration_table_name
end
end
class AddUserToProduct < ActiveRecord::Migration
def change
add_reference :products, :user, index: true
end
end
...@@ -11,36 +11,36 @@ ...@@ -11,36 +11,36 @@
# #
# 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: 20150706090027) do ActiveRecord::Schema.define(version: 20150708092744) do
create_table "cart_items", force: :cascade do |t| create_table "categories", force: :cascade do |t|
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.integer "cart_id", limit: 4 t.string "node_id", limit: 255
t.string "title", limit: 255
t.integer "category_type", limit: 4, default: 0
end
create_table "order_items", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "order_id", limit: 4
t.integer "product_id", limit: 4 t.integer "product_id", limit: 4
t.integer "quantity", limit: 4, default: 1 t.integer "quantity", limit: 4, default: 1
t.decimal "price", precision: 10, default: 0 t.decimal "price", precision: 10, default: 0
end end
add_index "cart_items", ["cart_id"], name: "index_cart_items_on_cart_id", using: :btree add_index "order_items", ["order_id"], name: "index_order_items_on_order_id", using: :btree
add_index "cart_items", ["product_id"], name: "index_cart_items_on_product_id", using: :btree add_index "order_items", ["product_id"], name: "index_order_items_on_product_id", using: :btree
create_table "carts", force: :cascade do |t| create_table "orders", force: :cascade do |t|
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.integer "user_id", limit: 4, null: false t.integer "user_id", limit: 4, null: false
t.decimal "total", precision: 10, default: 0 t.decimal "total", precision: 10, default: 0
end end
add_index "carts", ["user_id"], name: "index_carts_on_user_id", using: :btree add_index "orders", ["user_id"], name: "index_orders_on_user_id", using: :btree
create_table "categories", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "node_id", limit: 255
t.string "title", limit: 255
t.integer "category_type", limit: 4, default: 0
end
create_table "products", force: :cascade do |t| create_table "products", force: :cascade do |t|
t.datetime "created_at", null: false t.datetime "created_at", null: false
...@@ -54,9 +54,17 @@ ActiveRecord::Schema.define(version: 20150706090027) do ...@@ -54,9 +54,17 @@ ActiveRecord::Schema.define(version: 20150706090027) do
t.integer "category_id", limit: 4 t.integer "category_id", limit: 4
t.integer "product_type", limit: 4, default: 0 t.integer "product_type", limit: 4, default: 0
t.string "image", limit: 255 t.string "image", limit: 255
t.integer "user_id", limit: 4
end end
add_index "products", ["category_id"], name: "index_products_on_category_id", using: :btree add_index "products", ["category_id"], name: "index_products_on_category_id", using: :btree
add_index "products", ["user_id"], name: "index_products_on_user_id", using: :btree
create_table "seed_migration_data_migrations", force: :cascade do |t|
t.string "version", limit: 255
t.integer "runtime", limit: 4
t.datetime "migrated_on"
end
create_table "users", force: :cascade do |t| create_table "users", force: :cascade do |t|
t.string "email", limit: 255, default: "", null: false t.string "email", limit: 255, default: "", null: false
......
# This file should contain all the record creation needed to seed the database with its default values. # encoding: UTF-8
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). # This file is auto-generated from the current content of the database. Instead
# of editing this file, please use the migrations feature of Seed Migration to
# incrementally modify your database, and then regenerate this seed file.
# #
# Examples: # If you need to create the database on another system, you should be using
# db:seed, not running all the migrations from scratch. The latter is a flawed
# and unsustainable approach (the more migrations you'll amass, the slower
# it'll run and the greater likelihood for issues).
# #
# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) # It's strongly recommended to check this file into your version control system.
# Mayor.create(name: 'Emanuel', city: cities.first)
ActiveRecord::Base.transaction do
end
SeedMigration::Migrator.bootstrap(20150708092434)
namespace :sidekiq do
def sidekiq_pid
current_path + 'tmp/pids/sidekiq.pid'
end
def pid_file_exists?
test(*("[ -f #{sidekiq_pid} ]").split(' '))
end
def pid_process_exists?
pid_file_exists? && test(*("kill -0 $( cat #{sidekiq_pid} )").split(' '))
end
task :start do
on roles(:app) do
if !pid_process_exists?
execute "cd #{current_path} && RAILS_ENV=#{fetch(:rails_env)} ~/.rvm/bin/rvm default do bundle exec sidekiq -L log/sidekiq.log -P #{sidekiq_pid} -q default -q mailers -d"
end
end
end
task :stop do
on roles(:app) do
if pid_process_exists?
execute "kill `cat #{sidekiq_pid}`"
execute "rm #{sidekiq_pid}"
end
end
end
task :restart do
on roles(:app) do
invoke "sidekiq:stop"
invoke "sidekiq:start"
end
end
end
\ No newline at end of file
...@@ -45,6 +45,7 @@ namespace :aws do ...@@ -45,6 +45,7 @@ namespace :aws do
product.image_sm_url = item['SmallImage'].try(:[], 'URL') product.image_sm_url = item['SmallImage'].try(:[], 'URL')
product.price = item['ItemAttributes']['ListPrice'].try(:[], 'Amount').try(:to_i) product.price = item['ItemAttributes']['ListPrice'].try(:[], 'Amount').try(:to_i)
product.product_type = Product.product_types[:amazon] product.product_type = Product.product_types[:amazon]
product.user = User.first
end end
end end
......
FactoryGirl.define do
factory :category do
title 'String title'
category_type ''
node_id ''
end
end
FactoryGirl.define do
factory :product do
title 'String title'
asin ''
image_lg_url ''
image_md_url ''
image_sm_url ''
image ''
price 1000
category
end
end
require 'rails_helper'
RSpec.describe Category, :type => :model do
describe "ActiveRecord associations" do
it { should have_many(:products).dependent(:destroy) }
end
end
require 'rails_helper'
RSpec.describe OrderItem, :type => :model do
describe "ActiveRecord associations" do
it { should belong_to(:order) }
it { should belong_to(:product) }
end
end
require 'rails_helper'
RSpec.describe Order, :type => :model do
describe "ActiveRecord associations" do
it { should belong_to(:user) }
it { should have_many(:order_items).dependent(:destroy) }
it { should accept_nested_attributes_for(:order_items) }
end
end
require 'rails_helper'
RSpec.describe Product, :type => :model do
let(:product) { FactoryGirl.build(:product) }
describe "ActiveModel validations" do
it { should validate_presence_of(:category) }
it { should validate_presence_of(:title) }
it { should validate_length_of(:title).is_at_most(255) }
end
describe "ActiveRecord associations" do
it { should belong_to(:category) }
it { should belong_to(:user) }
end
describe 'Scopes' do
before(:all) do
FactoryGirl.create_list(:product, 20)
end
it 'should be random a collection' do
expect(Product.recommended.size).to eq(Settings.limit_product_recommended)
end
it 'Should be return newest' do
newest_products = Product.order(created_at: :desc).limit(Settings.limit_product_newest)
expect(Product.newest).to eq(newest_products)
end
end
describe 'Solr' do
before(:each) do
Product.clear_solr_index
end
it 'Search result should be returned' do
FactoryGirl.create(:product, title: 'PHP basic')
FactoryGirl.create(:product, title: 'PHP advance')
FactoryGirl.create(:product, title: 'Ruby beginner')
FactoryGirl.create(:product, title: 'Rails senior')
expect(Product.search(title: 'PHP').size).to eq(2)
expect(Product.search(title: 'ruby').size).to eq(1)
end
end
end
require 'rails_helper'
RSpec.describe User, :type => :model do
describe "ActiveRecord associations" do
it { should have_many(:orders).dependent(:nullify) }
it { should have_many(:products).dependent(:nullify) }
end
end
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.maintain_test_schema!
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
end
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
# This option will default to `true` in RSpec 4. It makes the `description`
# and `failure_message` of custom matchers include text for helper methods
# defined using `chain`, e.g.:
# be_bigger_than(2).and_smaller_than(4).description
# # => "be bigger than 2 and smaller than 4"
# ...rather than:
# # => "be bigger than 2"
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment