Hot questions for Using Spree in product

Question:

I want to upload picture of a product master variant from code. I can create a simple product using

product = Spree::Product.new
product.name = something
product.available_on = Time.now
product.description = something
product.tax_category_id = 1
.......
.......
product.save

but when i try to upload product image using an image from my assets it gives me an error of path not found I followed spree documentation which says i can create an image using

img = Spree::Image.create(:attachment => File.open(path), :viewable => product.master)

but when i enter image_url "image name" in place of path. it gives an error that /assets/images/imagename no such file or directory

Please let me know if i am missing something very basic here

Thanks


Answer:

Instead of using image_url helper, you should manually build your path:

path = Rails.root + 'app/assets/images' + 'my_image_name.png'
Spree::Image.create(:attachment => File.open(path), :viewable => product.master)

Question:

I'm using spree version 2.3.1, and an error showing undefined method `permalink' for Spree::Product appears from code i was using in a smaller version of spree, can anyone point out in wish version spree stopped using this attribute


Answer:

From version 2.3.0 Spree started using friendly_id gem instead of custom permalink generation code. You can find the commit that switched to friendly_id here.

Question:

I have an Spree application with spree-multi-domain extension support. Here their are different store with different products assigned to it.(in Admin panel)

like for STORE 1 domain is store1.example.com and for STORE 2 -> store2.example.com

Here I have set the wildcard subdomains for multiple store

*.example.com

Okay now, when I call example.com/api/products.json?token=MY_TOKEN_ID,

I get complete list of products in JSON format. But Here, I have a issue while retrieving products for Store 1 and Store 2 through api call.

When I call products.json for the

Store 1 store1.example.com/api/products.json?token=MY_TOKEN_ID and for

Store 2 store2.example.com/api/products.json?token=MY_TOKEN_ID

then also I get the complete list of products when as usual like example.com/api/products.json?token=MY_TOKEN_ID

What I'm expecting here is when I call the GET request for products of a particular store then I should get the products of that particular store which was assigned in the admin panel.

So What should I do, couldn't understand.

Please help??


Answer:

the spree-multi-gem is not 100% stable and still under development.

you need to override the API and use current_store for each request.

A new ControllerHelpers::Store concern provides a current_store helper to fetch a helper based on the request’s domain.

just an example, non related to api

create a /app/controllers/spree/taxons_controller_decorator.rb and extend the TaxonsController. you need to class_eval it, otherwise you override the complete class!!

Spree::TaxonsController.class_eval do
   def show
    @taxon = Spree::Taxon.find_by_store_id_and_permalink!(current_store.id, params[:id])
    return unless @taxon

    @searcher = build_searcher(params.merge(:taxon => @taxon.id))
    @products = @searcher.retrieve_products
    @taxonomies = get_taxonomies
  end
end

so by that, every other function from the Spree::TaxonsController stays as it was, and just the show method was overridden

so for your case: this is the orignal file

https://github.com/spree/spree/blob/master/api/app/controllers/spree/api/v1/products_controller.rb

so you need to go into your rails app and have a /app/controllers/spree/api/v1/products_controller_decorator.rb where you go (i think that works)

Spree::Api::V1::ProductsController.class_eval do

end

but after reading that i think the best idea is to override https://github.com/spree/spree/blob/715d4439f4f02a1d75b8adac74b77dd445b61908/api/app/controllers/spree/api/base_controller.rb#L132

Line 132 the product_scope :-)

this should help you - if not you better go magento :P

cheers

Question:

With Spree Commerce 3.0-stable, I need to write a custom product filter to show only products where at least one variant matches the selected OptionValue.

I've got a filter that displays the correct list of options in the checkboxes, but selecting an option doesn't change which products are returned.

For this example, Products are available in multiple "Metal" options (platinum, white gold, yellow gold, silver, etc.). I've got the price range filter set up, and it's working correctly.

How can I get the products to filter by an Option?

My lib/spree/product_filters.rb

