comable-core 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +97 -0
- data/app/assets/javascripts/comable/application.js +13 -0
- data/app/assets/stylesheets/comable/application.css +13 -0
- data/app/controllers/concerns/comable/permitted_attributes.rb +15 -0
- data/app/helpers/comable/application_helper.rb +84 -0
- data/app/helpers/comable/products_helper.rb +82 -0
- data/app/mailers/comable/order_mailer.rb +18 -0
- data/app/models/comable/ability.rb +18 -0
- data/app/models/comable/address.rb +39 -0
- data/app/models/comable/category.rb +72 -0
- data/app/models/comable/image.rb +15 -0
- data/app/models/comable/order.rb +121 -0
- data/app/models/comable/order/associations.rb +22 -0
- data/app/models/comable/order/callbacks.rb +43 -0
- data/app/models/comable/order/morrisable.rb +20 -0
- data/app/models/comable/order/scopes.rb +17 -0
- data/app/models/comable/order/validations.rb +39 -0
- data/app/models/comable/order_item.rb +98 -0
- data/app/models/comable/order_item/csvable.rb +32 -0
- data/app/models/comable/page.rb +30 -0
- data/app/models/comable/payment.rb +87 -0
- data/app/models/comable/payment_method.rb +27 -0
- data/app/models/comable/product.rb +55 -0
- data/app/models/comable/product/csvable.rb +20 -0
- data/app/models/comable/shipment.rb +85 -0
- data/app/models/comable/shipment_method.rb +9 -0
- data/app/models/comable/stock.rb +71 -0
- data/app/models/comable/stock/csvable.rb +26 -0
- data/app/models/comable/store.rb +30 -0
- data/app/models/comable/theme.rb +25 -0
- data/app/models/comable/tracker.rb +17 -0
- data/app/models/comable/user.rb +130 -0
- data/app/models/concerns/comable/cart_owner.rb +94 -0
- data/app/models/concerns/comable/checkout.rb +85 -0
- data/app/models/concerns/comable/importable.rb +67 -0
- data/app/models/concerns/comable/liquidable.rb +12 -0
- data/app/models/concerns/comable/product/search.rb +41 -0
- data/app/models/concerns/comable/ransackable.rb +38 -0
- data/app/models/concerns/comable/role_owner.rb +15 -0
- data/app/models/concerns/comable/sku_choice.rb +19 -0
- data/app/models/concerns/comable/sku_item.rb +17 -0
- data/app/uploaders/image_uploader.rb +7 -0
- data/app/views/comable/order_mailer/complete.text.erb +42 -0
- data/config/initializers/comma.rb +8 -0
- data/config/locales/en.yml +424 -0
- data/config/locales/ja.yml +425 -0
- data/db/migrate/20131214194807_create_comable_products.rb +15 -0
- data/db/migrate/20140120032559_create_comable_users.rb +46 -0
- data/db/migrate/20140502060116_create_comable_stocks.rb +14 -0
- data/db/migrate/20140723175431_create_comable_orders.rb +20 -0
- data/db/migrate/20140723175810_create_comable_order_items.rb +19 -0
- data/db/migrate/20140817194104_create_comable_payment_methods.rb +13 -0
- data/db/migrate/20140921191416_create_comable_shipment_methods.rb +11 -0
- data/db/migrate/20140926063541_create_comable_stores.rb +11 -0
- data/db/migrate/20141024025526_create_comable_addresses.rb +17 -0
- data/db/migrate/20150111031228_create_comable_categories.rb +10 -0
- data/db/migrate/20150111031229_create_comable_products_categories.rb +8 -0
- data/db/migrate/20150112173706_create_comable_images.rb +9 -0
- data/db/migrate/20150423095210_create_comable_shipments.rb +13 -0
- data/db/migrate/20150511171940_create_comable_payments.rb +12 -0
- data/db/migrate/20150513185230_create_comable_trackers.rb +12 -0
- data/db/migrate/20150519080729_create_comable_pages.rb +17 -0
- data/db/migrate/20150612143226_create_comable_themes.rb +15 -0
- data/db/migrate/20150612143445_add_theme_id_to_comable_stores.rb +7 -0
- data/db/seeds.rb +5 -0
- data/db/seeds/comable/users.rb +51 -0
- data/lib/comable/core.rb +48 -0
- data/lib/comable/core/configuration.rb +22 -0
- data/lib/comable/core/engine.rb +50 -0
- data/lib/comable/deprecator.rb +26 -0
- data/lib/comable/payment_provider.rb +14 -0
- data/lib/comable/payment_provider/base.rb +42 -0
- data/lib/comable/payment_provider/general.rb +15 -0
- data/lib/comable/state_machine_patch.rb +32 -0
- data/lib/comma_extractor_extentions.rb +31 -0
- data/lib/generators/comable/install/install_generator.rb +133 -0
- data/lib/generators/comable/install/templates/config/initializers/comable.rb +31 -0
- data/lib/tasks/comable_tasks.rake +4 -0
- metadata +346 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c695838cc0b5b98cc5c786a0065e9d2e17d6f937
|
4
|
+
data.tar.gz: c03b1988ad054478da5f280d9c0389017bebaf10
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c4ef62d68988158a6d985c432a1744ea1e31821eff13a45ebe881da8d9e2180fe9fdaa589ef77d7042e96fed15c1e8c8e9e23d8daa6492e29648cfd2fa04b4fa
|
7
|
+
data.tar.gz: 2470403342475d734c7d7db9bec17b54d3dd503184525f11134ed6ccd487b17289bd3aa76addb71b915f6732f900f2482ad22c18bfa77e5050951120538d3a64
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 YOSHIDA Hiroki
|
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/Rakefile
ADDED
@@ -0,0 +1,97 @@
|
|
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 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Comable'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
14
|
+
end
|
15
|
+
|
16
|
+
APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
|
17
|
+
load 'rails/tasks/engine.rake'
|
18
|
+
|
19
|
+
def command(string)
|
20
|
+
puts string
|
21
|
+
system string
|
22
|
+
end
|
23
|
+
|
24
|
+
if File.exist?('comable.gemspec')
|
25
|
+
$LOAD_PATH.unshift File.expand_path('..', __FILE__)
|
26
|
+
require 'tasks/release'
|
27
|
+
|
28
|
+
namespace :app do
|
29
|
+
namespace :spec do
|
30
|
+
desc 'Run the code examples'
|
31
|
+
RSpec::Core::RakeTask.new(:all) do |t|
|
32
|
+
FRAMEWORKS.each do |framework|
|
33
|
+
t.pattern << ",#{framework}/spec/**{,/*/**}/*_spec.rb"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
FRAMEWORKS.each do |framework|
|
38
|
+
desc "Run the code examples in #{framework}/spec"
|
39
|
+
RSpec::Core::RakeTask.new(framework) do |t|
|
40
|
+
t.pattern = "../#{framework}/spec/**{,/*/**}/*_spec.rb"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
namespace :db do
|
47
|
+
task 'migrate' => 'app:db:migrate'
|
48
|
+
task 'app:db:migrate' => 'migrate:all'
|
49
|
+
|
50
|
+
namespace :migrate do
|
51
|
+
task :all do
|
52
|
+
FRAMEWORKS.each do |framework|
|
53
|
+
command "cd #{framework} && test -d db/migrate && bundle exec rake db:migrate RAILS_ENV=#{Rails.env}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
namespace :migrate do
|
59
|
+
task 'reset' => 'app:db:migrate:reset'
|
60
|
+
task 'app:db:migrate:reset' => 'app:db:migrate'
|
61
|
+
task 'app:db:migrate' => 'reset:all'
|
62
|
+
|
63
|
+
namespace :reset do
|
64
|
+
task :all do
|
65
|
+
command "bundle exec rake db:drop db:create RAILS_ENV=#{Rails.env}"
|
66
|
+
Rake::Task['db:migrate'].invoke
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
task default: ['app:spec:all', 'rubocop', 'brakeman:all']
|
73
|
+
else
|
74
|
+
task default: ['app:spec', 'rubocop', 'brakeman']
|
75
|
+
end
|
76
|
+
|
77
|
+
Bundler::GemHelper.install_tasks
|
78
|
+
|
79
|
+
# from https://github.com/rspec/rspec-rails/issues/936
|
80
|
+
task 'test:prepare'
|
81
|
+
|
82
|
+
task :rubocop do
|
83
|
+
sh 'rubocop'
|
84
|
+
end
|
85
|
+
|
86
|
+
task :brakeman do
|
87
|
+
sh 'brakeman --exit-on-warn --ignore-config .brakeman.ignore'
|
88
|
+
end
|
89
|
+
|
90
|
+
namespace :brakeman do
|
91
|
+
task :all do
|
92
|
+
FRAMEWORKS.each do |framework|
|
93
|
+
next unless File.directory?("#{framework}/app")
|
94
|
+
sh "brakeman --exit-on-warn --ignore-config .brakeman.ignore #{framework}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Comable
|
2
|
+
module ApplicationHelper
|
3
|
+
def current_store
|
4
|
+
@current_store ||= Comable::Store.instance
|
5
|
+
end
|
6
|
+
|
7
|
+
def current_comable_user
|
8
|
+
resource = current_admin_user || current_user || Comable::User.new
|
9
|
+
resource.with_cookies(cookies)
|
10
|
+
end
|
11
|
+
|
12
|
+
def current_order
|
13
|
+
current_comable_user.incomplete_order
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_trackers
|
17
|
+
@curent_trackers ||= (controller_name == 'orders' && action_name == 'create') ? Comable::Tracker.activated : Comable::Tracker.activated.with_place(:everywhere)
|
18
|
+
end
|
19
|
+
|
20
|
+
def next_order_path
|
21
|
+
comable.next_order_path(state: current_order.state)
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_order_path
|
25
|
+
return next_order_path unless params[:state]
|
26
|
+
comable.next_order_path(state: params[:state])
|
27
|
+
end
|
28
|
+
|
29
|
+
def store_location
|
30
|
+
session[:user_return_to] = request.fullpath.gsub('//', '/')
|
31
|
+
end
|
32
|
+
|
33
|
+
def name_with_honorific(name)
|
34
|
+
Comable.t('honorific', name: name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def name_with_quantity(name, quantity)
|
38
|
+
return name unless quantity
|
39
|
+
return name if quantity <= 1
|
40
|
+
[
|
41
|
+
name,
|
42
|
+
"x#{quantity}"
|
43
|
+
].join(' ')
|
44
|
+
end
|
45
|
+
|
46
|
+
def liquidize(content, arguments)
|
47
|
+
string = Liquid::Template.parse(content).render(arguments.stringify_keys)
|
48
|
+
string.respond_to?(:html_safe) ? string.html_safe : string
|
49
|
+
end
|
50
|
+
|
51
|
+
# To use the functionality of liquid-rails
|
52
|
+
def liquid_assigns
|
53
|
+
view_context.assigns.merge(
|
54
|
+
current_store: current_store,
|
55
|
+
current_comable_user: current_comable_user,
|
56
|
+
current_order: current_order,
|
57
|
+
current_trackers: current_trackers,
|
58
|
+
form_authenticity_token: form_authenticity_token
|
59
|
+
).stringify_keys
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def after_sign_in_path_for(_resource)
|
65
|
+
session.delete(:user_return_to) || comable.root_path
|
66
|
+
end
|
67
|
+
|
68
|
+
def after_sign_out_path_for(_resource)
|
69
|
+
session.delete(:user_return_to) || comable.root_path
|
70
|
+
end
|
71
|
+
|
72
|
+
def after_sign_up_path_for(resource)
|
73
|
+
signed_in_root_path(resource) || comable.root_path
|
74
|
+
end
|
75
|
+
|
76
|
+
def after_update_path_for(resource)
|
77
|
+
signed_in_root_path(resource) || comable.root_path
|
78
|
+
end
|
79
|
+
|
80
|
+
def after_resetting_password_path_for(resource)
|
81
|
+
signed_in_root_path(resource) || comable.root_path
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Comable
|
2
|
+
module ProductsHelper
|
3
|
+
def listed_categories(categories, options = {})
|
4
|
+
content_tag(options[:tag] || :ul, class: options[:class]) do
|
5
|
+
categories.map do |category|
|
6
|
+
content_tag(:li, link_to_category(category), class: "#{'active' if @category == category}")
|
7
|
+
end.join.html_safe
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def link_to_category(category, force_link: false)
|
12
|
+
if force_link || @category != category
|
13
|
+
link_to category.name, comable.products_path(category_id: category.id)
|
14
|
+
else
|
15
|
+
category.name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def sku_table(product, options = nil)
|
20
|
+
stocks = product.stocks
|
21
|
+
content_tag(:table, nil, options) do
|
22
|
+
html = ''
|
23
|
+
html << build_sku_v_table_header(product, stocks)
|
24
|
+
html << build_sku_table_rows(product, stocks)
|
25
|
+
html.html_safe
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def build_sku_v_table_header(product, stocks)
|
32
|
+
sku_item_name = product.sku_h_item_name
|
33
|
+
sku_item_name += '/' + product.sku_v_item_name if product.sku_v?
|
34
|
+
|
35
|
+
html = ''
|
36
|
+
html << content_tag(:th, sku_item_name)
|
37
|
+
stocks.group_by(&:sku_h_choice_name).keys.each do |sku_h_choice_name|
|
38
|
+
html << content_tag(:th, sku_h_choice_name)
|
39
|
+
end
|
40
|
+
html.html_safe
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_sku_table_rows(product, stocks)
|
44
|
+
return content_tag(:tr, build_sku_table_row(stocks)) unless product.sku_v?
|
45
|
+
|
46
|
+
html = ''
|
47
|
+
stocks_groups_by_sku_v(stocks).each_pair do |sku_v_choice_name, sku_v_stocks|
|
48
|
+
html << content_tag(:tr, build_sku_table_row(sku_v_stocks, sku_v_choice_name))
|
49
|
+
end
|
50
|
+
html.html_safe
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_sku_table_row(stocks, sku_v_choice_name = nil)
|
54
|
+
html = ''
|
55
|
+
html << content_tag(:th, sku_v_choice_name)
|
56
|
+
html << stocks.map { |stock| content_tag(:td, build_sku_product_label(stock)) }.join
|
57
|
+
html.html_safe
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_sku_product_label(stock)
|
61
|
+
return unless stock
|
62
|
+
content_tag(:div, class: 'radio') do
|
63
|
+
content_tag(:label) do
|
64
|
+
html = ''
|
65
|
+
html << radio_button_tag(:stock_id, stock.id, false, disabled: stock.unstocked?)
|
66
|
+
html << stock.code
|
67
|
+
html.html_safe
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def stocks_groups_by_sku_v(stocks)
|
73
|
+
sku_h_choice_names = stocks.group_by(&:sku_h_choice_name).keys
|
74
|
+
|
75
|
+
stocks.group_by(&:sku_v_choice_name).each_with_object({}) do |(sku_v_choice_name, sku_v_stocks), group|
|
76
|
+
group[sku_v_choice_name] = sku_h_choice_names.map do |sku_h_choice_name|
|
77
|
+
sku_v_stocks.find { |s| s.sku_h_choice_name == sku_h_choice_name }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Comable
|
2
|
+
class OrderMailer < ActionMailer::Base
|
3
|
+
include Comable::ApplicationHelper
|
4
|
+
helper Comable::ApplicationHelper
|
5
|
+
helper_method :subject_for
|
6
|
+
|
7
|
+
def complete(order)
|
8
|
+
@order = order
|
9
|
+
mail(from: current_store.email, to: order.email, subject: subject_for(order))
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def subject_for(order)
|
15
|
+
Comable.t('order_mailer.complete.subject', store_name: current_store.name, order_code: order.code)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Comable
|
2
|
+
class Ability
|
3
|
+
include CanCan::Ability
|
4
|
+
|
5
|
+
def initialize(user)
|
6
|
+
user ||= Comable::User.new # guest user (not logged in)
|
7
|
+
|
8
|
+
case user.role.to_sym
|
9
|
+
when :admin
|
10
|
+
can :manage, :all
|
11
|
+
when :reporter
|
12
|
+
can :read, :all
|
13
|
+
else
|
14
|
+
fail CanCan::AccessDenied
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Comable
|
2
|
+
class Address < ActiveRecord::Base
|
3
|
+
belongs_to :user, class_name: Comable::User.name, autosave: false
|
4
|
+
|
5
|
+
validates :family_name, presence: true, length: { maximum: 255 }
|
6
|
+
validates :first_name, presence: true, length: { maximum: 255 }
|
7
|
+
validates :zip_code, presence: true, length: { maximum: 255 }
|
8
|
+
validates :state_name, presence: true, length: { maximum: 255 }
|
9
|
+
validates :city, presence: true, length: { maximum: 255 }
|
10
|
+
validates :detail, length: { maximum: 255 }
|
11
|
+
validates :phone_number, length: { maximum: 255 }
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def find_or_clone(address)
|
15
|
+
all.to_a.find { |obj| obj.same_as? address } || address.clone
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def same_as?(address)
|
20
|
+
contents == address.contents
|
21
|
+
end
|
22
|
+
|
23
|
+
def clone
|
24
|
+
self.class.new(contents)
|
25
|
+
end
|
26
|
+
|
27
|
+
def contents
|
28
|
+
attributes.except('id', 'user_id', 'created_at', 'updated_at')
|
29
|
+
end
|
30
|
+
|
31
|
+
def full_name
|
32
|
+
"#{family_name} #{first_name}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def full_address
|
36
|
+
"#{state_name} #{city} #{detail}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Comable
|
2
|
+
class Category < ActiveRecord::Base
|
3
|
+
has_and_belongs_to_many :products, class_name: Comable::Product.name, join_table: :comable_products_categories
|
4
|
+
has_ancestry
|
5
|
+
acts_as_list scope: [:ancestry]
|
6
|
+
|
7
|
+
default_scope -> { order('position ASC') }
|
8
|
+
|
9
|
+
DEFAULT_PATH_NAME_DELIMITER = ' > '
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def path_names(delimiter: DEFAULT_PATH_NAME_DELIMITER)
|
13
|
+
categoris.path(&:name).join(delimiter)
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_by_path_names(path_names, delimiter: DEFAULT_PATH_NAME_DELIMITER)
|
17
|
+
path_names.map do |path_name|
|
18
|
+
find_by_path_name(path_name, delimiter: delimiter)
|
19
|
+
end.compact
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_by_path_name(path_name, root: nil, delimiter: DEFAULT_PATH_NAME_DELIMITER)
|
23
|
+
names = path_name.split(delimiter)
|
24
|
+
names.inject(root) do |category, name|
|
25
|
+
(category ? category.children : roots).find_by(name: name) || break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_jstree(options = {})
|
30
|
+
build_to_jstree(arrange_serializable(order: :position), options).to_json
|
31
|
+
end
|
32
|
+
|
33
|
+
def from_jstree!(jstree_json)
|
34
|
+
jstree = JSON.parse(jstree_json)
|
35
|
+
|
36
|
+
transaction do
|
37
|
+
restore_from_jstree!(jstree)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def build_to_jstree(serialized_categories, options = {})
|
44
|
+
serialized_categories.map do |serialized_category|
|
45
|
+
options.merge(
|
46
|
+
id: serialized_category['id'],
|
47
|
+
text: serialized_category['name'],
|
48
|
+
children: build_to_jstree(serialized_category['children'], options)
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def restore_from_jstree!(jstree, parent = nil)
|
54
|
+
return unless jstree
|
55
|
+
|
56
|
+
jstree.each.with_index(1) do |node, index|
|
57
|
+
return find(node['_destroy']).destroy! if node['_destroy'].present?
|
58
|
+
|
59
|
+
restore_from_jstree_node!(node, parent, position: index)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def restore_from_jstree_node!(node, parent, default_attributes = {})
|
64
|
+
attributes = default_attributes.merge(parent: parent, name: node['text'])
|
65
|
+
category = node['id'].to_i.zero? ? new : find(node['id'])
|
66
|
+
category.update_attributes!(attributes)
|
67
|
+
|
68
|
+
restore_from_jstree!(node['children'], category)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|