Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SpreeConf 2012: Introducing Spree 1.0

jsqu99
February 18, 2012

SpreeConf 2012: Introducing Spree 1.0

SpreeConf 2012 (Day 1) training presentation. Gives an overview of Spree 1.0, followed by architecture discussions, installation details, and extension building

jsqu99

February 18, 2012
Tweet

Other Decks in Programming

Transcript

  1. Introducing 1.0 $ gem install rails -v '~> 3.1.1' $

    gem install spree $ rails new my_store $ spree install my_store 1 Saturday, February 18, 12
  2. Features • Provide common features only • Avoid bloat •

    Make everything else possible 5 Saturday, February 18, 12
  3. products users variants carts orders shipping payments returns inventory checkout

    coupons promotions properties taxonomies reports api adjustments back-ordering internationalization sales VAT theming pluggable search extensions 6 Saturday, February 18, 12
  4. Rails 3.1 Devise Kaminari ERB Asset Pipeline RSpec Deface Paperclip

    ActiveMerchant Faker MetaSearch ActiveRecord State Machine JQuery FactoryGirl Javascript Engines 8 Saturday, February 18, 12
  5. $ gem install rails -v '~> 3.1.1' $ gem install

    spree $ rails new my_store $ spree install my_store $ cd my_store $ bundle exec rails server Installation 31 Saturday, February 18, 12
  6. $ gem install rails -v '~> 3.1.1' $ gem install

    spree $ rails new my_store $ spree install my_store $ cd my_store $ bundle exec rails server Installation Under the Hood 32 Saturday, February 18, 12
  7. Installation assets //= require store/spree_core //= require store/spree_auth //= require

    store/spree_api //= require store/spree_promo //= require_tree . my_store/app/assets/javascripts/store/all.js 36 Saturday, February 18, 12
  8. //= require jquery //= require jquery_ujs //= require jquery.validate/jquery.validate.min //=

    require jquery.formalize.min //= require store/checkout //= require store/product //= require store/cart //= require store/helpers Installation assets spree/core/app/assets/javascripts/store/spree_core.js //= require store/spree_core //= require store/spree_auth //= require store/spree_api //= require store/spree_promo //= require_tree . 37 Saturday, February 18, 12
  9. spree_store_credits spree_affiliate spree_solr spree_gift_card spree_print_invoice spree_sphinx spree_related_products spree_comments spree_blog spree_volume_pricing

    spree_fulfillment spree_wishlist spree_wholesale spree_multi_domain spree_google_checkout spree_recently_viewed spree_pages spree_email_to_friend spree_digital spree_redirects spree_mp3player spree_editor spree_product_translations spree_faq spree_homepager spree_share spree_page_cache 48 Saturday, February 18, 12
  10. I need even more! • Write an extension • Customize

    your app 51 Saturday, February 18, 12
  11. Overriden products/show.html.erb <h1>Here's my new product detail page and I'm

    only going to show you the product name:</h1> <h2><%= @product.name %></h2> 60 Saturday, February 18, 12
  12. gem 'spree_core', :git => 'git://github.com/spree/spree.git' gem 'spree_some_extension', :path => '../spree_some_extension'

    gem 'spree_some_other_extension', :path => '../spree_some_other_extension' ~/my_store/Gemfile Add Extensions $ spree extension some_extension $ spree extension some_other_extension 64 Saturday, February 18, 12
  13. Gemfile Order Matters gem 'spree_core', :git => 'git://github.com/spree/spree.git' gem 'spree_some_extension',

    :path => '../spree_some_extension' gem 'spree_some_other_extension', :path => '../spree_some_other_extension' gem 'spree_core', :git => 'git://github.com/spree/spree.git' gem 'spree_some_other_extension', :path => '../spree_some_other_extension' gem 'spree_some_extension', :path => '../spree_some_extension' 71 Saturday, February 18, 12
  14. auth app/views/spree/products/show.html.erb Rails Application app/overrides/change_product_detail.rb Deface::Override.new(:virtual_path => "spree/products/show", :name =>

    "add_submit_review_button", :insert_after => "[data-hook='product_properties']" :partial => "spree/shared/reviews") View Override Deface 75 Saturday, February 18, 12
  15. module Spree class Product < ActiveRecord::Base has_many :product_option_types, :dependent =>

    :destroy has_many :option_types, :through => :product_option_types has_many :product_properties, :dependent => :destroy has_many :properties, :through => :product_properties ....... end spree/core/app/models/spree/product.rb Model Override 78 Saturday, February 18, 12
  16. module Spree class Product < ActiveRecord::Base has_many :product_option_types, :dependent =>

    :destroy has_many :option_types, :through => :product_option_types has_many :product_properties, :dependent => :destroy has_many :properties, :through => :product_properties ....... end Spree::Product.class_eval do def minimum_met? return true if self.minimum_order_count.blank? self.current_order_count >= self.minimum_order_count end end spree/core/app/models/spree/product.rb some_extension/app/models/spree/product_decorator.rb Model Override 78 Saturday, February 18, 12
  17. module Spree class ProductsController < BaseController HTTP_REFERER_REGEXP = /^https?:\/\/[^\/]+\/t\/([a-z0-9\-\/]+)$/ rescue_from

    ActiveRecord::RecordNotFound, :with => :render_404 helper 'spree/taxons' respond_to :html def index @searcher = Spree::Config.searcher_class.new(params) @products = @searcher.retrieve_products respond_with(@products) end spree/core/app/controllers/spree/products_controller.rb Controller Override 80 Saturday, February 18, 12
  18. module Spree class ProductsController < BaseController HTTP_REFERER_REGEXP = /^https?:\/\/[^\/]+\/t\/([a-z0-9\-\/]+)$/ rescue_from

    ActiveRecord::RecordNotFound, :with => :render_404 helper 'spree/taxons' respond_to :html def index @searcher = Spree::Config.searcher_class.new(params) @products = @searcher.retrieve_products respond_with(@products) end Spree::ProductsController.class_eval do before_filter :get_collection_products, :only => :show private def get_collection_products @collection_products ||= @object.collection.products.not_deleted end end spree/core/app/controllers/spree/products_controller.rb some_extension/app/controllers/spree/products_controller_decorator.rb Controller Override 80 Saturday, February 18, 12
  19. Rails Application config/application.rb module MyStore class Application < Rails::Application config.to_prepare

    do # Load application's model / class decorators Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator*.rb")) do |c| Rails.configuration.cache_classes ? require(c) : load(c) end end 82 Saturday, February 18, 12
  20. spree/core/app/models/spree/user.rb 1 # Default implementation of User. This class is

    intended to be modified by extensions (ex. spree_auth) 2 module Spree 3 class User < ActiveRecord::Base 4 5 has_many :orders 6 7 belongs_to :ship_address, :foreign_key => 'ship_address_id', :class_name => 'Spree::Address' 8 belongs_to :bill_address, :foreign_key => 'bill_address_id', :class_name => 'Spree::Address' 9 10 scope :registered 11 12 def anonymous? 13 false 14 end 15 16 # Creates an anonymous user 17 def self.anonymous! 18 create 19 end 20 21 attr_accessor :password 22 attr_accessor :password_confirmation 23 end 24 end spree_core ‘user’ 89 Saturday, February 18, 12
  21. 1 module Spree 2 class User < ActiveRecord::Base 3 devise

    :database_authenticatable, :token_authenticatable, :registerable, :recoverable, 4 :rememberable, :trackable, :validatable, :encryptable, :encryptor => 'authlogic_sha512' 5 6 has_many :orders 7 has_and_belongs_to_many :roles, :join_table => 'spree_roles_users' 8 belongs_to :ship_address, :foreign_key => 'ship_address_id', :class_name => 'Spree::Address' 9 belongs_to :bill_address, :foreign_key => 'bill_address_id', :class_name => 'Spree::Address' 10 11 before_save :check_admin 12 before_validation :set_login 13 14 # Setup accessible (or protected) attributes for your model 15 attr_accessible :email, :password, :password_confirmation, :remember_me, :persistence_token 16 17 users_table_name = User.table_name 18 roles_table_name = Role.table_name 19 20 scope :admin, lambda { includes(:roles).where("#{roles_table_name}.name" => "admin") } 21 scope :registered, where("#{users_table_name}.email NOT LIKE ?", "%@example.net") 22 23 # Creates an anonymous user. An anonymous user is basically an auto-generated +User+ account that is created for the customer 24 # behind the scenes and its completely transparently to the customer. All +Orders+ must have a +User+ so this is necessary 25 # when adding to the "cart" (which is really an order) and before the customer has a chance to provide an email or to register. 26 def self.anonymous! 27 token = User.generate_token(:persistence_token) 28 User.create(:email => "#{token}@example.net", :password => token, :password_confirmation => token, :persistence_token => token) 29 end 30 31 def anonymous? 32 email =~ /@example.net$/ 33 end spree/auth/app/models/spree/user.rb spree_auth ‘user’ 90 Saturday, February 18, 12
  22. spree/auth/app/controllers/spree/checkout_controller_decorator.rb 1 Spree::CheckoutController.class_eval do 2 before_filter :check_authorization 3 before_filter :check_registration,

    :except => [:registration, :update_registration] 4 5 helper 'spree/users' 6 7 def registration 8 @user = Spree::User.new 9 end 10 11 def update_registration ....... 21 end 22 23 private 24 def check_authorization 25 authorize!(:edit, current_order, session[:access_token]) 26 end 27 28 # Introduces a registration step whenever the +registration_step+ preference is true. 29 def check_registration 30 return unless Spree::Auth::Config[:registration_step] 31 return if current_user or current_order.email spree_auth ‘checkout’ 93 Saturday, February 18, 12
  23. spree/core/lib/spree/core/controller_helpers.rb 1 module Spree 2 module Core 3 module ControllerHelpers

    4 .... 5 .... 6 def associate_user 7 return unless current_user and current_order 8 current_order.associate_user!(current_user) 9 session[:guest_token] = nil 10 end spree_core ‘helper’ 96 Saturday, February 18, 12
  24. class Spree::UserPasswordsController < Devise::PasswordsController include Spree::Core::ControllerHelpers helper 'spree/users', 'spree/base' after_filter

    :associate_user spree/auth/app/controllers/spree/user_passwords_controller.rb spree/core/lib/spree/core/controller_helpers.rb 1 module Spree 2 module Core 3 module ControllerHelpers 4 .... 5 .... 6 def associate_user 7 return unless current_user and current_order 8 current_order.associate_user!(current_user) 9 session[:guest_token] = nil 10 end spree/auth/app/models/spree/order_decorator.rb Spree::Order.class_eval do token_resource # Associates the specified user with the order and destroys any previous association with guest user if # necessary. def associate_user!(user) self.user = user self.email = user.email # disable validations since this can cause issues when associating an incomplete address during the address step save(:validate => false) end end 97 Saturday, February 18, 12
  25. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 102 Saturday, February 18, 12
  26. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating 103 Saturday, February 18, 12
  27. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating Spree Reviews - Capabilities 4 Rate existing reviews 104 Saturday, February 18, 12
  28. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating 4 Rate existing reviews 5 Admin approve/deny 105 Saturday, February 18, 12
  29. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating 4 Rate existing reviews 5 Admin approve/deny 6 Admin configuration 106 Saturday, February 18, 12
  30. $ spree extension reviews $ cd spree_reviews $ bundle exec

    rake test_app start coding 107 Saturday, February 18, 12
  31. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating 4 Rate existing reviews 5 Admin approve/deny 6 Admin configuration 109 Saturday, February 18, 12
  32. What should be in the model? $ bundle exec rails

    g model review product_id:integer Submit a Review 110 Saturday, February 18, 12
  33. What should be in the model? $ bundle exec rails

    g model review product_id:integer name:string Submit a Review 110 Saturday, February 18, 12
  34. What should be in the model? $ bundle exec rails

    g model review product_id:integer name:string rating:integer Submit a Review 110 Saturday, February 18, 12
  35. What should be in the model? $ bundle exec rails

    g model review product_id:integer name:string rating:integer review:text Submit a Review 110 Saturday, February 18, 12
  36. app/views/spree/products/show.html.erb 1 <div class="row"> 2 3 <div id="product-images" data-hook="product_images"> 4

    <div id="main-image" data-hook> 5 <%= render :partial => 'image' %> 6 </div> 7 <div id="thumbnails" data-hook> 8 <%= render :partial => 'thumbnails', :locals => { :product => @product } %> 9 </div> 10 </div> 11 12 <div data-hook="product_properties"> 13 <%= render :partial => 'properties' %> 14 </div> 15 16 </div> 112 Saturday, February 18, 12
  37. app/overrides/add_reviews_after_product_properties.rb app/views/spree/products/show.html.erb 1 <div class="row"> 2 3 <div id="product-images" data-hook="product_images">

    4 <div id="main-image" data-hook> 5 <%= render :partial => 'image' %> 6 </div> 7 <div id="thumbnails" data-hook> 8 <%= render :partial => 'thumbnails', :locals => { :product => @product } %> 9 </div> 10 </div> 11 12 <div data-hook="product_properties"> 13 <%= render :partial => 'properties' %> 14 </div> 15 16 </div> 112 Saturday, February 18, 12
  38. app/overrides/add_reviews_after_product_properties.rb app/views/spree/products/show.html.erb 1 <div class="row"> 2 3 <div id="product-images" data-hook="product_images">

    4 <div id="main-image" data-hook> 5 <%= render :partial => 'image' %> 6 </div> 7 <div id="thumbnails" data-hook> 8 <%= render :partial => 'thumbnails', :locals => { :product => @product } %> 9 </div> 10 </div> 11 12 <div data-hook="product_properties"> 13 <%= render :partial => 'properties' %> 14 </div> 15 16 </div> Deface::Override.new(:virtual_path => "spree/products/show", 112 Saturday, February 18, 12
  39. app/overrides/add_reviews_after_product_properties.rb app/views/spree/products/show.html.erb 1 <div class="row"> 2 3 <div id="product-images" data-hook="product_images">

    4 <div id="main-image" data-hook> 5 <%= render :partial => 'image' %> 6 </div> 7 <div id="thumbnails" data-hook> 8 <%= render :partial => 'thumbnails', :locals => { :product => @product } %> 9 </div> 10 </div> 11 12 <div data-hook="product_properties"> 13 <%= render :partial => 'properties' %> 14 </div> 15 16 </div> Deface::Override.new(:virtual_path => "spree/products/show", :name => "add_submit_review_link", 112 Saturday, February 18, 12
  40. app/overrides/add_reviews_after_product_properties.rb app/views/spree/products/show.html.erb 1 <div class="row"> 2 3 <div id="product-images" data-hook="product_images">

    4 <div id="main-image" data-hook> 5 <%= render :partial => 'image' %> 6 </div> 7 <div id="thumbnails" data-hook> 8 <%= render :partial => 'thumbnails', :locals => { :product => @product } %> 9 </div> 10 </div> 11 12 <div data-hook="product_properties"> 13 <%= render :partial => 'properties' %> 14 </div> 15 16 </div> Deface::Override.new(:virtual_path => "spree/products/show", :name => "add_submit_review_link", :insert_after => "[data-hook='product_properties'], #product_properties[data-hook]" 112 Saturday, February 18, 12
  41. app/overrides/add_reviews_after_product_properties.rb app/views/spree/products/show.html.erb 1 <div class="row"> 2 3 <div id="product-images" data-hook="product_images">

    4 <div id="main-image" data-hook> 5 <%= render :partial => 'image' %> 6 </div> 7 <div id="thumbnails" data-hook> 8 <%= render :partial => 'thumbnails', :locals => { :product => @product } %> 9 </div> 10 </div> 11 12 <div data-hook="product_properties"> 13 <%= render :partial => 'properties' %> 14 </div> 15 16 </div> Deface::Override.new(:virtual_path => "spree/products/show", :name => "add_submit_review_link", :insert_after => "[data-hook='product_properties'], #product_properties[data-hook]" :partial => "spree/shared/reviews") 112 Saturday, February 18, 12
  42. <div class="row"> <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', :locals => { :product => @product } %> </div> </div> <div data-hook="product_properties"> <%= render :partial => 'properties' %> </div> </div> app/views/spree/products/show.html.erb 113 Saturday, February 18, 12
  43. <div id="reviews"> <%= link_to t('write_your_own_review'), new_product_review_path(@product) %> </div> <div class="row">

    <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', :locals => { :product => @product } %> </div> </div> <div data-hook="product_properties"> <%= render :partial => 'properties' %> </div> </div> app/views/spree/shared/_reviews.html app/views/spree/products/show.html.erb 113 Saturday, February 18, 12
  44. <div class="row"> <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', :locals => { :product => @product } %> </div> </div> <div data-hook="product_properties"> <%= render :partial => 'properties' %> </div> </div> <div id="reviews"> <%= link_to t('write_your_own_review'), new_product_review_path(@product) %> </div> 114 Saturday, February 18, 12
  45. Spree Reviews Dummy App $ spree extension reviews $ cd

    spree_reviews $ bundle exec rake test_app $ cd spec/dummy $ bundle exec rails server 115 Saturday, February 18, 12
  46. $ bundle exec rails g controller spree/reviews class Spree::ReviewsController <

    Spree::BaseController before_filter :load_product, :only => [:index, :new, :create] def new @review = Spree::Review.new(:product => @product) end private def load_product @product = Spree::Product.find_by_permalink!(params[:product_id]) end end app/controllers/spree/reviews_controller.rb Create Reviews 120 Saturday, February 18, 12
  47. <h2> <%= I18n.t('leave_us_a_review_for') + ' "' + @product.name + '"'

    %> </h2> <%= render 'form', { :review => @review, :product => @product } %> app/views/spree/reviews/new.html.erb app/views/spree/reviews/_form.html.erb <%= form_for review, :url => product_reviews_path(product), :html => {:method => :post} do |f| %> <p class="review_rating_field"> <%= f.label :rating %> <%= f.text_field :rating%> </p> <p class="review_name_field"> <%= f.label :name %> <%= f.text_field :name%> </p> <p class="review_title_field"> <%= f.label :title %> <%= f.text_field :title %> </p> <p class="review_review_field"> <%= f.label :review %> <%= f.text_area :review %> </p> <p class="review_submit"> <%= f.submit I18n.t('submit_your_review') %> </p> <% end %> Standard ‘new’ html 122 Saturday, February 18, 12
  48. def create @review = Spree::Review.new(params[:review]) @review.product = @product if @review.save

    flash[:notice] = t('review_successfully_submitted') redirect_to (product_path(@product)) else render :action => "new" end end Spree::ReviewsController#create 126 Saturday, February 18, 12
  49. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating 4 Rate existing reviews 5 Admin approve/deny 6 Admin configuration 128 Saturday, February 18, 12
  50. app/views/spree/shared/_reviews.html 1 <div id="reviews"> 2 <h6><%= t(:reviews) %></h6> 3 4

    <% for review in @product.reviews %> 5 <%= render :partial => 'spree/shared/review', :locals => {:review => review} %> 6 <% end %> 7 8 <div id="reviews"> 9 <%= link_to t('write_your_own_review'), new_product_review_path(@product) %> 10 </div> 11 12 </div> Revisit our partial 129 Saturday, February 18, 12
  51. app/views/spree/shared/_review.html <div> <span><strong><%= review.name %></strong></span> <span>&nbsp; <%= review.title %> </span>

    <br/> <span><%= t('submitted_on') %><%= l review.created_at.to_date %></span> <div> <%= review.review %> </div> </div> partial within a partial 130 Saturday, February 18, 12
  52. app/views/spree/shared/_reviews.html 1 <div id="reviews"> 2 <h6> <%= t(:reviews) %></h6> 3

    4 <% for review in @product.reviews %> 5 <%= render :partial => 'spree/shared/review', :locals => {:review => review} %> 6 <% end %> 7 8 <div id="reviews"> 9 <%= link_to t('write_your_own_review'), new_product_review_path(@product) %> 10 </div> 11 12 </div> Offending line #4 132 Saturday, February 18, 12
  53. app/models/spree/product_decorator.rb 1 # Add access to reviews to the product

    model 2 Spree::Product.class_eval do 3 has_many :reviews 4 end Decorate the model 133 Saturday, February 18, 12
  54. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating 4 Rate existing reviews 5 Admin approve/deny 6 Admin configuration 135 Saturday, February 18, 12
  55. app/views/spree/shared/_reviews.html 1 <div id="reviews"> 2 <h6> <%= t(:reviews) %></h6> 3

    <%= render :partial => 'spree/shared/rating', :locals => {:product => @product, :review => 0} %> 4 5 <% for review in @product.reviews.approval_filter %> 6 <%= render :partial => 'spree/shared/review', :locals => {:review => review} %> 7 <hr/> 8 <% end %> 9 10 <div id="reviews"> 11 <%= link_to t('write_your_own_review'), new_product_review_path(@product) %> 12 </div> 13 </div> New line #3 136 Saturday, February 18, 12
  56. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating 4 Rate existing reviews 5 Admin approve/deny 6 Admin configuration 137 Saturday, February 18, 12
  57. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating 4 Rate existing reviews 5 Admin approve/deny 6 Admin configuration 139 Saturday, February 18, 12
  58. app/overrides/add_reviews_tab_to_admin.rb 1 Deface::Override.new(:virtual_path => "spree/layouts/admin", 2 :name => "reviews_admin_tab", 3

    :insert_bottom => "[data-hook='admin_tabs']", 4 :text => "<%= tab(:reviews, :label => 'review_management') %>") Admin Tab 140 Saturday, February 18, 12
  59. class Spree::Admin::ReviewsController < Spree::Admin::ResourceController helper Spree::ReviewsHelper def index @unapproved_reviews =

    Spree::Review.not_approved.find(:all, :order => "created_at DESC") @approved_reviews = Spree::Review.approved.find(:all, :order => "created_at DESC") end def approve r = Spree::Review.find(params[:id]) if r.update_attribute(:approved, true) r.product.recalculate_rating flash[:notice] = t("info_approve_review") else flash[:error] = t("error_approve_review") end redirect_to admin_reviews_path end end Implement a Spree Admin Controller 142 Saturday, February 18, 12
  60. Spree Reviews - Capabilities 1 Submit a review 2 See

    existing reviews 3 See average rating 4 Rate existing reviews 5 Admin approve/deny 6 Admin configuration 143 Saturday, February 18, 12
  61. spree/core/app/views/spree/admin/configurations/index.html.erb 1 <h1><%= t(:configurations) %></h1> 2 3 <table class="index" data-hook="configuration">

    4 <thead> 5 <tr> 6 <th><%= t(:system) %></th> 7 <th><%= t(:description) %></th> 8 </tr> 9 </thead> 10 <tbody data-hook="admin_configurations_menu"> 11 <tr> 12 <td><%= link_to t(:general_settings), admin_general_settings_path %></td> 13 <td><%= t(:general_settings_description) %></td> 14 </tr> 15 <tr> 16 <td><%= link_to t(:mail_methods), admin_mail_methods_path %></td> 17 <td><%= t(:email_server_settings_description) %></td> 18 </tr> 19 <tr> 20 <td><%= link_to t(:tax_categories), admin_tax_categories_path %></td> 21 <td><%= t(:tax_categories_setting_description) %></td> 22 </tr> 23 ..... 24 ..... 25 ..... 26 <tr> 27 <td><%= link_to t(:zones), admin_zones_path %></td> 28 <td><%= t(:zone_setting_description) %></td> 29 </tr> 30 </tbody> 31 </table> Original Configuration HTML 145 Saturday, February 18, 12
  62. app/overrides/add_reviews_to_admin_configuration_menu.rb Deface::Override.new( :virtual_path => "spree/admin/configurations/index", :name => "add_reviews_to_admin_configuration_menu", :insert_bottom =>

    "[data-hook='admin_configurations_menu'], #admin_configurations_menu[data-hook]", :text => "<%= configurations_menu_item(t('spree_reviews.review_settings'), admin_review_settings_path, t('spree_reviews.manage_review_settings')) %>" ) Override for Configuration Item Deface 146 Saturday, February 18, 12
  63. Spree Reviews - Preferences class Spree::Review < ActiveRecord::Base .... scope

    :preview, :limit => Spree::Reviews::Config[:preview_size], :order=>"created_at desc" app/models/spree/review.rb 149 Saturday, February 18, 12
  64. 1 class Spree::ReviewsAbility 2 include CanCan::Ability 3 4 def initialize(user)

    5 can :create, Spree::Review do |review| 6 user.has_role?(:user) || !Spree::Reviews::Config[:require_login] 7 end 8 can :create, Spree::FeedbackReview do |review| 9 user.has_role?(:user) || !Spree::Reviews::Config[:require_login] 10 end 11 end 12 end Spree Reviews - Preferences app/models/spree/reviews_ability.rb 150 Saturday, February 18, 12
  65. Spree Reviews - CanCan 1 module SpreeReviews 2 class Engine

    < Rails::Engine 3 engine_name 'spree_reviews' 4 5 config.autoload_paths += %W(#{config.root}/lib) 6 7 def self.activate 8 Dir.glob(File.join(File.dirname(__FILE__),"../../app/**/*_decorator*.rb")) do |c| 9 Rails.env.production? ? require(c) : load(c) 10 end 11 Spree::Ability.register_ability(Spree::ReviewsAbility) 12 end 13 14 config.to_prepare &method(:activate).to_proc 15 end 16 end lib/spree_reviews/engine.rb 151 Saturday, February 18, 12
  66. //= require store/spree_core //= require store/spree_auth //= require store/spree_api //=

    require store/spree_promo //= require_tree . assets/javascripts/store/all.js spree apps have 154 Saturday, February 18, 12
  67. lib/generators/spree_some_extension/install/install_generator.rb module SpreeSomeExtension module Generators class InstallGenerator < Rails::Generators::Base def

    add_javascripts append_file "app/assets/javascripts/store/all.js", "//= require store/spree_some_extension\n" append_file "app/assets/javascripts/admin/all.js", "//= require admin/spree_some_extension\n" end def add_stylesheets inject_into_file "app/assets/stylesheets/store/all.css", " *= require store/spree_some_extension\n", :before => /\*\//, :verbose => true inject_into_file "app/assets/stylesheets/admin/all.css", " *= require admin/spree_some_extension\n", :before => /\*\//, :verbose => true end extension assets 156 Saturday, February 18, 12
  68. lib/generators/spree_reviews/install/install_generator.rb 1 module SpreeReviews 2 module Generators 3 class InstallGenerator

    < Rails::Generators::Base 4 5 def add_javascripts 6 append_file "app/assets/javascripts/store/all.js", "//= require store/jquery.rating\n" 7 end 8 9 def add_stylesheets 10 inject_into_file "app/assets/stylesheets/store/all.css", " *= require store/spree_reviews \n", :before => /\*\//, :verbose => true 11 end reviews assets 158 Saturday, February 18, 12
  69. my_store/app/assets/javascripts/store/all.js //= require store/spree_core //= require store/spree_auth //= require store/spree_api

    //= require store/spree_promo //= require_tree . //= require store/jquery.rating your spree app - voila 159 Saturday, February 18, 12
  70. 1 module SpreeReviews 2 module Generators 3 class InstallGenerator <

    Rails::Generators::Base 4 5 def add_javascripts 6 append_file "app/assets/javascripts/store/all.js", "//= require store/jquery.rating\n" 7 end 8 9 def add_stylesheets 10 inject_into_file "app/assets/stylesheets/store/all.css", " *= require store/ spree_reviews\n", :before => /\*\//, :verbose => true 11 end 12 13 def add_migrations 14 run 'rake railties:install:migrations FROM=spree_reviews' 15 end 16 17 def run_migrations 18 res = ask "Would you like to run the migrations now? [Y/n]" 19 if res == "" || res.downcase == "y" 20 run 'rake db:migrate' 21 else 22 puts "Skipping rake db:migrate, don't forget to run it!" 23 end 24 end 25 end 26 end 27 end 161 Saturday, February 18, 12
  71. finished product - spree reviews gem 'spree', ‘~> 1.0.0’ gem

    'spree_reviews', :git => 'git://github.com/spree/spree_reviews.git bundle exec rails g spree_reviews:install 162 Saturday, February 18, 12
  72. Versionfile "1.0.x" => { :branch => 'master' } "0.70.x" =>

    { :ref => '41c5318612a38f9173226379c3790c6c79e11ffe' } 163 Saturday, February 18, 12