module Spree
  module Core
    module ProductFilters
      # Example: filtering by price
      #   The named scope just maps incoming labels onto their conditions, and builds the conjunction
      #   'price' is in the base scope's context (ie, "select foo from products where ...") so
      #     we can access the field right away
      #   The filter identifies which scope to use, then sets the conditions for each price range
      #
      # If user checks off three different price ranges then the argument passed to
      # below scope would be something like ["$10 - $15", "$15 - $18", "$18 - $20"]
      #
      Spree::Product.add_search_scope :price_range_any do |*opts|
        conds = opts.map {|o| Spree::Core::ProductFilters.price_filter[:conds][o]}.reject { |c| c.nil? }
        scope = conds.shift
        conds.each do |new_scope|
          scope = scope.or(new_scope)
        end
        Spree::Product.joins(master: :default_price).where(scope)
      end

      def ProductFilters.format_price(amount)
        Spree::Money.new(amount)
      end

      def ProductFilters.price_filter
        v = Spree::Price.arel_table
        conds = [ [ Spree.t(:under_price, price: format_price(1000))     , v[:amount].lteq(1000)],
                  [ "#{format_price(1000)} - #{format_price(1500)}"        , v[:amount].in(1000..1500)],
                  [ "#{format_price(1500)} - #{format_price(1800)}"        , v[:amount].in(1500..1800)],
                  [ "#{format_price(1800)} - #{format_price(2000)}"        , v[:amount].in(1800..2000)],
                  [ Spree.t(:or_over_price, price: format_price(2000)) , v[:amount].gteq(2000)]]
        {
          name:   Spree.t(:price_range),
          scope:  :price_range_any,
          conds:  Hash[*conds.flatten],
          labels: conds.map { |k,v| [k, k] }
        }
      end



      # Test for discrete option values selection
      def ProductFilters.option_with_values(option_scope, option, values)
        # get values IDs for Option with name {@option} and value-names in {@values} for use in SQL below
        option_values = Spree::OptionValue.where(:presentation => [values].flatten).joins(:option_type).where(OptionType.table_name => {:name => option}).pluck("#{OptionValue.table_name}.id")
        return option_scope if option_values.empty?

        option_scope = option_scope.where("#{Product.table_name}.id in (select product_id from #{Variant.table_name} v left join spree_option_values_variants ov on ov.variant_id = v.id where ov.option_value_id in (?))", option_values)
        option_scope
        puts option_scope.inspect
      end

      # multi-option scope
      Spree::Product.scope :option_any,
                         lambda { |*opts|
                           option_scope = Spree::Product.includes(:variants_including_master)
                           opts.map { |opt|
                             # opt is an array => ['option-name', [value1, value2, value3, ...]]
                             option_scope = option_with_values(option_scope, *opt)
                           }
                           option_scope
                         }

      # metal filter
      def ProductFilters.metal_filter
        metals = Spree::OptionValue.where( :option_type_id => Spree::OptionType.find_by!(name: "Metal") ).order("position").map(&:presentation).compact.uniq
        {
            :name => "Metal Type",
            :scope => :option_any,
            :conds => nil,
            :option => 'metal',
            :labels => metals.map { |k| [k, k] }
        }
      end

    end
  end
end

My app/views/spree/home/index.html.erb

<% content_for :sidebar do %>
  <div data-hook="homepage_sidebar_navigation">
    <%= render :partial => 'spree/shared/filters' %>
    <%= render :partial => 'spree/shared/taxonomies' %>
  </div>
<% end %>
<h2>Test!</h2>
<div data-hook="homepage_products">
  <% cache(cache_key_for_products) do %>
    <%= render :partial => 'spree/shared/products', :locals => { :products => @products } %>
  <% end %>
</div>

My app/views/spree/shared/_filters.html.erb

<% filters = [Spree::Core::ProductFilters.metal_filter,Spree::Core::ProductFilters.price_filter] %>

<% unless filters.empty? %>
  <%= form_tag '', :method => :get, :id => 'sidebar_products_search' do %>
    <%= hidden_field_tag 'per_page', params[:per_page] %>
    <% filters.each do |filter| %> <i><%= filter[:name] %> </i>
      <% labels = filter[:labels] || filter[:conds].map {|m,c| [m,m]} %>
      <% next if labels.empty? %>
      <div class="navigation" data-hook="navigation">
        <h4 class="filter-title"> <%= filter[:name] %> </h4>
        <ul class="list-group">
          <% labels.each do |nm,val| %>
            <% label = "#{filter[:name]}_#{nm}".gsub(/\s+/,'_') %>
            <li class="list-group-item">
              <input type="checkbox"
                     id="<%= label %>"
                     name="search[<%= filter[:scope].to_s %>][]"
                     value="<%= val %>"
                     <%= params[:search] && params[:search][filter[:scope]] && params[:search][filter[:scope]].include?(val.to_s) ? "checked" : "" %> />
              <label class="nowrap" for="<%= label %>"> <%= nm %> </label>
            </li>
          <% end %>
        </ul>
      </div>
    <% end %>
    <%= submit_tag Spree.t(:search), :name => nil, :class => 'btn btn-primary' %>
  <% end %>
