piggybak_variants 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +16 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/piggybak_variants/piggybak_variants.js +32 -0
  5. data/app/assets/stylesheets/piggybak_variants/application.css +13 -0
  6. data/app/controllers/piggybak_variants/application_controller.rb +4 -0
  7. data/app/helpers/piggybak_variants_helper.rb +17 -0
  8. data/app/models/piggybak_variants/option.rb +17 -0
  9. data/app/models/piggybak_variants/option_configuration.rb +18 -0
  10. data/app/models/piggybak_variants/option_value.rb +15 -0
  11. data/app/models/piggybak_variants/variant.rb +36 -0
  12. data/app/views/piggybak_variants/cart/_form.html.erb +29 -0
  13. data/app/views/rails_admin/main/_option_values.html.haml +12 -0
  14. data/config/routes.rb +2 -0
  15. data/db/migrate/20121015144538_create_new_variants.rb +13 -0
  16. data/db/migrate/20121016150359_create_options.rb +13 -0
  17. data/db/migrate/20121016153016_create_option_values.rb +14 -0
  18. data/db/migrate/20121016161156_create_option_values_variants.rb +12 -0
  19. data/db/migrate/20121022205428_create_option_configurations.rb +13 -0
  20. data/lib/acts_as_sellable_with_variants.rb +18 -0
  21. data/lib/piggybak_variants.rb +5 -0
  22. data/lib/piggybak_variants/engine.rb +81 -0
  23. data/lib/piggybak_variants/version.rb +3 -0
  24. data/test/dummy/README.rdoc +261 -0
  25. data/test/dummy/Rakefile +7 -0
  26. data/test/dummy/app/assets/javascripts/application.js +15 -0
  27. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  28. data/test/dummy/app/controllers/application_controller.rb +3 -0
  29. data/test/dummy/app/helpers/application_helper.rb +2 -0
  30. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  31. data/test/dummy/config.ru +4 -0
  32. data/test/dummy/config/application.rb +56 -0
  33. data/test/dummy/config/boot.rb +10 -0
  34. data/test/dummy/config/database.yml +25 -0
  35. data/test/dummy/config/environment.rb +5 -0
  36. data/test/dummy/config/environments/development.rb +37 -0
  37. data/test/dummy/config/environments/production.rb +67 -0
  38. data/test/dummy/config/environments/test.rb +37 -0
  39. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  40. data/test/dummy/config/initializers/inflections.rb +15 -0
  41. data/test/dummy/config/initializers/mime_types.rb +5 -0
  42. data/test/dummy/config/initializers/secret_token.rb +7 -0
  43. data/test/dummy/config/initializers/session_store.rb +8 -0
  44. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  45. data/test/dummy/config/locales/en.yml +5 -0
  46. data/test/dummy/config/routes.rb +4 -0
  47. data/test/dummy/public/404.html +26 -0
  48. data/test/dummy/public/422.html +26 -0
  49. data/test/dummy/public/500.html +25 -0
  50. data/test/dummy/public/favicon.ico +0 -0
  51. data/test/dummy/script/rails +6 -0
  52. data/test/integration/navigation_test.rb +10 -0
  53. data/test/piggybak_variants_test.rb +7 -0
  54. data/test/test_helper.rb +15 -0
  55. metadata +152 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
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,16 @@
1
+ PiggybakVariants Gem (Engine)
2
+ ========
3
+
4
+ Advanced variant support for Piggybak.
5
+
6
+
7
+ TODO
8
+ ========
9
+
10
+ * Notes specified in variant.rb
11
+
12
+
13
+ Copyright
14
+ ========
15
+
16
+ Copyright (c) 2012 End Point. See LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'PiggybakVariants'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,32 @@
1
+ $(function() {
2
+ $('.variant_options input[type=radio]').click(function() { piggybak_variants.toggle_variant_form(); });
3
+ piggybak_variants.toggle_variant_form();
4
+ });
5
+
6
+ var piggybak_variants = {
7
+ toggle_variant_form: function() {
8
+ $('.unavailable, .all_selected').hide();
9
+ var all_selected = true;
10
+ var selected = new Array();
11
+ $.each($('.option'), function(i, j) {
12
+ var option_id = $(j).attr('id');
13
+ if($('input[name=' + option_id + ']:checked').length) {
14
+ selected.push($('input[name=' + option_id + ']:checked').val());
15
+ } else {
16
+ all_selected = false;
17
+ }
18
+ });
19
+ var selected_key = selected.join('_');
20
+ if(all_selected && variant_map[selected_key]) {
21
+ $('.variant_options form').show();
22
+ $('#sellable_id').val(variant_map[selected_key].id);
23
+ $('#variant_price span').html(variant_map[selected_key].price);
24
+ } else if(all_selected) { // if all selected and not available
25
+ $('.variant_options form').hide();
26
+ $('.unavailable').show();
27
+ } else { // if not all selected
28
+ $('.all_selected').show();
29
+ $('.variant_options form').hide();
30
+ }
31
+ }
32
+ };
@@ -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,4 @@
1
+ module PiggybakVariants
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ module PiggybakVariantsHelper
2
+ def variant_cart_form(object)
3
+ render "piggybak_variants/cart/form", :object => object
4
+ end
5
+
6
+ def options_for_klass(klass)
7
+ options = ::PiggybakVariants::OptionConfiguration.find_all_by_klass(klass).collect { |oc| oc.option }
8
+ end
9
+
10
+ def variant_map(object)
11
+ map = {}
12
+ object.variants.available.each do |variant|
13
+ map[variant.option_values.collect { |ov| ov.id }.join('_')] = { :id => variant.piggybak_sellable.id, :price => number_to_currency(variant.piggybak_sellable.price) }
14
+ end
15
+ map.to_json
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module PiggybakVariants
2
+ class Option < ActiveRecord::Base
3
+ self.table_name = "options"
4
+
5
+ attr_accessible :name, :position
6
+ has_many :option_values
7
+ has_many :option_configurations
8
+
9
+ default_scope :order => "position ASC"
10
+
11
+ accepts_nested_attributes_for :option_values, :allow_destroy => true
12
+ attr_accessible :option_values_attributes, :allow_destroy => true
13
+
14
+ accepts_nested_attributes_for :option_configurations, :allow_destroy => true
15
+ attr_accessible :option_configurations_attributes, :allow_destroy => true
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module PiggybakVariants
2
+ class OptionConfiguration < ActiveRecord::Base
3
+ self.table_name = "option_configurations"
4
+
5
+ VARIANT_CLASSES = []
6
+
7
+ attr_accessible :klass, :option_id
8
+ belongs_to :option
9
+
10
+ def klass_enum
11
+ VARIANT_CLASSES
12
+ end
13
+
14
+ def admin_label
15
+ self.klass
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module PiggybakVariants
2
+ class OptionValue < ActiveRecord::Base
3
+ self.table_name = "option_values"
4
+
5
+ attr_accessible :name, :position
6
+ has_and_belongs_to_many :variants
7
+ belongs_to :option, :inverse_of => :option_values
8
+
9
+ default_scope :order => "position ASC"
10
+
11
+ def admin_label
12
+ "#{self.option.name}: #{self.name}"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ module PiggybakVariants
2
+ class Variant < ActiveRecord::Base
3
+ self.table_name = "variants"
4
+
5
+ acts_as_sellable
6
+ belongs_to :item, :polymorphic => true
7
+ has_and_belongs_to_many :option_values
8
+
9
+ scope :available, joins(:piggybak_sellable).where("sellables.active IS TRUE AND (sellables.quantity > 0 OR sellables.unlimited_inventory IS TRUE)")
10
+ validate :option_value_validation
11
+
12
+ def admin_label
13
+ "#{self.piggybak_sellable.sku}: #{self.piggybak_sellable.price}"
14
+ end
15
+
16
+ def option_value_validation
17
+ # TODO: Figure out why this validation is only being called sometime
18
+ # TODO: Add verification to prevent duplicate option value sets
19
+
20
+ klass = self.item.class
21
+ options = ::PiggybakVariants::OptionConfiguration.find_all_by_klass(klass).collect { |oc| oc.option }
22
+ error = false
23
+ options.each do |option|
24
+ matching_option_values = self.option_values.select { |ov| ov.option == option }.size
25
+ if matching_option_values > 1
26
+ self.errors.add(:option_values, "You must have only option value for #{option.name}")
27
+ error = true
28
+ elsif matching_option_values < 1
29
+ self.errors.add(:option_values, "You must have one option value for #{option.name}")
30
+ error = true
31
+ end
32
+ end
33
+ self.errors.add(:base, "Option value errors") if error
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ <% if object.reflections.keys.include?(:variants) -%>
2
+ <%= javascript_include_tag "piggybak_variants/piggybak_variants" %>
3
+
4
+ <script type="text/javascript">
5
+ var variant_map = <%= raw variant_map(object) %>;
6
+ </script>
7
+
8
+ <div class="variant_options">
9
+ <% options_for_klass(object.class).each do |option| -%>
10
+ <div class="option" id="option_<%= option.id %>">
11
+ <h4><%= option.name %></h4>
12
+ <% option.option_values.each do |option_value| -%>
13
+ <input type="radio" name="option_<%= option.id %>" value="<%= option_value.id %>" />
14
+ <%= option_value.name %><br />
15
+ <% end -%>
16
+ <br />
17
+ </div>
18
+ <% end -%>
19
+ <span class="all_selected">You must select an choice from each option.</span>
20
+ <span class="unavailable">This combination is not available.</span>
21
+ <%= form_tag piggybak.cart_add_url do -%>
22
+ <input type="hidden" id="sellable_id" name="sellable_id" />
23
+ <div id="variant_price">Price: <span></span></div>
24
+ <label for="quantity">Quantity</label>
25
+ <%= text_field_tag :quantity, 1 %>
26
+ <%= submit_tag "Add to Cart", :id => "submit" %>
27
+ <% end -%>
28
+ </div>
29
+ <% end -%>
@@ -0,0 +1,12 @@
1
+ :ruby
2
+ column_type_dom_id = form.dom_id(field).sub(field.method_name.to_s, "")
3
+ attr = column_type_dom_id.gsub("frame_variants_attributes", '').gsub(/^_/, '[').gsub(/_$/, ']')
4
+
5
+ = #TODO: Add JavaScript to limit selectors here
6
+
7
+ - options_for_klass(@object.class).each do |option|
8
+ %h5= option.name
9
+ - option.option_values.each do |option_value|
10
+ %p
11
+ = check_box_tag "frame[variants_attributes]#{attr}[option_value_ids][]", option_value.id, form.object.option_values.include?(option_value), :style => "margin:-2px 5px 0px 0px;"
12
+ %span= option_value.name
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ PiggybakVariants::Engine.routes.draw do
2
+ end
@@ -0,0 +1,13 @@
1
+ class CreateNewVariants < ActiveRecord::Migration
2
+ def up
3
+ create_table :variants, :force => true do |t|
4
+ t.integer :item_id, :null => false
5
+ t.string :item_type, :null => false
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def down
11
+ drop_table :variants
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class CreateOptions < ActiveRecord::Migration
2
+ def up
3
+ create_table :options, :force => true do |t|
4
+ t.string :name
5
+ t.integer :position
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def down
11
+ drop_table :options
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ class CreateOptionValues < ActiveRecord::Migration
2
+ def up
3
+ create_table :option_values, :force => true do |t|
4
+ t.integer :option_id
5
+ t.string :name
6
+ t.integer :position
7
+ t.timestamps
8
+ end
9
+ end
10
+
11
+ def down
12
+ drop_table :options_values
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ class CreateOptionValuesVariants < ActiveRecord::Migration
2
+ def up
3
+ create_table :option_values_variants, :id => false do |t|
4
+ t.integer :option_value_id
5
+ t.integer :variant_id
6
+ end
7
+ end
8
+
9
+ def down
10
+ drop_table :option_values_variants
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ class CreateOptionConfigurations < ActiveRecord::Migration
2
+ def up
3
+ create_table :option_configurations, :force => true do |t|
4
+ t.string :klass, :null => false
5
+ t.references :option, :null => false
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def down
11
+ drop_table :option_assignments
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module Piggybak
2
+ module ActsAsSellableWithVariants
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_sellable_with_variants
7
+ has_many :variants, :dependent => :destroy, :class_name => "::PiggybakVariants::Variant", :as => :item
8
+
9
+ accepts_nested_attributes_for :variants, :allow_destroy => true
10
+ attr_accessible :variants_attributes #, :allow_destroy => true
11
+
12
+ ::PiggybakVariants::OptionConfiguration::VARIANT_CLASSES << self.to_s
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ ::ActiveRecord::Base.send :include, Piggybak::ActsAsSellableWithVariants
@@ -0,0 +1,5 @@
1
+ require "piggybak_variants/engine"
2
+ require "acts_as_sellable_with_variants"
3
+
4
+ module PiggybakVariants
5
+ end
@@ -0,0 +1,81 @@
1
+ module PiggybakVariants
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace PiggybakVariants
4
+
5
+ config.to_prepare do
6
+ ApplicationController.class_eval do
7
+ helper :piggybak_variants
8
+ end
9
+ RailsAdmin::ApplicationController.class_eval do
10
+ helper :piggybak_variants
11
+ end
12
+ end
13
+
14
+ initializer "piggybak_variants.precompile_hook" do |app|
15
+ app.config.assets.precompile += ['piggybak_variants/piggybak_variants.js']
16
+ end
17
+
18
+ initializer "piggybak.rails_admin_config" do |app|
19
+ RailsAdmin.config do |config|
20
+ config.model PiggybakVariants::OptionConfiguration do
21
+ visible false
22
+ object_label_method :admin_label
23
+ label "Classes with Option"
24
+ edit do
25
+ field :klass
26
+ end
27
+ end
28
+
29
+ config.model PiggybakVariants::OptionValue do
30
+ visible false
31
+ object_label_method :admin_label
32
+ edit do
33
+ field :name
34
+ field :position
35
+ end
36
+ end
37
+
38
+ config.model PiggybakVariants::Option do
39
+ navigation_label "Variants"
40
+ list do
41
+ field :name
42
+ field :position
43
+ field :option_values
44
+ field :option_configurations do
45
+ label "Classes with Option"
46
+ end
47
+ end
48
+ edit do
49
+ field :name
50
+ field :position
51
+ field :option_configurations do
52
+ label "Classes with Option"
53
+ active true
54
+ end
55
+ field :option_values do
56
+ active true
57
+ end
58
+ end
59
+ end
60
+
61
+ config.model PiggybakVariants::Variant do
62
+ object_label_method :admin_label
63
+ show do
64
+ field :option_values do
65
+ visible true
66
+ end
67
+ end
68
+ edit do
69
+ field :option_values do
70
+ partial "option_values"
71
+ help "An option value for each option must be selected"
72
+ end
73
+ field :piggybak_sellable do
74
+ active true
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end