comable-core 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|