<% end %>

Answer:

David Gross's answer above worked for me, although I am using the option of colour. Here's what my code looks like and the steps I took to get it to work.

1) Copy an unedited version of product_filters.rb to lib/product_filters.rb

2) Initialise it: in initializers/spree.rb, add in:

require 'product_filters'

# Spree.config do |config| etc........

3) Add this code to product_filters.rb:

  def ProductFilters.option_with_values(option_scope, option, values)
    # get values IDs for Option with name {@option} and value-names in {@values} for use in SQL below
    option_values = Spree::OptionValue.where(:presentation => [values].flatten).joins(:option_type).where(OptionType.table_name => {:name => option}).pluck("#{OptionValue.table_name}.id")
    return option_scope if option_values.empty?

    option_scope = option_scope.where("#{Product.table_name}.id in (select product_id from #{Variant.table_name} v left join spree_option_values_variants ov on ov.variant_id = v.id where ov.option_value_id in (?))", option_values)
    option_scope
  end

  # option scope
  Spree::Product.add_search_scope :option_any do |*opts|
    option_scope = Spree::Product.includes(:variants_including_master)
    option_type = ProductFilters.colour_filter[:option]

    opts.map { |opt|
      # opt is an array => ['option-name', [value1, value2, value3, ...]]
      option_scope = ProductFilters.option_with_values(option_scope, option_type, *opt)
     }
     option_scope
  end

  # colour option - object that describes the filter.
  def ProductFilters.colour_filter
    # Get an array of possible colours (option type of 'colour')
    # e.g. returns ["Gold", "Black", "White", "Silver", "Purple", "Multicoloured"]
    colours = Spree::OptionValue.where(:option_type_id => Spree::OptionType.find_by_name("colour")).order("position").map(&:presentation).compact.uniq
    {
        :name => "Colour",
        :scope => :option_any,
        :conds => nil,
        :option => 'colour', # this is MANDATORY
        :class => "colour",
        :labels => colours.map { |k| [k, k] }
    }
  end

4) Add your new filter to app/models/spree/taxons.rb so it appears on the front end:

def applicable_filters
  fs = []
  # fs << ProductFilters.taxons_below(self)
  ## unless it's a root taxon? left open for demo purposes

  fs << Spree::Core::ProductFilters.price_filter if Spree::Core::ProductFilters.respond_to?(:price_filter)
  fs << Spree::Core::ProductFilters.brand_filter if Spree::Core::ProductFilters.respond_to?(:brand_filter)
  fs << Spree::Core::ProductFilters.colour_filter if Spree::Core::ProductFilters.respond_to?(:colour_filter)
  fs
end

That should be it. I hope that helps - let me know if I can help further. Unfortunately the Spree filtering docs are nonexistent so we have to make do.

Question:

I'd like to change the "products" overview of my Spree 3.0.x shop to show only products that have one or more variants that can be supplied. This would be on top of the normal scopes (available, with price in current currency). It would also affect the taxonomy-listings.

I don't want to affect the search page, though: when searching, unavailable products should show up.

As far as I can see, there is no such scope, neither on Spree:Product nor on Spree::Product scopes.rb.

If there is no such scope, what would be the proper chain of joins- and includes to write this scope on Product?


Answer:

I'd probably start with something like:

Spree::Product.joins(
  variants_including_master: :stock_items
).group('spree_products.id').having("SUM(count_on_hand) > 0")

That should give you close to what you're looking for.

Question:

I created products and their variants. Variants have options size and color.

I'm trying to read all variants and their color names:

variants = product.variants_including_master.active(current_currency).includes([:option_values])

variants.each do |variant|
  # here I want to read variant options color and size
  # something like:  variant.option_values['color']
end

I've seen a lot of stuff on internet and can't get anything.


Answer:

I solved my situation with this:

      variants = product.variants_including_master.active(current_currency).includes([:option_values])

      variants.each do |variant|

        color = variant.option_values.select { |a| a.option_type.id == 2 }.first

        if not color.nil? then
          @product_colors << color[:name]
        end
      end

Question:

So i see this question has been asked before but the answer wasn't posted even though the question got answered, and since i dont have enough points to comment on the answer i need to ask it again. here is the original post

Is there a scope to allow me to show only products with variants with stock?

This is for spree 3.0. when i added the shown code

