Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Port @jstirk pull request to apply to the latest namespace updates. […

…fixes #9]
  • Loading branch information...
commit 0d43d9f77d6654c8fbaca587d8e0c54fb9824b2c 1 parent ae12c0c
Jeff Dutil JDutil authored
6 .gitignore
... ... @@ -1,4 +1,6 @@
1   -.DS_Store
2 1 *.swp
  2 +.DS_Store
  3 +.rvmrc
  4 +Gemfile.lock
3 5 pkg
4   -
  6 +spec/dummy
4 .rspec
... ... @@ -0,0 +1,4 @@
  1 +--color
  2 +--format=nested
  3 +--backtrace
  4 +--profile
11 Gemfile
... ... @@ -0,0 +1,11 @@
  1 +source 'http://rubygems.org'
  2 +
  3 +# TODO: remove when 0.80.0.beta or higher is released.
  4 +gem 'spree', :git => 'git://github.com/spree/spree.git'
  5 +
  6 +group :test do
  7 + gem 'faker'
  8 + gem 'factory_girl'
  9 +end
  10 +
  11 +gemspec
11 README.md
Source Rendered
@@ -51,3 +51,14 @@ Run:
51 51
52 52 $ bundle
53 53 $ bundle exec rails g spree_related_products:install
  54 +
  55 +Development
  56 +-----------
  57 +
  58 + * Fork the repo
  59 + * clone your repo
  60 + * Run `bundle`
  61 + * Run `bundle exec rake test_app` to create the test application in `spec/test_app`.
  62 + * Make your changes.
  63 + * Ensure specs pass by running `bundle exec rake`
  64 + * Submit your pull request
29 Rakefile
... ... @@ -1,18 +1,17 @@
1   -require 'rubygems'
2   -require 'rake'
3   -require 'rake/testtask'
4   -require 'rake/packagetask'
5   -require 'rake/gempackagetask'
  1 +require 'bundler'
  2 +Bundler::GemHelper.install_tasks
  3 +Bundler.setup
6 4
7   -spec = eval(File.read('spree_related_products.gemspec'))
  5 +require 'rspec/core/rake_task'
  6 +RSpec::Core::RakeTask.new
8 7
9   -Rake::GemPackageTask.new(spec) do |p|
10   - p.gem_spec = spec
11   -end
  8 +require 'spree/core/testing_support/common_rake'
  9 +
  10 +desc "Default Task"
  11 +task :default => [:spec]
12 12
13   -desc "Release to gemcutter"
14   -task :release => :package do
15   - require 'rake/gemcutter'
16   - Rake::Gemcutter::Tasks.new(spec).define
17   - Rake::Task['gem:push'].invoke
18   -end
  13 +desc "Generates a dummy app for testing"
  14 +task :test_app do
  15 + ENV['LIB_NAME'] = 'spree_related_products'
  16 + Rake::Task['common:test_app'].invoke
  17 +end
98 app/models/spree/calculator/related_product_discount.rb
... ... @@ -1,52 +1,50 @@
59 app/models/spree/product_decorator.rb
... ... @@ -1,23 +1,72 @@
1 1 Spree::Product.class_eval do
2 2 has_many :relations, :as => :relatable
3 3
  4 + # Returns all the Spree::RelationType's which apply_to this class.
4 5 def self.relation_types
5 6 Spree::RelationType.find_all_by_applies_to(self.to_s, :order => :name)
6 7 end
7 8
8   - def method_missing(method, *args)
9   - relation_type = self.class.relation_types.detect { |rt| rt.name.downcase.gsub(" ", "_").pluralize == method.to_s.downcase }
  9 + # The AREL Relations that will be used to filter the resultant items.
  10 + #
  11 + # By default this will remove any items which are deleted, or not yet available.
  12 + #
  13 + # You can override this method to fine tune the filter. For example,
  14 + # to only return Spree::Product's with more than 2 items in stock, you could
  15 + # do the following:
  16 + #
  17 + # def self.relation_filter
  18 + # set = super
  19 + # set.where('spree_products.count_on_hand >= 2')
  20 + # end
  21 + #
  22 + # This could also feasibly be overridden to sort the result in a
  23 + # particular order, or restrict the number of items returned.
  24 + def self.relation_filter
  25 + where('spree_products.deleted_at' => nil).where('spree_products.available_on IS NOT NULL').where('spree_products.available_on <= ?', Time.now)
  26 + end
10 27
  28 + # Decides if there is a relevant Spree::RelationType related to this class
  29 + # which should be returned for this method.
  30 + #
  31 + # If so, it calls relations_for_relation_type. Otherwise it passes
  32 + # it up the inheritance chain.
  33 + def method_missing(method, *args)
11 34 # Fix for Ruby 1.9
12 35 raise NoMethodError if method == :to_ary
13 36
  37 + relation_type = self.class.relation_types.detect { |rt| rt.name.downcase.gsub(" ", "_").pluralize == method.to_s.downcase }
  38 +
14 39 if relation_type.nil?
15 40 super
16 41 else
17   - relations.find_all_by_relation_type_id(relation_type.id).map(&:related_to).select do |product|
18   - product.deleted_at.nil? && product.available_on && product.available_on <= Time.now()
19   - end
  42 + relations_for_relation_type(relation_type)
20 43 end
  44 + end
21 45
  46 + private
  47 +
  48 + # Returns all the Products that are related to this record for the given RelationType.
  49 + #
  50 + # Uses the Relations to find all the related items, and then filters
  51 + # them using +Product.relation_filter+ to remove unwanted items.
  52 + def relations_for_relation_type(relation_type)
  53 + # Find all the relations that belong to us for this RelationType
  54 + related_ids = relations.where(:relation_type_id => relation_type.id).select(:related_to_id).collect(&:related_to_id)
  55 +
  56 + # Construct a query for all these records
  57 + result = self.class.where(:id => related_ids)
  58 +
  59 + # Merge in the relation_filter if it's available
  60 + result = result.merge(self.class.relation_filter.scoped) if relation_filter
  61 +
  62 + result
22 63 end
  64 +
  65 + # Simple accessor for the class-level relation_filter.
  66 + # Could feasibly be overloaded to filter results relative to this
  67 + # record (eg. only higher priced items)
  68 + def relation_filter
  69 + self.class.relation_filter
  70 + end
  71 +
23 72 end
2  lib/spree_related_products.rb
@@ -10,7 +10,6 @@ def self.activate
@@ -18,4 +17,3 @@ def self.activate
123 spec/models/spree/product_spec.rb
... ... @@ -0,0 +1,123 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Spree::Product do
  4 +
  5 + context "class" do
  6 + describe ".relation_types" do
  7 + it "should return all the RelationTypes in use for this Product" do
  8 + relation_type = Spree::RelationType.create!(:name => "Related Products", :applies_to => "Spree::Product")
  9 + Spree::Product.relation_types.should include(relation_type)
  10 + end
  11 + end
  12 + end
  13 +
  14 + context "instance" do
  15 + before(:each) do
  16 + @product = Factory(:product)
  17 + @relation_type = Spree::RelationType.create(:name => "Related Products", :applies_to => "Spree::Product")
  18 + end
  19 +
  20 + describe ".relations" do
  21 + it "has many relations" do
  22 + @product.save!
  23 + other1 = Factory(:product)
  24 + other2 = Factory(:product)
  25 +
  26 + relation1 = Spree::Relation.create!(:relatable => @product, :related_to => other1, :relation_type => @relation_type)
  27 + relation2 = Spree::Relation.create!(:relatable => @product, :related_to => other2, :relation_type => @relation_type)
  28 +
  29 + @product.reload
  30 + @product.relations.should include(relation1)
  31 + @product.relations.should include(relation2)
  32 + end
  33 +
  34 + it "has many relations for different RelationTypes" do
  35 + @product.save!
  36 + other = Factory(:product)#valid_product!
  37 +
  38 + other_relation_type = Spree::RelationType.new(:name => "Recommended Products")
  39 +
  40 + relation1 = Spree::Relation.create!(:relatable => @product, :related_to => other, :relation_type => @relation_type)
  41 + relation2 = Spree::Relation.create!(:relatable => @product, :related_to => other, :relation_type => other_relation_type)
  42 +
  43 + @product.reload
  44 + @product.relations.should include(relation1)
  45 + @product.relations.should include(relation2)
  46 + end
  47 + end
  48 +
  49 + describe "RelationType finders" do
  50 + before(:each) do
  51 + @product.save!
  52 + @other = Factory(:product)
  53 + @relation = Spree::Relation.create!(:relatable => @product, :related_to => @other, :relation_type => @relation_type)
  54 + @product.reload
  55 + end
  56 +
  57 + it "should return the relevant relations" do
  58 + @product.related_products.should include(@other)
  59 + end
  60 +
  61 + it "should be the pluralised form of the RelationType name" do
  62 + @relation_type.update_attributes(:name => 'Related Product')
  63 + @product.related_products.should include(@other)
  64 + end
  65 +
  66 + it "should not return relations for another RelationType" do
  67 + @product.save!
  68 + other2 = Factory(:product)
  69 +
  70 + other_relation_type = Spree::RelationType.new(:name => "Recommended Products")
  71 +
  72 + relation1 = Spree::Relation.create!(:relatable => @product, :related_to => @other, :relation_type => @relation_type)
  73 + relation2 = Spree::Relation.create!(:relatable => @product, :related_to => other2, :relation_type => other_relation_type)
  74 +
  75 + @product.reload
  76 + @product.related_products.should include(@other)
  77 + @product.related_products.should_not include(other2)
  78 + end
  79 +
  80 + it "should not return Products that are deleted" do
  81 + @other.update_attributes(:deleted_at => Time.now)
  82 +
  83 + @product.related_products.should be_blank
  84 + end
  85 +
  86 + it "should not return Products that are not yet available" do
  87 + @other.update_attributes(:available_on => Time.now + 1.hour)
  88 +
  89 + @product.related_products.should be_blank
  90 + end
  91 +
  92 + it "should not return Products where available_on are blank" do
  93 + @other.update_attributes(:available_on => nil)
  94 +
  95 + @product.related_products.should be_blank
  96 + end
  97 +
  98 + it "should return all results if .relation_filter is nil" do
  99 + Spree::Product.should_receive(:relation_filter).and_return(nil)
  100 + @other.update_attributes(:available_on => Time.now + 1.hour)
  101 +
  102 + @product.related_products.should include(@other)
  103 + end
  104 +
  105 + context "with an enhanced Product.relation_filter" do
  106 + it "should restrict the filter" do
  107 + relation_filter = Spree::Product.relation_filter
  108 + Spree::Product.should_receive(:relation_filter).at_least(:once).and_return(relation_filter.includes(:master).where('spree_variants.count_on_hand > 2'))
  109 +
  110 + @other.master.update_attributes(:count_on_hand => 1)
  111 +
  112 + other2 = Factory(:product)
  113 + other2.master.update_attributes(:count_on_hand => 3)
  114 + relation = Spree::Relation.create!(:relatable => @product, :related_to => other2, :relation_type => @relation_type)
  115 +
  116 + results = @product.related_products
  117 + results.should_not include(@other)
  118 + results.should include(other2)
  119 + end
  120 + end
  121 + end
  122 + end
  123 +end
18 spec/spec_helper.rb
... ... @@ -0,0 +1,18 @@
  1 +# Configure Rails Environment
  2 +ENV["RAILS_ENV"] = "test"
  3 +require File.expand_path("../dummy/config/environment.rb", __FILE__)
  4 +require 'rspec/rails'
  5 +
  6 +# Requires supporting ruby files with custom matchers and macros, etc,
  7 +# in spec/support/ and its subdirectories.
  8 +Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
  9 +
  10 +# Requires factories defined in spree_core
  11 +require 'spree/core/testing_support/factories'
  12 +
  13 +RSpec.configure do |config|
  14 + # If you're not using ActiveRecord, or you'd prefer not to run each of your
  15 + # examples within a transaction, remove the following line or assign false
  16 + # instead of true.
  17 + config.use_transactional_fixtures = true
  18 +end
3  spree.txt
... ... @@ -1,3 +0,0 @@
1   -0.50.x :branch => '0-50-stable'
2   -# 0.40.x :ref => '251f6c20fee3d2f07d93'
3   -# 0.30.x :tag => 'v0.30.x'
5 spree_related_products.gemspec
@@ -17,5 +17,8 @@ Gem::Specification.new do |s|
12 test/functional/admin/relations_controller_test.rb
... ... @@ -1,12 +0,0 @@
1   -require File.dirname(__FILE__) + '/../../test_helper'
2   -
3   -# Re-raise errors caught by the controller.
4   -Spree::Admin::RelationsController.class_eval { def rescue_action(e) raise e end }
5   -
6   -class Spree::Admin::RelationsControllerTest < ActionController::TestCase
7   -
8   - # Replace this with your real tests.
9   - def test_truth
10   - assert true
11   - end
12   -end
0  test/unit/helpers/admin/relations_helper_test.rb
No changes.

0 comments on commit 0d43d9f

Please sign in to comment.
Something went wrong with that request. Please try again.