power_shop 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +13 -0
- data/Rakefile +18 -0
- data/app/controllers/catalog_controller.rb +2 -0
- data/app/controllers/power_shop/application_controller.rb +13 -0
- data/app/controllers/power_shop/cart_controller.rb +53 -0
- data/app/controllers/power_shop/catalog_controller.rb +32 -0
- data/app/controllers/power_shop/orders_controller.rb +33 -0
- data/app/forms/power_shop/order_form.rb +99 -0
- data/app/mailers/order_notifications.rb +2 -0
- data/app/mailers/power_shop/order_notifications.rb +29 -0
- data/app/models/category.rb +4 -0
- data/app/models/order.rb +2 -0
- data/app/models/order_item.rb +2 -0
- data/app/models/power_shop/category.rb +18 -0
- data/app/models/power_shop/order.rb +8 -0
- data/app/models/power_shop/order_item.rb +9 -0
- data/app/models/power_shop/product.rb +45 -0
- data/app/models/power_shop/shop_image.rb +8 -0
- data/app/models/power_shop/shopping_cart.rb +8 -0
- data/app/models/power_shop/shopping_cart_item.rb +6 -0
- data/app/models/product.rb +4 -0
- data/app/models/shop_image.rb +3 -0
- data/app/models/shopping_cart.rb +2 -0
- data/app/models/shopping_cart_item.rb +2 -0
- data/app/uploaders/power_shop/shop_image_uploader.rb +30 -0
- data/app/uploaders/shop_image_uploader.rb +2 -0
- data/app/views/power_shop/cart/show.html.erb +0 -0
- data/app/views/power_shop/catalog/category.html.erb +0 -0
- data/app/views/power_shop/catalog/index.html.erb +2 -0
- data/app/views/power_shop/catalog/product.html.erb +0 -0
- data/app/views/power_shop/orders/success.html.erb +0 -0
- data/config/locales/ru.yml +12 -0
- data/config/routes.rb +18 -0
- data/db/migrate/20140723131458_create_categories.rb +20 -0
- data/db/migrate/20140728071046_create_products.rb +20 -0
- data/db/migrate/20140729041407_create_cart.rb +8 -0
- data/db/migrate/20140731171009_create_shop_image.rb +13 -0
- data/db/migrate/20140804101936_create_friendly_id_slugs.rb +15 -0
- data/db/migrate/20140828061402_create_orders.rb +13 -0
- data/db/migrate/20140828061556_create_order_items.rb +14 -0
- data/lib/generators/power_shop/install/install_generator.rb +20 -0
- data/lib/generators/power_shop/install/templates/active_admin/category.rb.erb +43 -0
- data/lib/generators/power_shop/install/templates/active_admin/order.rb +50 -0
- data/lib/generators/power_shop/install/templates/active_admin/product.rb.erb +64 -0
- data/lib/power_shop/engine.rb +27 -0
- data/lib/power_shop/middleware/shopping_cart.rb +26 -0
- data/lib/power_shop/version.rb +3 -0
- data/lib/power_shop.rb +4 -0
- data/lib/spec.rake +15 -0
- data/lib/tasks/spec.rake +15 -0
- data/power_shop.gemspec +45 -0
- data/script/rails +7 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- metadata +78 -10
- data/spec/dummy/log/test.log +0 -85730
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 927a4565c38e1512685b3dfd996f02c190835387
|
4
|
+
data.tar.gz: ae853db448b6bfeac215261b925b0877450326cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38a297f31b3b99cb7d377479793dfb86d595b5f307629cb43a622d525137ffefef102a919591a9ef8e9756bdcfb91b7f1574c70ac3369d99d72fc1a098ac8fe9
|
7
|
+
data.tar.gz: 1234305aa80b6489041eb86eed6b1b0c32a6725f3d803fee66edf7364e89c3dbe666590104a7341fe6f6af62df92dbc9fd732b90b43ae455828a60d3c8847d8e
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Semyon Pupkov
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
power_shop
|
2
|
+
==========
|
3
|
+
|
4
|
+
Simple and flexible Ruby On Rails Ecomerce engine
|
5
|
+
|
6
|
+
![build status](https://semaphoreapp.com/api/v1/projects/ebecbf67-1b78-4e5d-9a59-208ad57864f8/217416/shields_badge.png)
|
7
|
+
[![Code Climate](https://codeclimate.com/github/artofhuman/power_shop/badges/gpa.svg)](https://codeclimate.com/github/artofhuman/power_shop)
|
8
|
+
[![Gem Version](https://badge.fury.io/rb/power_shop.svg)](http://badge.fury.io/rb/power_shop)
|
9
|
+
|
10
|
+
Requirements
|
11
|
+
|
12
|
+
1. CKeditor https://github.com/galetahub/ckeditor (Add gem ckeditor to you Gemfile)
|
13
|
+
2. Rmagick (sudo apt-get install libmagickwand-dev)
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'active_support/deprecation'
|
8
|
+
ActiveSupport::Deprecation.silenced = true
|
9
|
+
|
10
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
11
|
+
load 'rails/tasks/engine.rake'
|
12
|
+
|
13
|
+
load 'lib/tasks/spec.rake'
|
14
|
+
|
15
|
+
Bundler::GemHelper.install_tasks
|
16
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each {|f| load f }
|
17
|
+
|
18
|
+
task :default => [:spec]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PowerShop
|
2
|
+
class ApplicationController < ::ApplicationController
|
3
|
+
|
4
|
+
# Public: get cart from middleware
|
5
|
+
# TODO: concern for project application controller
|
6
|
+
#
|
7
|
+
# Returns ShoppingCart
|
8
|
+
def cart
|
9
|
+
@cart ||= request.env['shopping_cart']
|
10
|
+
end
|
11
|
+
helper_method :cart
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module PowerShop
|
2
|
+
# Controller represents methods for cart
|
3
|
+
class CartController < PowerShop::ApplicationController
|
4
|
+
|
5
|
+
before_filter :find_product, :only => [:add_product, :delete_product]
|
6
|
+
skip_before_filter :verify_authenticity_token
|
7
|
+
|
8
|
+
# Public: add products to shopping cart
|
9
|
+
# if request is simple post redirects back
|
10
|
+
# if request is ajax respond with json message
|
11
|
+
#
|
12
|
+
# Returns text/html or text/json
|
13
|
+
def add_product
|
14
|
+
cart.add(@product, @product.price, params.fetch(:quantity, 1))
|
15
|
+
|
16
|
+
if request.xhr?
|
17
|
+
render_json_cart
|
18
|
+
else
|
19
|
+
redirect_to :back
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete_product
|
24
|
+
# TODO: 99 - its hack for remove all items :)
|
25
|
+
cart.remove(@product, 99)
|
26
|
+
redirect_to :back
|
27
|
+
end
|
28
|
+
|
29
|
+
# Pubclic: show cart page
|
30
|
+
# TODO: preload associations
|
31
|
+
#
|
32
|
+
# Returns text/html
|
33
|
+
def show
|
34
|
+
@cart = cart
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def find_product
|
40
|
+
@product = ::Product.find(params[:product_id])
|
41
|
+
end
|
42
|
+
|
43
|
+
# Internal: render json response with cart params
|
44
|
+
#
|
45
|
+
# Returns text/json
|
46
|
+
def render_json_cart
|
47
|
+
render :json => {
|
48
|
+
total_items: cart.total_unique_items,
|
49
|
+
subtotal: cart.subtotal
|
50
|
+
}.to_json
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PowerShop
|
2
|
+
class CatalogController < PowerShop::ApplicationController
|
3
|
+
# Public: show all products with pagination
|
4
|
+
#
|
5
|
+
# Returns: text/html
|
6
|
+
def index
|
7
|
+
@products = scoped_products.page(params[:page])
|
8
|
+
end
|
9
|
+
|
10
|
+
# Public: show products inside current category
|
11
|
+
#
|
12
|
+
# Returns: text/html
|
13
|
+
def category
|
14
|
+
@category = ::Category.friendly.find(params[:id])
|
15
|
+
@products = scoped_products.where(category: @category).page(params[:page])
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: show detail page for product
|
19
|
+
#
|
20
|
+
# Returns text/html
|
21
|
+
def product
|
22
|
+
@category = ::Category.friendly.find(params[:category_id])
|
23
|
+
@product = scoped_products.friendly.find(params[:id])
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def scoped_products
|
29
|
+
::Product.active.includes(:images, :category)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module PowerShop
|
2
|
+
class OrdersController < PowerShop::ApplicationController
|
3
|
+
|
4
|
+
# GET: create new order action
|
5
|
+
# if order success created, redirect to success page
|
6
|
+
#
|
7
|
+
# Returns text/html
|
8
|
+
def create
|
9
|
+
@cart = cart
|
10
|
+
@order_form = ::OrderForm.new(@cart, permited_params[:order_form])
|
11
|
+
|
12
|
+
if @order_form.valid?
|
13
|
+
if @order_form.perform
|
14
|
+
flash[:order_id] = @order_form.order.id
|
15
|
+
return redirect_to power_shop.success_order_url
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
render :template => 'power_shop/cart/show'
|
20
|
+
end
|
21
|
+
|
22
|
+
# GET: show simple page for success message
|
23
|
+
#
|
24
|
+
# Returns text/html
|
25
|
+
def success
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
def permited_params
|
30
|
+
params.permit(order_form: [:user_name, :user_phone, :user_email])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module PowerShop
|
4
|
+
# class represent form for create new order
|
5
|
+
# TODO: this class want specs
|
6
|
+
class OrderForm
|
7
|
+
include ActiveModel::Model
|
8
|
+
|
9
|
+
attr_accessor :user_name, :user_email, :user_phone, :delivery_address
|
10
|
+
attr_reader :cart, :order
|
11
|
+
|
12
|
+
validates :user_name, :user_phone, presence: true
|
13
|
+
validates_email_format_of :user_email, allow_nil: true, allow_blank: true
|
14
|
+
|
15
|
+
delegate :total, :to => :cart
|
16
|
+
|
17
|
+
# Public: initialize new order form
|
18
|
+
#
|
19
|
+
# cart - Shopping cart
|
20
|
+
def initialize(cart, attributes={})
|
21
|
+
@order = ::Order.new
|
22
|
+
@cart = cart
|
23
|
+
|
24
|
+
apply_attributes(attributes) if attributes.present?
|
25
|
+
end
|
26
|
+
|
27
|
+
# Public: process create new order, clear cart, and sent notifications
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
# if @order_form.valid?
|
31
|
+
# @order_form.perform
|
32
|
+
#
|
33
|
+
# Returns boolean
|
34
|
+
def perform
|
35
|
+
return false unless valid?
|
36
|
+
|
37
|
+
if result = make_order
|
38
|
+
cart.clear
|
39
|
+
send_emails
|
40
|
+
end
|
41
|
+
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def create_order_items
|
48
|
+
cart.shopping_cart_items.each do |cart_item|
|
49
|
+
order.order_items.create(
|
50
|
+
{
|
51
|
+
product_id: cart_item.item.id,
|
52
|
+
product_title: cart_item.item.name,
|
53
|
+
price: cart_item.price,
|
54
|
+
quantity: cart_item.quantity
|
55
|
+
}
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_order
|
61
|
+
not_expected_attributes = ['id', 'created_at', 'updated_at']
|
62
|
+
order_attributes = @order.attribute_names.reject do |attr|
|
63
|
+
not_expected_attributes.include?(attr)
|
64
|
+
end
|
65
|
+
|
66
|
+
order_attributes.each do |attr|
|
67
|
+
order.send("#{attr}=", self.send(attr))
|
68
|
+
end
|
69
|
+
|
70
|
+
order.save!
|
71
|
+
end
|
72
|
+
|
73
|
+
# Internal: it makes order in database
|
74
|
+
#
|
75
|
+
# Retuurns boolean
|
76
|
+
def make_order
|
77
|
+
ActiveRecord::Base.transaction do
|
78
|
+
create_order
|
79
|
+
create_order_items
|
80
|
+
end
|
81
|
+
rescue
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
def send_emails
|
86
|
+
::OrderNotifications.for_admin(order).deliver
|
87
|
+
::OrderNotifications.for_user(order, user_email).deliver if user_email.present?
|
88
|
+
end
|
89
|
+
|
90
|
+
# Internal: sets form values
|
91
|
+
#
|
92
|
+
# Returns nothing
|
93
|
+
def apply_attributes(attributes)
|
94
|
+
attributes.each_pair do |attr, value|
|
95
|
+
send("#{attr}=", value)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PowerShop
|
2
|
+
class OrderNotifications < ActionMailer::Base
|
3
|
+
default from: "from@example.com"
|
4
|
+
|
5
|
+
def for_admin(order)
|
6
|
+
@order = order
|
7
|
+
mail(
|
8
|
+
to: admin_emails,
|
9
|
+
subject: 'На сайте оформлен заказ',
|
10
|
+
content_type: "text/html"
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def for_user(order, user_email)
|
15
|
+
@order = order
|
16
|
+
mail(
|
17
|
+
to: user_email,
|
18
|
+
subject: 'Спасибо за заказ',
|
19
|
+
content_type: "text/html"
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def admin_emails
|
26
|
+
'admin@example.com'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/app/models/order.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module PowerShop
|
2
|
+
class Category < ActiveRecord::Base
|
3
|
+
self.abstract_class = true
|
4
|
+
self.table_name = 'categories'
|
5
|
+
|
6
|
+
acts_as_nested_set
|
7
|
+
|
8
|
+
has_many :products, :dependent => :destroy,
|
9
|
+
class_name: '::Product'
|
10
|
+
|
11
|
+
has_one :image, :class_name => '::ShopImage', :as => :subject,
|
12
|
+
:dependent => :destroy
|
13
|
+
|
14
|
+
accepts_nested_attributes_for :image, :allow_destroy => true
|
15
|
+
|
16
|
+
validates :name, presence: true
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module PowerShop
|
2
|
+
class Product < ActiveRecord::Base
|
3
|
+
self.abstract_class = true
|
4
|
+
self.table_name = 'products'
|
5
|
+
|
6
|
+
belongs_to :category, class_name: '::Category'
|
7
|
+
|
8
|
+
# == Calbacks for calculate active products in category
|
9
|
+
after_create :increment_category_count, if: ->(product) { product.active? }
|
10
|
+
after_destroy :decrement_category_count, if: ->(product) { product.active? }
|
11
|
+
after_update :touch_category_count
|
12
|
+
|
13
|
+
has_many :images, :class_name => '::ShopImage', :as => :subject,
|
14
|
+
:dependent => :destroy
|
15
|
+
|
16
|
+
accepts_nested_attributes_for :images, :allow_destroy => true
|
17
|
+
validates_presence_of :name, :price, :category_id
|
18
|
+
|
19
|
+
# Public: get only active products
|
20
|
+
#
|
21
|
+
# Returns ActiveRecord::Relation
|
22
|
+
def self.active
|
23
|
+
where(active: true)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: get first image from images
|
27
|
+
#
|
28
|
+
# Returns ShopImage
|
29
|
+
def main_image
|
30
|
+
images.first
|
31
|
+
end
|
32
|
+
|
33
|
+
def increment_category_count
|
34
|
+
::Category.increment_counter :products_count, category_id
|
35
|
+
end
|
36
|
+
|
37
|
+
def decrement_category_count
|
38
|
+
::Category.decrement_counter :products_count, category_id
|
39
|
+
end
|
40
|
+
|
41
|
+
def touch_category_count
|
42
|
+
active? ? increment_category_count : decrement_category_count
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module PowerShop
|
4
|
+
class ShopImageUploader < CarrierWave::Uploader::Base
|
5
|
+
|
6
|
+
include CarrierWave::RMagick
|
7
|
+
|
8
|
+
# Choose what kind of storage to use for this uploader:
|
9
|
+
storage :file
|
10
|
+
# storage :fog
|
11
|
+
|
12
|
+
# Override the directory where uploaded files will be stored.
|
13
|
+
# This is a sensible default for uploaders that are meant to be mounted:
|
14
|
+
def store_dir
|
15
|
+
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
version :admin_thumb do
|
19
|
+
process :resize_to_limit => [100, 100]
|
20
|
+
end
|
21
|
+
|
22
|
+
version :thumb do
|
23
|
+
process :resize_to_limit => [100, 100]
|
24
|
+
end
|
25
|
+
|
26
|
+
def extension_white_list
|
27
|
+
%w(jpg jpeg png)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/config/routes.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
PowerShop::Engine.routes.draw do
|
3
|
+
scope 'catalog' do
|
4
|
+
get '/:id', to: 'catalog#category', as: 'category'
|
5
|
+
get '/:category_id/:id', to: 'catalog#product', as: 'product'
|
6
|
+
end
|
7
|
+
|
8
|
+
get 'catalog', to: 'catalog#index'
|
9
|
+
|
10
|
+
resource :cart, :only => [:show, :destroy], controller: 'cart' do
|
11
|
+
post 'add_product/:product_id' => 'cart#add_product', :as => 'add_product'
|
12
|
+
delete 'delete_product/:product_id' => 'cart#delete_product', :as => 'delete_product'
|
13
|
+
end
|
14
|
+
|
15
|
+
resource :order, :only => :create do
|
16
|
+
get 'success' => 'orders#success'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class CreateCategories < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :categories do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.string :slug
|
6
|
+
t.integer :parent_id
|
7
|
+
t.integer :lft
|
8
|
+
t.integer :rgt
|
9
|
+
t.integer :depth
|
10
|
+
t.text :description
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
add_index :categories, :slug, unique: true
|
16
|
+
add_index :categories, :rgt
|
17
|
+
add_index :categories, :lft
|
18
|
+
add_index :categories, :parent_id
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class CreateProducts < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :products do |t|
|
4
|
+
t.belongs_to :category, null: false
|
5
|
+
t.boolean :active, default: true
|
6
|
+
t.string :name, null: false
|
7
|
+
t.string :slug
|
8
|
+
t.text :description
|
9
|
+
t.float :price, null: false
|
10
|
+
t.integer :sort, default: 500
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
add_index :products, :slug, unique: true
|
16
|
+
add_index :products, :category_id
|
17
|
+
|
18
|
+
add_column :categories, :products_count, :integer, :default => 0
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateShopImage < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :shop_images do |t|
|
4
|
+
t.integer :subject_id, null: false
|
5
|
+
t.string :subject_type, null: false
|
6
|
+
t.string :image
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :shop_images, :subject_id
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateFriendlyIdSlugs < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :friendly_id_slugs do |t|
|
4
|
+
t.string :slug, :null => false
|
5
|
+
t.integer :sluggable_id, :null => false
|
6
|
+
t.string :sluggable_type, :limit => 50
|
7
|
+
t.string :scope
|
8
|
+
t.datetime :created_at
|
9
|
+
end
|
10
|
+
add_index :friendly_id_slugs, :sluggable_id
|
11
|
+
add_index :friendly_id_slugs, [:slug, :sluggable_type]
|
12
|
+
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], :unique => true
|
13
|
+
add_index :friendly_id_slugs, :sluggable_type
|
14
|
+
end
|
15
|
+
end
|