joins(
  variants_including_master: :stock_items
).group('spree_products.id').having("SUM(count_on_hand) > 0")

to my product decorator like such

def self.available(available_on = nil, currency = nil)
    # joins(:master => :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.now)      
    joins(:master => :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.now).joins(variants_including_master: :stock_items).group('spree_products.id').having("SUM(count_on_hand) > 0")  
end
search_scopes << :available

this blows up the view since the result has some strange properties due to the GROUP BY as far as i can tell. this errors out in the products_helper.rb file on the line

  max_updated_at = (@products.maximum(:updated_at) || Date.today).to_s(:number)

because @products.maximum(:updated_at) returns a hash

{[6, 6]=>2015-07-04 19:08:36 UTC, [3, 3]=>2015-07-04 19:07:37 UTC, [10, 10]=>2015-07-07 19:01:12 UTC}

instead of returning a date

2015-07-07 19:01:12 UTC

which it does normally.

this code also blows up on the taxon page with the following error:

PG::GroupingError: ERROR: column "spree_products_taxons.position" must appear in the GROUP BY clause or be used in an aggregate function LINE 1: SELECT DISTINCT "spree_products"."id", spree_products_taxon... ^

: SELECT DISTINCT "spree_products"."id", spree_products_taxons.position AS alias_0 FROM "spree_products" INNER JOIN "spree_variants" ON "spree_variants"."product_id" = "spree_products"."id" AND "spree_variants"."is_master" = 't' AND "spree_variants"."deleted_at" IS NULL INNER JOIN "spree_prices" ON "spree_prices"."variant_id" = "spree_variants"."id" AND "spree_prices"."deleted_at" IS NULL INNER JOIN "spree_variants" "variants_including_masters_spree_products" ON "variants_including_masters_spree_products"."product_id" = "spree_products"."id" AND "variants_including_masters_spree_products"."deleted_at" IS NULL INNER JOIN "spree_stock_items" ON "spree_stock_items"."variant_id" = "variants_including_masters_spree_products"."id" AND "spree_stock_items"."deleted_at" IS NULL LEFT OUTER JOIN "spree_products_taxons" ON "spree_products_taxons"."product_id" = "spree_products"."id" WHERE "spree_products"."deleted_at" IS NULL AND ("spree_products".deleted_at IS NULL or "spree_products".deleted_at >= '2015-07-09 14:30:53.668422') AND ("spree_products".available_on <= '2015-07-09 14:30:53.669089') AND "spree_products_taxons"."taxon_id" IN (2, 7, 14) AND (spree_prices.amount IS NOT NULL) AND "spree_prices"."currency" = 'USD' GROUP BY spree_products.id HAVING SUM(count_on_hand) > 0 ORDER BY spree_products_taxons.position ASC LIMIT 12 OFFSET 0

SO there must be a better way to do this so that the GROUP by clause doesn't throw everything off.


Answer:

Try something like this:

  joins(:master => :prices, variants: :stock_items).where("#{StockItem.quoted_table_name}.count_on_hand > 0 AND #{Product.quoted_table_name}.available_on <= ?", available_on || Time.now).uniq

You might want to include backorderable and track_inventory too - I don't know your setup, maybe you don't have such variants at all.

Edit: Added uniq. If you want master variants too then just change variants: :stock_itemspart to variants_including_master: :stock_items.

Question:

I want to add taxonomies from sidebar to main page, instead of list of products, but my deface is not working.

