comable-core 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +97 -0
  4. data/app/assets/javascripts/comable/application.js +13 -0
  5. data/app/assets/stylesheets/comable/application.css +13 -0
  6. data/app/controllers/concerns/comable/permitted_attributes.rb +15 -0
  7. data/app/helpers/comable/application_helper.rb +84 -0
  8. data/app/helpers/comable/products_helper.rb +82 -0
  9. data/app/mailers/comable/order_mailer.rb +18 -0
  10. data/app/models/comable/ability.rb +18 -0
  11. data/app/models/comable/address.rb +39 -0
  12. data/app/models/comable/category.rb +72 -0
  13. data/app/models/comable/image.rb +15 -0
  14. data/app/models/comable/order.rb +121 -0
  15. data/app/models/comable/order/associations.rb +22 -0
  16. data/app/models/comable/order/callbacks.rb +43 -0
  17. data/app/models/comable/order/morrisable.rb +20 -0
  18. data/app/models/comable/order/scopes.rb +17 -0
  19. data/app/models/comable/order/validations.rb +39 -0
  20. data/app/models/comable/order_item.rb +98 -0
  21. data/app/models/comable/order_item/csvable.rb +32 -0
  22. data/app/models/comable/page.rb +30 -0
  23. data/app/models/comable/payment.rb +87 -0
  24. data/app/models/comable/payment_method.rb +27 -0
  25. data/app/models/comable/product.rb +55 -0
  26. data/app/models/comable/product/csvable.rb +20 -0
  27. data/app/models/comable/shipment.rb +85 -0
  28. data/app/models/comable/shipment_method.rb +9 -0
  29. data/app/models/comable/stock.rb +71 -0
  30. data/app/models/comable/stock/csvable.rb +26 -0
  31. data/app/models/comable/store.rb +30 -0
  32. data/app/models/comable/theme.rb +25 -0
  33. data/app/models/comable/tracker.rb +17 -0
  34. data/app/models/comable/user.rb +130 -0
  35. data/app/models/concerns/comable/cart_owner.rb +94 -0
  36. data/app/models/concerns/comable/checkout.rb +85 -0
  37. data/app/models/concerns/comable/importable.rb +67 -0
  38. data/app/models/concerns/comable/liquidable.rb +12 -0
  39. data/app/models/concerns/comable/product/search.rb +41 -0
  40. data/app/models/concerns/comable/ransackable.rb +38 -0
  41. data/app/models/concerns/comable/role_owner.rb +15 -0
  42. data/app/models/concerns/comable/sku_choice.rb +19 -0
  43. data/app/models/concerns/comable/sku_item.rb +17 -0
  44. data/app/uploaders/image_uploader.rb +7 -0
  45. data/app/views/comable/order_mailer/complete.text.erb +42 -0
  46. data/config/initializers/comma.rb +8 -0
  47. data/config/locales/en.yml +424 -0
  48. data/config/locales/ja.yml +425 -0
  49. data/db/migrate/20131214194807_create_comable_products.rb +15 -0
  50. data/db/migrate/20140120032559_create_comable_users.rb +46 -0
  51. data/db/migrate/20140502060116_create_comable_stocks.rb +14 -0
  52. data/db/migrate/20140723175431_create_comable_orders.rb +20 -0
  53. data/db/migrate/20140723175810_create_comable_order_items.rb +19 -0
  54. data/db/migrate/20140817194104_create_comable_payment_methods.rb +13 -0
  55. data/db/migrate/20140921191416_create_comable_shipment_methods.rb +11 -0
  56. data/db/migrate/20140926063541_create_comable_stores.rb +11 -0
  57. data/db/migrate/20141024025526_create_comable_addresses.rb +17 -0
  58. data/db/migrate/20150111031228_create_comable_categories.rb +10 -0
  59. data/db/migrate/20150111031229_create_comable_products_categories.rb +8 -0
  60. data/db/migrate/20150112173706_create_comable_images.rb +9 -0
  61. data/db/migrate/20150423095210_create_comable_shipments.rb +13 -0
  62. data/db/migrate/20150511171940_create_comable_payments.rb +12 -0
  63. data/db/migrate/20150513185230_create_comable_trackers.rb +12 -0
  64. data/db/migrate/20150519080729_create_comable_pages.rb +17 -0
  65. data/db/migrate/20150612143226_create_comable_themes.rb +15 -0
  66. data/db/migrate/20150612143445_add_theme_id_to_comable_stores.rb +7 -0
  67. data/db/seeds.rb +5 -0
  68. data/db/seeds/comable/users.rb +51 -0
  69. data/lib/comable/core.rb +48 -0
  70. data/lib/comable/core/configuration.rb +22 -0
  71. data/lib/comable/core/engine.rb +50 -0
  72. data/lib/comable/deprecator.rb +26 -0
  73. data/lib/comable/payment_provider.rb +14 -0
  74. data/lib/comable/payment_provider/base.rb +42 -0
  75. data/lib/comable/payment_provider/general.rb +15 -0
  76. data/lib/comable/state_machine_patch.rb +32 -0
  77. data/lib/comma_extractor_extentions.rb +31 -0
  78. data/lib/generators/comable/install/install_generator.rb +133 -0
  79. data/lib/generators/comable/install/templates/config/initializers/comable.rb +31 -0
  80. data/lib/tasks/comable_tasks.rake +4 -0
  81. 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,15 @@
1
+ module Comable
2
+ module PermittedAttributes
3
+ def permitted_address_attributes
4
+ [
5
+ :family_name,
6
+ :first_name,
7
+ :zip_code,
8
+ :state_name,
9
+ :city,
10
+ :detail,
11
+ :phone_number
12
+ ]
13
+ end
14
+ end
15
+ end
@@ -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