Deface::Override.new(
:virtual_path => 'spree/shared/_products',
:name => 'change view',
:replace => "[data-hook='homepage_products']",
:text => "
            <% max_level = Spree::Config[:max_level_in_taxons_menu] || 1 %>
            <nav id='taxonomies2' class='sidebar-item' data-hook>
                <% @taxonomies.each do |taxonomy| %>
                    <% cache [I18n.locale, taxonomy, max_level] do %>
                        <h4 class='taxonomy-root'><%= Spree.t(:shop_by_taxonomy, :taxonomy => taxonomy.name) %></h4>
                        <%= taxons_tree(taxonomy.root, @taxon, max_level) %>
                    <% end %>
                <% end %>
            </nav> ")

I am stuck at this really badly.


Answer:

You should replace the virtual path with the homepage index view one: spree/home/index, where the element you are trying to replace is placed.

Also, I'm not sure name should contain a space, try changing it with "change_view". Always be sure the name is unique and restart your server when adding a new deface override file.

Question:

On my spree store I need to find any product that has one or more tags in a list of tags. The tags in spree are done with the acts-as-taggable-on gem.

I have tried to do this with

Spree::Product.joins("spree_taggings").where("taggings.id IN (?)", list_of_tag_ids )

but it doesn't seem to work.

How would I be able to look up products by their tags?


Answer:

Search by ID is neccessary? If you could search by names, you could use the build in scope .tagged_with

Spree::Product.tagged_with(["awesome", "cool"], :match_all => true)

Docs: https://github.com/mbleigh/acts-as-taggable-on#finding-tagged-objects

If search by ID is really necessary, you could grab those first of course.

ActsAsTaggableOn::Tag.where("id IN (?)", list_of_tag_ids).pluck(:name)

Question:

i'm working on a Rails application with Spree Commerce.

I'm trying to display all products in a taxon cause i'm using them not as category but as some kind of registry.

So, i'm editing /app/views/spree/taxons/show.html.erb. Especifically this line:

<%= render partial: 'spree/shared/products', locals: { products: @products, taxon: @taxon } %>

Changing it for:

<%= render :partial => 'spree/shared/products', :locals => { :products => @products } %>

But it still says "No products found". But in my index they are showed.

So, How can I properly show all products?

Thanks in advance.

ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
Rails 4.2.5
Spree 3.0.4

As requested:

1) This is the show from products

<% @body_id = 'product-details' %>

<% cache [I18n.locale, current_currency, @product, @product.possible_promotions] do %>
  <div data-hook="product_show" itemscope itemtype="https://schema.org/Product">
    <div class="col-md-4" data-hook="product_left_part">
      <div data-hook="product_left_part_wrap">
        <div id="product-images" data-hook="product_images">
          <div id="main-image" class="panel panel-default" data-hook>
            <div class="panel-body text-center">
              <%= render :partial => 'image' %>
            </div>
          </div>
          <div id="thumbnails" data-hook>
            <%= render :partial => 'thumbnails' %>
          </div>
        </div>

        <div data-hook="product_properties">
          <%= render :partial => 'properties' %>
        </div>

        <div data-hook="promotions">
          <%= render :partial => 'promotions' %>
        </div>
      </div>
    </div>

    <div class="col-md-8" data-hook="product_right_part">
      <div data-hook="product_right_part_wrap">
        <div id="product-description" data-hook="product_description">
          <h1 class="product-title" itemprop="name"><%= @product.name %></h1>

          <div class="well" itemprop="description" data-hook="description">
            <%= product_description(@product) rescue Spree.t(:product_has_no_description) %>
          </div>

          <div id="cart-form" data-hook="cart_form">
            <%= render :partial => 'cart_form' %>
          </div>
        </div>

        <%= render :partial => 'taxons' %>
      </div>
    </div>
  </div>
<% end %>

2) Show from taxons

<h1 class="taxon-title"><%= @taxon.name %></h1>

<% content_for :sidebar do %>
  <div data-hook="taxon_sidebar_navigation">
    <%= render partial: 'spree/shared/taxonomies' %>
    <%= render partial: 'spree/shared/filters' if @taxon.leaf? %>
  </div>
<% end %>

<div data-hook="taxon_products">
  <%= render partial: 'spree/shared/products', locals: { products: @products, taxon: @taxon } %>
</div>

<% unless params[:keywords].present? %>
  <div data-hook="taxon_children">
    <% cache [I18n.locale, @taxon] do %>
      <%= render partial: 'taxon', collection: @taxon.children %>
    <% end %>
  </div>
<% end %>

3) This is how I render in my index

<div data-hook="homepage_products">
  <% cache(cache_key_for_products) do %>
    <%= render :partial => 'spree/shared/products', :locals => { :products => @products } %>
  <% end %>
</div>

4) _products.html.erb

<%
  paginated_products = @searcher.retrieve_products if params.key?(:keywords)
  paginated_products ||= products
%>

<% content_for :head do %>
  <% if paginated_products.respond_to?(:num_pages) %>
    <%= rel_next_prev_link_tags paginated_products %>
  <% end %>
<% end %>

<div data-hook="products_search_results_heading">
  <% if products.empty? %>
    <div data-hook="products_search_results_heading_no_results_found">
      <%= Spree.t(:no_products_found) %>
    </div>
  <% elsif params.key?(:keywords) %>
    <div data-hook="products_search_results_heading_results_found">
      <h6 class="search-results-title"><%= Spree.t(:search_results, keywords: h(params[:keywords])) %></h6>
    </div>
  <% end %>
</div>

<% if products.any? %>
  <div id="products" class="row" data-hook>
    <% products.each do |product| %>
      <% url = spree.product_url(product, taxon_id: @taxon.try(:id)) %>
      <div id="product_<%= product.id %>" class="col-md-3 col-sm-6 product-list-item" data-hook="products_list_item" itemscope itemtype="https://schema.org/Product">
        <div class="panel panel-default">
          <% cache(@taxon.present? ? [I18n.locale, current_currency, @taxon, product] : [I18n.locale, current_currency, product]) do %>
            <div class="panel-body text-center product-body">
              <%= link_to small_image(product, itemprop: "image"), url, itemprop: 'url' %><br/>
              <%= link_to truncate(product.name, length: 50), url, class: 'info', itemprop: "name", title: product.name %>
            </div>
            <div class="panel-footer text-center">
              <span itemprop="offers" itemscope itemtype="https://schema.org/Offer">
                <span class="price selling lead" itemprop="price"><%= display_price(product) %></span>
              </span>
            </div>
          <% end %>
        </div>
      </div>
    <% end %>
    <% reset_cycle("classes") %>
  </div>
<% end %>

<% if paginated_products.respond_to?(:num_pages) %>
  <%= paginate paginated_products, theme: 'twitter-bootstrap-3' %>
<% end %>

Controller index:

module Spree
  class HomeController < Spree::StoreController
    helper 'spree/products'
    respond_to :html

    def index
      @searcher = build_searcher(params.merge(include_images: true))
      @products = @searcher.retrieve_products
      @taxonomies = Spree::Taxonomy.includes(root: :children)
    end
  end
end

Answer:

Go to spree/frontend/app/controllers/spree/taxons_controller.rb

make the show method look like this

    def show
      @taxon = Taxon.friendly.find(params[:id])
      return unless @taxon

      @searcher = build_searcher(params.merge(include_images: true))
      @products = @searcher.retrieve_products
      @taxonomies = Spree::Taxonomy.includes(root: :children)
    end

Question:

Right now I've the following piece of code, which retrieves all images attached to Spree products.

<% @product.images_by_variant.each do |variant_id,images| %>
    <ul class="gallery gallery-<%= variant_id %>" data-variant="<%= variant_id %>">
        <% images.each do |image| %>
            <li>
                <%= image_tag(image.attachment.url(:single)) %>
            </li>
        <% end %>
    </ul>
<% end %>

However, there are also images displayed that represent all variants. I want to hide those.

How can I only show variant (different colors for example) for products?

Cheers!


Answer:

I found the solution. In the products_controller_decorator.rb, I added the following piece of code to the show method:

variants = @product.variants
if variants.blank?
    @variants = @product
elsif variants.length > 1
   @variants = variants.reject {|variant| variant.is_master? }
end

In the view I rendered the following partial:

<% if @variants == @product %>
    <%= render partial: 'product_single/product_gallery', locals: {variant: @product} %>
<% else %>
    <% @variants.each do |variant| %>
        <%= render partial: 'product_single/product_gallery', locals: {variant: variant} %>
    <% end %>
<% end %>

Question:

I'm getting a Stack Level Too Deep error in my Production environment for spree 2.3.0.

It occurs when I try to go to the product images index page in the Admin panel. This error is not happening in Development, only production on Ninefold. The error does not occur when I run the app locally in Production mode. I can seem to trace what is causing it. I have not changed any of the default core functionality in Spree related to Product Images. The images render fine on the front-end.

I'm using paperclip and aws. At one point there was no error on production. However, I've check all my commits and cant pinpoint what changed that is causing this issue. I've added other gems, but none of the gems conflict with the paperclip gems or its dependency, which was my my guess at what might be causing this.

here is the production log error.

+0000
I, [2015-01-11T22:02:28.691119 #26116] INFO -- : Processing by Spree::Admin::ImagesController#index as HTML
I, [2015-01-11T22:02:28.691252 #26116] INFO -- : Parameters: {"product_id"=>"black-string-tie-halter-top-with-printed-neck-binding"}
D, [2015-01-11T22:02:28.693443 #26116] DEBUG -- : Cache read: spree/app_configuration/redirect_https_to_http
D, [2015-01-11T22:02:28.693920 #26116] DEBUG -- : Cache read: spree/backend_configuration/locale
D, [2015-01-11T22:02:28.695806 #26116] DEBUG -- : [1m[36mSpree::Preference Load (0.7ms)[0m [1mSELECT "spree_preferences".* FROM "spree_preferences" WHERE "spree_preferences"."key" = 'spree/backend_configuration/locale' LIMIT 1[0m
D, [2015-01-11T22:02:28.695979 #26116] DEBUG -- : Cache write: spree/backend_configuration/locale
D, [2015-01-11T22:02:28.696566 #26116] DEBUG -- : Cache read: spree/app_configuration/allow_ssl_in_production
D, [2015-01-11T22:02:28.696881 #26116] DEBUG -- : Cache read: spree/app_configuration/check_for_spree_alerts
D, [2015-01-11T22:02:28.697171 #26116] DEBUG -- : Cache read: spree/app_configuration/last_check_for_spree_alerts
D, [2015-01-11T22:02:28.699099 #26116] DEBUG -- : [1m[35mSpree::User Load (0.5ms)[0m SELECT "spree_users".* FROM "spree_users" WHERE "spree_users"."id" = 1 ORDER BY "spree_users"."id" ASC LIMIT 1
D, [2015-01-11T22:02:28.702026 #26116] DEBUG -- : [1m[36m (0.5ms)[0m [1mSELECT COUNT(*) FROM "spree_roles" INNER JOIN "spree_roles_users" ON "spree_roles"."id" = "spree_roles_users"."role_id" WHERE "spree_roles_users"."user_id" = $1 AND "spree_roles"."name" = 'admin'[0m [["user_id", 1]]
I, [2015-01-11T22:02:28.706068 #26116] INFO -- : Completed 500 Internal Server Error in 15ms
F, [2015-01-11T22:02:28.706732 #26116] FATAL -- : 
SystemStackError (stack level too deep):
vendor/bundle/ruby/2.0.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract/connection_pool.rb:629

Any help would be greatly appreciated!

update - Full Stack Trace in Production Environment:

SystemStackError in Spree::Admin::ImagesController#index
stack level too deep

Rails.root: /var/www/apps/11829/releases/dbeebbeebaf8ac5fa580e0fd192ad9fc06e7ce4b

Application Trace | Framework Trace | Full Trace
vendor/bundle/ruby/2.0.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract/connection_pool.rb:629

Answer:

So after going through all the gems I installed I finally found the issue.

It was an alias_method in the Spree::Admin::ImagesController decorator that caused the Stack Level Too Deep error.

Question:

In my app, I am using spree gem. I want to store product_id and taxon_id in spree_products_taxons tables. But when I create, it shows above error. My code is like

def import
  require 'csv'
  file = params[:file]
  CSV.foreach(file.path, headers: true, encoding:'iso-8859-1:utf-8') do |row|

    @prod = Spree::Product.new()
    @prod.name = row["name"]
    @prod.shipping_category_id = row["shipping_category_id"]
    @prod.description = row["description"]
    @prod.available_on = row["available_on"]
    @prod.meta_description = row["meta_description"]
    @prod.meta_keywords = row["meta_keywords"]
    @prod.tax_category_id = row["tax_category_id"]
    @prod.shipping_category_id = row["shipping_category_id"]
    @prod.promotionable = row["promotionable"]
    @prod.meta_title = row["meta_title"]
    @prod.featured = row["featured"]
    @prod.supplier_id = row["supplier_id"]
    @prod.master.price = row["master_price"]
    @prod.master.cost_price = row["cost_price"]
    @prod.master.sku = row["sku"]
    @prod.master.tax_category_id = row["tax_category_id"]
    @prod.save!
    @prod_taxon = Spree.ProductTaxon.create(taxon_id: row["taxon_id"], product_id: @prod.id)
  end
  redirect_to admin_products_path, notice: "products imported."
end

Answer:

Based on the latest Spree::Taxon model and Spree::Product model from their GitHub repository, it looks like you should be able to replace this part of your code:

@prod.save!
@prod_taxon = Spree.ProductTaxon.create(taxon_id: row["taxon_id"], product_id: @prod.id)

with this (untested)

@prod.save!
@prod_taxon = Spree::Taxon.find(row["taxon_id"])
@prod.taxons << @prod_taxon

Hope that helps!

Question:

I am using the SpreeCommerce system to build an commerce site. I would like to add a button to the product page, next to the add to cart button, that would link to a different page from every product page. I have searched through their documentation and I can't figure out where they even add their cart button.

EDIT: If it is unclear, I don't know where the code that I want to edit is. I know how edit it and what I should put, but I don't know where to put it. If it is not like other edits with spree (you just duplicate a file and make changes) then I need more help, but I don't know what file to duplicate.


Answer:

1) run bundle open spree , navigate to core/app/spree/product/show.html.erb n 2) Inspect element on your browser and find where your button goes. 3) Override it using deface.

Here's an example:

I'm putting a button before div#cart-form (untested modify accordingly)

Deface::Override.new(:virtual_path  => "spree/product/show",
                         :insert_before => "div#cart-form",
                         :text          => "<button name="button">Click me</button>",
                         :name          => "button_product_show")

Lets assume file name is button_product_show.rb

I would put this file in app/overrides/spree/button_product_show.rb

spree/product/show.html.erb looks like below (at least on the version I'm using):

<div data-hook="product_show" itemscope itemtype="http://schema.org/Product">
  <% @body_id = 'product-details' %>

  <div class="columns six alpha" data-hook="product_left_part">
    <div class="row" data-hook="product_left_part_wrap">

      <div id="product-images" data-hook="product_images">
        <div id="main-image" data-hook>
          <%= render :partial => 'image' %>
        </div>
        <div id="thumbnails" data-hook>
          <%= render :partial => 'thumbnails' %>
        </div>
      </div>

      <div data-hook="product_properties">
        <%= render :partial => 'properties' %>
      </div>

    </div>
  </div>

  <div class="columns ten omega" data-hook="product_right_part">
    <div class="row" data-hook="product_right_part_wrap">

      <div id="product-description" data-hook="product_description">

        <h1 class="product-title" itemprop="name"><%= accurate_title %></h1>

        <div itemprop="description" data-hook="description">
          <%= product_description(@product) rescue t(:product_has_no_description) %>
        </div>

        <div id="cart-form" data-hook="cart_form">
          <%= render :partial => 'cart_form' %>
        </div>

      </div>

      <%= render :partial => 'taxons' %>

    </div>
  </div>

</div>

Further information can be found here: http://guides.spreecommerce.com/developer/view.html

Question:

I'm trying to add my products to taxons but I can't work out how its done. I can see in the schema there is spree_products_taxons but this doesn't have any model.

How do I add a product to a taxon in rails?


Answer:

There is a taxons field under product > edit where you can select the taxonomy of the product.

You need to create the taxonomy before selecting

You can create it from Products > Taxonomies

For more details, you can visit the Spree guides

Question:

I want to deploy a spree app to heroku and in order to do that I need to precompile my assets locally I did

heroku addons:create heroku-postgresql 

then I added config/application.rb

config.assets.initialize_on_precompile = false

my database.yaml file is

default: &default
  adapter: postgresql
  encoding: unicode
  pool: 5

development:
  <<: *default
  host: localhost
  database: anzels_development
  username: anzels
  password: 1234

test:
  <<: *default
  host: localhost
  database: anzels_test
  username: anzels
  password: 1234


production:
  adapter: postgresql
  encoding: unicode
  database: anzels_production
  pool: 5
  password:

and whenever I run

sumeet@sumi-pc:~/anzels$ rake assets:precompile RAILS_ENV=production

I get an error rake aborted!

ActiveRecord::NoDatabaseError: FATAL:  role "sumeet" does not exist
/home/sumeet/anzels/config/environment.rb:5:in `<top (required)>'
PG::ConnectionBad: FATAL:  role "sumeet" does not exist
/home/sumeet/anzels/config/environment.rb:5:in `<top (required)>'
Tasks: TOP => environment
(See full trace by running task with --trace)

config/enviormenr.rb

# Load the Rails application.
require File.expand_path('../application', __FILE__)

# Initialize the Rails application.
Rails.application.initialize!

please help


Answer:

In local computer you are trying to run

rake assets:precompile RAILS_ENV=production

but this requires your local compute to have config/database.yml with the config mentioned by @thieu-nguyen

so add the following in

username: $PRODUCTION_DB_USER

password: $PRODUCTION_DB_PASS

under production in config/database.yml

then add the environment for you local computer as

PRODUCTION_DB_USER=anzels

PRODUCTION_DB_PASS=1234

and for heroku as

PRODUCTION_DB_USER=user

PRODUCTION_DB_PASS="" (empty)


ANOTHER EASIER WAY IS

username: anzels

password: 1234

to production in config/database.yml **JUST BEFORE assests precompilation ** then run command

git checkout config/database.yml

JUST BEFORE GIT COMMIT command the idea is to not to commit the username and password but temporarily edit it for assests precompilation purpose only