boom
This commit is contained in:
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
\#*
|
||||
*~
|
||||
.#*
|
||||
.DS_Store
|
||||
.idea
|
||||
.project
|
||||
tmp
|
||||
nbproject
|
||||
*.swp
|
||||
spec/test_app
|
||||
.tmproj
|
||||
35
Gemfile
Normal file
35
Gemfile
Normal file
@@ -0,0 +1,35 @@
|
||||
source 'http://rubygems.org'
|
||||
|
||||
#gem 'spree_core', :path => '../spree/core'
|
||||
#gem 'spree_digital', :path => 'spree_digital'
|
||||
#gem "sqlite3-ruby"
|
||||
#
|
||||
#group :test do
|
||||
# gem 'rspec-rails', '= 2.5.0'
|
||||
# gem 'factory_girl', '= 1.3.3'
|
||||
# gem 'factory_girl_rails', '= 1.0.1'
|
||||
# gem 'rcov'
|
||||
# gem 'shoulda'
|
||||
# gem 'faker'
|
||||
# if RUBY_VERSION < "1.9"
|
||||
# gem "ruby-debug"
|
||||
# else
|
||||
# gem "ruby-debug19"
|
||||
# end
|
||||
#end
|
||||
#
|
||||
#group :cucumber do
|
||||
# gem 'cucumber-rails'
|
||||
# gem 'database_cleaner', '= 0.6.7.RC'
|
||||
# gem 'nokogiri'
|
||||
# gem 'capybara', '= 0.4.1.2'
|
||||
# gem 'factory_girl', '= 1.3.3'
|
||||
# gem 'factory_girl_rails', '= 1.0.1'
|
||||
# gem 'faker'
|
||||
# gem 'launchy'
|
||||
# if RUBY_VERSION < "1.9"
|
||||
# gem "ruby-debug"
|
||||
# else
|
||||
# gem "ruby-debug19"
|
||||
# end
|
||||
#end
|
||||
217
Gemfile.lock
Normal file
217
Gemfile.lock
Normal file
@@ -0,0 +1,217 @@
|
||||
PATH
|
||||
remote: /Users/Future/Sites/Rails/Projekte/spree/core
|
||||
specs:
|
||||
spree_core (0.60.99)
|
||||
activemerchant (= 1.12.0)
|
||||
acts_as_list (= 0.1.2)
|
||||
faker (= 0.9.5)
|
||||
highline (= 1.5.1)
|
||||
jquery-rails (= 0.2.6)
|
||||
meta_search (= 1.0.1)
|
||||
nested_set (= 1.6.4)
|
||||
paperclip (= 2.3.8)
|
||||
rails (= 3.0.6)
|
||||
rd_find_by_param (= 0.1.1)
|
||||
rd_resource_controller
|
||||
rd_unobtrusive_date_picker (= 0.1.0)
|
||||
state_machine (= 0.9.4)
|
||||
stringex (= 1.0.3)
|
||||
will_paginate (= 3.0.pre2)
|
||||
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
spree_digital (0.60.99)
|
||||
spree_core (>= 0.60.99)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
abstract (1.0.0)
|
||||
actionmailer (3.0.6)
|
||||
actionpack (= 3.0.6)
|
||||
mail (~> 2.2.15)
|
||||
actionpack (3.0.6)
|
||||
activemodel (= 3.0.6)
|
||||
activesupport (= 3.0.6)
|
||||
builder (~> 2.1.2)
|
||||
erubis (~> 2.6.6)
|
||||
i18n (~> 0.5.0)
|
||||
rack (~> 1.2.1)
|
||||
rack-mount (~> 0.6.14)
|
||||
rack-test (~> 0.5.7)
|
||||
tzinfo (~> 0.3.23)
|
||||
activemerchant (1.12.0)
|
||||
activesupport (>= 2.3.8)
|
||||
braintree (>= 2.0.0)
|
||||
builder (>= 2.0.0)
|
||||
activemodel (3.0.6)
|
||||
activesupport (= 3.0.6)
|
||||
builder (~> 2.1.2)
|
||||
i18n (~> 0.5.0)
|
||||
activerecord (3.0.6)
|
||||
activemodel (= 3.0.6)
|
||||
activesupport (= 3.0.6)
|
||||
arel (~> 2.0.2)
|
||||
tzinfo (~> 0.3.23)
|
||||
activeresource (3.0.6)
|
||||
activemodel (= 3.0.6)
|
||||
activesupport (= 3.0.6)
|
||||
activesupport (3.0.6)
|
||||
acts_as_list (0.1.2)
|
||||
arel (2.0.9)
|
||||
braintree (2.9.1)
|
||||
builder
|
||||
builder (2.1.2)
|
||||
capybara (0.4.1.2)
|
||||
celerity (>= 0.7.9)
|
||||
culerity (>= 0.2.4)
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
selenium-webdriver (>= 0.0.27)
|
||||
xpath (~> 0.1.3)
|
||||
celerity (0.8.9)
|
||||
childprocess (0.1.8)
|
||||
ffi (~> 1.0.6)
|
||||
columnize (0.3.2)
|
||||
configuration (1.2.0)
|
||||
cucumber (0.10.2)
|
||||
builder (>= 2.1.2)
|
||||
diff-lcs (>= 1.1.2)
|
||||
gherkin (>= 2.3.5)
|
||||
json (>= 1.4.6)
|
||||
term-ansicolor (>= 1.0.5)
|
||||
cucumber-rails (0.4.1)
|
||||
cucumber (>= 0.10.1)
|
||||
nokogiri (>= 1.4.4)
|
||||
rack-test (>= 0.5.7)
|
||||
culerity (0.2.15)
|
||||
database_cleaner (0.6.7.RC)
|
||||
diff-lcs (1.1.2)
|
||||
erubis (2.6.6)
|
||||
abstract (>= 1.0.0)
|
||||
factory_girl (1.3.3)
|
||||
factory_girl_rails (1.0.1)
|
||||
factory_girl (~> 1.3)
|
||||
railties (>= 3.0.0)
|
||||
faker (0.9.5)
|
||||
i18n (~> 0.4)
|
||||
ffi (1.0.7)
|
||||
rake (>= 0.8.7)
|
||||
gherkin (2.3.5)
|
||||
json (>= 1.4.6)
|
||||
highline (1.5.1)
|
||||
i18n (0.5.0)
|
||||
jquery-rails (0.2.6)
|
||||
rails (~> 3.0)
|
||||
thor (~> 0.14.4)
|
||||
json (1.5.1)
|
||||
json_pure (1.5.1)
|
||||
launchy (0.4.0)
|
||||
configuration (>= 0.0.5)
|
||||
rake (>= 0.8.1)
|
||||
linecache (0.43)
|
||||
mail (2.2.15)
|
||||
activesupport (>= 2.3.6)
|
||||
i18n (>= 0.4.0)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
meta_search (1.0.1)
|
||||
actionpack (~> 3.0.2)
|
||||
activerecord (~> 3.0.2)
|
||||
activesupport (~> 3.0.2)
|
||||
arel (~> 2.0.2)
|
||||
mime-types (1.16)
|
||||
nested_set (1.6.4)
|
||||
activerecord (>= 3.0.0)
|
||||
railties (>= 3.0.0)
|
||||
nokogiri (1.4.4)
|
||||
paperclip (2.3.8)
|
||||
activerecord
|
||||
activesupport
|
||||
polyglot (0.3.1)
|
||||
rack (1.2.2)
|
||||
rack-mount (0.6.14)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (0.5.7)
|
||||
rack (>= 1.0)
|
||||
rails (3.0.6)
|
||||
actionmailer (= 3.0.6)
|
||||
actionpack (= 3.0.6)
|
||||
activerecord (= 3.0.6)
|
||||
activeresource (= 3.0.6)
|
||||
activesupport (= 3.0.6)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.0.6)
|
||||
railties (3.0.6)
|
||||
actionpack (= 3.0.6)
|
||||
activesupport (= 3.0.6)
|
||||
rake (>= 0.8.7)
|
||||
thor (~> 0.14.4)
|
||||
rake (0.8.7)
|
||||
rcov (0.9.9)
|
||||
rd_find_by_param (0.1.1)
|
||||
activerecord (~> 3.0)
|
||||
activesupport (~> 3.0)
|
||||
rd_resource_controller (1.0.1)
|
||||
rd_unobtrusive_date_picker (0.1.0)
|
||||
rspec (2.5.0)
|
||||
rspec-core (~> 2.5.0)
|
||||
rspec-expectations (~> 2.5.0)
|
||||
rspec-mocks (~> 2.5.0)
|
||||
rspec-core (2.5.1)
|
||||
rspec-expectations (2.5.0)
|
||||
diff-lcs (~> 1.1.2)
|
||||
rspec-mocks (2.5.0)
|
||||
rspec-rails (2.5.0)
|
||||
actionpack (~> 3.0)
|
||||
activesupport (~> 3.0)
|
||||
railties (~> 3.0)
|
||||
rspec (~> 2.5.0)
|
||||
ruby-debug (0.10.4)
|
||||
columnize (>= 0.1)
|
||||
ruby-debug-base (~> 0.10.4.0)
|
||||
ruby-debug-base (0.10.4)
|
||||
linecache (>= 0.3)
|
||||
rubyzip (0.9.4)
|
||||
selenium-webdriver (0.1.4)
|
||||
childprocess (>= 0.1.7)
|
||||
ffi (>= 1.0.7)
|
||||
json_pure
|
||||
rubyzip
|
||||
shoulda (2.11.3)
|
||||
sqlite3 (1.3.3)
|
||||
sqlite3-ruby (1.3.3)
|
||||
sqlite3 (>= 1.3.3)
|
||||
state_machine (0.9.4)
|
||||
stringex (1.0.3)
|
||||
term-ansicolor (1.0.5)
|
||||
thor (0.14.6)
|
||||
treetop (1.4.9)
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.26)
|
||||
will_paginate (3.0.pre2)
|
||||
xpath (0.1.3)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
capybara (= 0.4.1.2)
|
||||
cucumber-rails
|
||||
database_cleaner (= 0.6.7.RC)
|
||||
factory_girl (= 1.3.3)
|
||||
factory_girl_rails (= 1.0.1)
|
||||
faker
|
||||
launchy
|
||||
nokogiri
|
||||
rcov
|
||||
rspec-rails (= 2.5.0)
|
||||
ruby-debug
|
||||
shoulda
|
||||
spree_core!
|
||||
spree_digital!
|
||||
sqlite3-ruby
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2011 funkensturm.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
114
README.textile
Normal file
114
README.textile
Normal file
@@ -0,0 +1,114 @@
|
||||
h2. Spree Digital
|
||||
|
||||
This is a spree extension to enable downloadable products. The design goal is to keep it robust. It should survive future spree versions out of the box.
|
||||
|
||||
NOTE: This is still under development, but since things were falling into place, I thought I'd publish it.
|
||||
|
||||
h3. Introduction
|
||||
|
||||
The idea is simple. You attach a file to a Product (or a Variant of this Product) and when people buy it, they will receive a link via email where they can download it once. There are a few assumptions that spree_digital (currently) makes and it's important to be aware of them. These might change, but since I programmed digital_spree over night, this is what you get ;)
|
||||
|
||||
* The table structure of spree_core is not touched. Spree_digital lives parallel to spree_core and does not do any changes to your existing database, except adding two new tables.
|
||||
* The download links will be sent via email in the order confirmation (or "resend" from the admin section). The links do *not* appear in the order "overview" that the customer sees.
|
||||
* There should only be "live" payments. Once the order is checked-out, the download links will immediately be sent (i.e. in the order confirmation).
|
||||
* You should define a shipping method called "download" and it should be cost-free. Spree_digital will use that one when all products in the cart are digital
|
||||
* One may buy several items of the same digital product in one cart. The customer will simply receive several links by doing so. This allows customer's to legally purchase multiple copies of the same product and maybe give one away to a friend.
|
||||
* The links will only work 3 times (but we tell the customer that it works only once). After 24 hours the links are deactivated. This should keep you reasonably free from complaints of people who are not capable of appropriately clicking on a link. They can try a whole day long, 3 times per link. The file should really be downloadable for weird customers. I understand that this is a little bit security by obscurity, but it's better than other solutions that I've seen.
|
||||
* The file @views/order_mailer/confirm_email.text.erb@ is the only thing that should need customization. But since I assume you do that anyway, it doesn't hurt to do it when you're using spree_digital. The reason is that the download links are added to the confirmation email to the customer.
|
||||
* A purchased product can be downloaded even if you disable the product immediately. You would have to remove the attached file in your admin section to prevent people from downloading purchased products.
|
||||
|
||||
h3. Installation
|
||||
|
||||
Add something like this to your @Gemfile@ in your brand new Rails 3 application:
|
||||
|
||||
<pre>
|
||||
gem 'spree'
|
||||
gem 'spree_social', :git => 'git://github.com/funkensturm/spree_digital.git', :branch => 'master'
|
||||
</pre>
|
||||
|
||||
If you prefer to use specific versions, feel free to do so. This configuration, for instance, determines some versions manually:
|
||||
|
||||
<pre>
|
||||
gem 'rails', '= 3.0.5'
|
||||
gem 'spree', '= 0.50.0'
|
||||
gem 'spree_social', :git => 'git://github.com/funkensturm/spree_digital.git', :branch => 'master'
|
||||
</pre>
|
||||
|
||||
The following terminal commands are corresponding to the spree README at "github.com/spree/spree":http://github.com/spree/spree and are needed to bring up spree:
|
||||
|
||||
<pre>
|
||||
bundle install
|
||||
rails g spree:site
|
||||
rake spree:install
|
||||
rake db:migrate
|
||||
</pre>
|
||||
|
||||
Of course you may already have a spree application running. But *either way* you will have to run these two commands at this point:
|
||||
|
||||
<pre>
|
||||
rake spree_digital:install # This one will copy the migration file from spree_digital to your rails_root/db/migrate
|
||||
rake db:migrate
|
||||
</pre>
|
||||
|
||||
This should be it. You might want to populate your store with sample data like so:
|
||||
|
||||
<pre>
|
||||
rake spree_sample:install
|
||||
rake db:seed
|
||||
rake db:sample
|
||||
</pre>
|
||||
|
||||
h4. Important Notice
|
||||
|
||||
You should go to the spree admin section and create a shipping method that has the word @download@ somehow in its name (it should be cost-free, but it doesn't have to). It will be detected by spree_digital. Otherwise your customer will be forced to choose something like "UPS" even if they purchase only downloadable products.
|
||||
|
||||
h3. Usage
|
||||
|
||||
...
|
||||
|
||||
|
||||
h1. Developer Section
|
||||
|
||||
h3. Table Diagram
|
||||
|
||||
<img src="http://github.com/funkensturm/spree_digital/raw/master/doc/tables.png">
|
||||
|
||||
h3. Installation (for Developers)
|
||||
|
||||
Get the spree framework and spree_digital extension for it:
|
||||
|
||||
<pre>
|
||||
git clone git://github.com/spree/spree.git
|
||||
git clone git://github.com/funkensturm/spree_digital
|
||||
</pre>
|
||||
|
||||
Go into the spree directory and run the bundle command:
|
||||
|
||||
<pre>
|
||||
cd spree
|
||||
bundle install
|
||||
</pre>
|
||||
|
||||
Go into the spree_digital directory and do the same:
|
||||
|
||||
NOTE: At this point you may need to uncomment the stuff in the @Gemfile@ before you can start developing and testing!
|
||||
|
||||
<pre>
|
||||
cd spree_digital
|
||||
bundle install
|
||||
</pre>
|
||||
|
||||
Bring up the test application (you only need to do this whenever you fiddle around with the migrations) and then you can run the tests as you please.
|
||||
|
||||
<pre>
|
||||
rake test_app
|
||||
rake spec
|
||||
</pre>
|
||||
|
||||
This link may be very helpful to you: "http://github.com/spree/spree":http://github.com/spree/spree
|
||||
|
||||
h3. License
|
||||
|
||||
Copyright (c) 2011 funkensturm.
|
||||
Released under the MIT License
|
||||
See "LICENSE":LICENSE
|
||||
75
Rakefile
Normal file
75
Rakefile
Normal file
@@ -0,0 +1,75 @@
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/packagetask'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
gemfile = File.expand_path('../spec/test_app/Gemfile', __FILE__)
|
||||
if File.exists?(gemfile) && (%w(spec cucumber).include?(ARGV.first.to_s) || ARGV.size == 0)
|
||||
require 'bundler'
|
||||
ENV['BUNDLE_GEMFILE'] = gemfile
|
||||
Bundler.setup
|
||||
|
||||
require 'rspec'
|
||||
require 'rspec/core/rake_task'
|
||||
RSpec::Core::RakeTask.new
|
||||
|
||||
require 'cucumber/rake/task'
|
||||
Cucumber::Rake::Task.new do |t|
|
||||
t.cucumber_opts = %w{--format progress}
|
||||
end
|
||||
end
|
||||
|
||||
desc "Default Task"
|
||||
task :default => [:spec, :cucumber ]
|
||||
|
||||
spec = eval(File.read('spree_digital.gemspec'))
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
end
|
||||
|
||||
desc "Release to gemcutter"
|
||||
task :release => :package do
|
||||
require 'rake/gemcutter'
|
||||
Rake::Gemcutter::Tasks.new(spec).define
|
||||
Rake::Task['gem:push'].invoke
|
||||
end
|
||||
|
||||
desc "Default Task"
|
||||
task :default => [ :spec ]
|
||||
|
||||
desc "Regenerates a rails 3 app for testing"
|
||||
task :test_app do
|
||||
require '../spree/lib/generators/spree/test_app_generator'
|
||||
class SpreeDigitalTestAppGenerator < Spree::Generators::TestAppGenerator
|
||||
|
||||
def install_gems
|
||||
inside "test_app" do
|
||||
run 'rake spree_core:install'
|
||||
run 'rake spree_digital:install'
|
||||
end
|
||||
end
|
||||
|
||||
def migrate_db
|
||||
run_migrations
|
||||
end
|
||||
|
||||
protected
|
||||
def full_path_for_local_gems
|
||||
<<-gems
|
||||
gem 'spree_core', :path => \'#{File.join(File.dirname(__FILE__), "../spree/", "core")}\'
|
||||
gem 'spree_digital', :path => \'#{File.dirname(__FILE__)}\'
|
||||
gems
|
||||
end
|
||||
|
||||
end
|
||||
SpreeDigitalTestAppGenerator.start
|
||||
end
|
||||
|
||||
namespace :test_app do
|
||||
desc 'Rebuild test and cucumber databases'
|
||||
task :rebuild_dbs do
|
||||
system("cd spec/test_app && rake db:drop db:migrate RAILS_ENV=test && rake db:drop db:migrate RAILS_ENV=cucumber")
|
||||
end
|
||||
end
|
||||
7
Versionfile
Normal file
7
Versionfile
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is used to designate compatibilty with different versions of Spree
|
||||
# Please see http://spreecommerce.com/documentation/extensions.html#versionfile for details
|
||||
|
||||
# Currently this works with these both:
|
||||
|
||||
"0.60.x" => { :branch => "master" }
|
||||
"0.50.x" => { :branch => "master" }
|
||||
12
app/controllers/admin/digitals_controller.rb
Normal file
12
app/controllers/admin/digitals_controller.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class Admin::DigitalsController < Admin::BaseController
|
||||
|
||||
resource_controller
|
||||
|
||||
index.before do
|
||||
@product = Product.find_by_permalink(params[:product_id])
|
||||
end
|
||||
|
||||
create.wants.html { redirect_to admin_product_digitals_url(@product) }
|
||||
destroy.wants.html { redirect_to admin_product_digitals_url(@product) }
|
||||
|
||||
end
|
||||
16
app/controllers/digitals_controller.rb
Normal file
16
app/controllers/digitals_controller.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class DigitalsController < Spree::BaseController
|
||||
|
||||
ssl_required :show
|
||||
|
||||
def show
|
||||
link = DigitalLink.find_by_secret(params[:secret])
|
||||
if link.present? and link.digital.attachment.present?
|
||||
attachment = link.digital.attachment
|
||||
if link.authorize! and File.file?(attachment.path)
|
||||
send_file attachment.path :filename => attachment.original_filename, :type => attachment.content_type and return
|
||||
end
|
||||
end
|
||||
render :unauthorized
|
||||
end
|
||||
|
||||
end
|
||||
10
app/models/digital.rb
Normal file
10
app/models/digital.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
class Digital < ActiveRecord::Base
|
||||
|
||||
belongs_to :variant
|
||||
has_many :digital_links, :dependent => :destroy
|
||||
|
||||
has_attached_file :attachment, :path => ":rails_root/private/digitals/:id/:basename.:extension"
|
||||
|
||||
# TODO: Limit the attachment to one single file. Paperclip supports many by default :/
|
||||
|
||||
end
|
||||
27
app/models/digital_link.rb
Normal file
27
app/models/digital_link.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
class DigitalLink < ActiveRecord::Base
|
||||
|
||||
belongs_to :digital
|
||||
belongs_to :line_item
|
||||
|
||||
before_validation :set_defaults, :on => :create
|
||||
|
||||
# Can this link stil be used? It is valid if it's less than 24 hours old and was not accessed more than 3 times
|
||||
def authorizable?
|
||||
self.created_at > 1.day.ago and self.access_counter < 3
|
||||
end
|
||||
|
||||
# This method should be called when a download is initiated.
|
||||
# It returns +true+ or +false+ depending on whether the authorization is granted.
|
||||
def authorize!
|
||||
authorizable? && increment!(:access_counter) ? true : false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Populating the secret automatically and zero'ing the access_counter (otherwise it might turn out to be NULL)
|
||||
def set_defaults
|
||||
self.secret = SecureRandom.hex(15)
|
||||
self.access_counter = 0
|
||||
end
|
||||
|
||||
end
|
||||
22
app/models/line_item_decorator.rb
Normal file
22
app/models/line_item_decorator.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
LineItem.class_eval do
|
||||
|
||||
has_many :digital_links
|
||||
|
||||
after_save :create_digital_links, :if => :digital?
|
||||
|
||||
# Is this item digital?
|
||||
def digital?
|
||||
variant.digital?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Create the download link for this item if it is digital.
|
||||
def create_digital_links
|
||||
digital_links.delete_all
|
||||
self.quantity.times do
|
||||
digital_links.create!(:digital => variant.digital)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
20
app/models/order_decorator.rb
Normal file
20
app/models/order_decorator.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
Order.class_eval do
|
||||
|
||||
# Are all products/variants of this Order to be downloaded by the customer?
|
||||
def digital?
|
||||
line_items.map { |item| return false unless item.digital? }
|
||||
true
|
||||
end
|
||||
|
||||
# Determine which method to use for shipping of digital products.
|
||||
def digital_shipping_method
|
||||
rates = rate_hash
|
||||
# If there is a shipping method has "Download" in its name then we take that one.
|
||||
rates.each { |rate| return rate if rate[:name].downcase.include?('download') }
|
||||
# Other than that, we take the first one that we find that doesn't cost anything.
|
||||
rates.each { |rate| return rate if rate[:cost] == 0 }
|
||||
# Well, at this point we have a problem. No shipping method is cost-free or called "download".
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
20
app/models/variant_decorator.rb
Normal file
20
app/models/variant_decorator.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
Variant.class_eval do
|
||||
|
||||
has_one :digital, :dependent => :destroy
|
||||
after_save :destroy_digital, :if => :deleted?
|
||||
|
||||
# Is this variant to be downloaded by the customer?
|
||||
def digital?
|
||||
digital.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Spree never deleted Digitals, that's why ":dependent => :destroy" won't work on Digital.
|
||||
# We need to delete the Digital manually here as soon as the Variant is nullified.
|
||||
# Otherwise you'll have orphan Digitals (and their attached files!) associated with unused Variants.
|
||||
def destroy_digital
|
||||
digital.destroy
|
||||
end
|
||||
|
||||
end
|
||||
5
app/views/admin/digitals/_digital.html.erb
Normal file
5
app/views/admin/digitals/_digital.html.erb
Normal file
@@ -0,0 +1,5 @@
|
||||
<% if digital.attachment_file_name.present? %>
|
||||
<%= digital.attachment_file_name %> (<%= number_to_human_size(digital.attachment_file_size) %>)
|
||||
<% else %>
|
||||
<%=t 'broken_file' %>
|
||||
<% end %>
|
||||
35
app/views/admin/digitals/_form.html.erb
Normal file
35
app/views/admin/digitals/_form.html.erb
Normal file
@@ -0,0 +1,35 @@
|
||||
<div class="yui-g">
|
||||
<div class="yui-u first">
|
||||
<%= form_for(:digital, :url => { :controller => 'digitals', :action => 'create' }, :html => { :multipart => true }) do |f| %>
|
||||
<fieldset>
|
||||
<legend><%= Variant.human_name %> "<%= variant.options_text %>"</legend>
|
||||
|
||||
<%= f.field_container :current_file do %>
|
||||
<strong><%=t 'current_file' %>:</strong><br/>
|
||||
<% if variant.digital? %>
|
||||
<%= render variant.digital %><%= %>
|
||||
<% else %>
|
||||
<%=t 'none' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<p class="form-buttons">
|
||||
<% if variant.digital? %>
|
||||
<%= link_to t("delete_file"), admin_product_digital_url(:id => variant.digital.id), :confirm => t('delete_file_cofirmation', :filename => variant.digital.attachment_file_name), :method => :delete %>
|
||||
<% else %>
|
||||
|
||||
<%= f.field_container :file do %>
|
||||
<%= f.label :file, t("new_file") %> <span class="required">*</span><br/>
|
||||
<%= f.file_field :attachment %>
|
||||
<% end %>
|
||||
|
||||
<%= hidden_field_tag 'digital[variant_id]', variant.id %>
|
||||
|
||||
<%= button t("upload") %>
|
||||
<% end %>
|
||||
</p>
|
||||
|
||||
</fieldset>
|
||||
<% end %>
|
||||
</div>
|
||||
</div><br/>
|
||||
20
app/views/admin/digitals/index.html.erb
Normal file
20
app/views/admin/digitals/index.html.erb
Normal file
@@ -0,0 +1,20 @@
|
||||
<%= render :partial => 'admin/shared/product_sub_menu' %>
|
||||
<%= render :partial => 'admin/shared/product_tabs', :locals => {:current => "Digital Versions"} %>
|
||||
|
||||
<% if @product.has_variants? %>
|
||||
|
||||
<% for variant in @product.variants do %>
|
||||
<%= render 'form', :variant => variant %>
|
||||
<% end %>
|
||||
|
||||
<% else %>
|
||||
This product has no variants.
|
||||
|
||||
<% if @product.master.digital? %>
|
||||
A digital version of this product currently exists:
|
||||
<%= render @product.master.digital %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'form', :variant => @product.master %>
|
||||
|
||||
<% end %>
|
||||
5
app/views/checkout/_delivery.html.erb
Normal file
5
app/views/checkout/_delivery.html.erb
Normal file
@@ -0,0 +1,5 @@
|
||||
<%#
|
||||
This is basically a switch that will render the appropriate "delivery choice" partial.
|
||||
When there are no physical items at all, there should be no different shipping methods to choose.
|
||||
%>
|
||||
<%= render @order.digital? && @order.digital_shipping_method.present? ? 'delivery_digital' : 'delivery_original' %>
|
||||
12
app/views/checkout/_delivery_digital.html.erb
Normal file
12
app/views/checkout/_delivery_digital.html.erb
Normal file
@@ -0,0 +1,12 @@
|
||||
<fieldset id='shipping_method'>
|
||||
<legend><%= t("shipping_method") %></legend>
|
||||
|
||||
<%= radio_button :order, :shipping_method_id, @order.digital_shipping_method[:id] %>
|
||||
<%==t 'digital_shipping', :email => current_user.email %> (<%= number_to_currency @order.digital_shipping_method[:cost] %>)
|
||||
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="continue button primary" value="<%=t("save_and_continue") %>"/>
|
||||
</div>
|
||||
32
app/views/checkout/_delivery_original.html.erb
Normal file
32
app/views/checkout/_delivery_original.html.erb
Normal file
@@ -0,0 +1,32 @@
|
||||
<%#
|
||||
NOTE: The code below is an exact duplication of `spree_core/views/checkout/_delivery.html.erb´
|
||||
We overwrote the original so we retain access to it by cloning it here.
|
||||
Feel free to update the code below according to the original if needed!
|
||||
%>
|
||||
|
||||
<fieldset id='shipping_method'>
|
||||
<legend><%= t("shipping_method") %></legend>
|
||||
<div class="inner">
|
||||
<div id="methods">
|
||||
<p class="field radios">
|
||||
<% @order.rate_hash.each do |shipping_method| %>
|
||||
<% next if shipping_method[:id] == @order.digital_shipping_method[:id] %>
|
||||
<label>
|
||||
<%= radio_button(:order, :shipping_method_id, shipping_method[:id]) %>
|
||||
<%= shipping_method[:name] %> <%= number_to_currency shipping_method[:cost] %>
|
||||
</label><br />
|
||||
<% end %>
|
||||
</p>
|
||||
</div>
|
||||
<% if Spree::Config[:shipping_instructions] && @order.rate_hash.present? %>
|
||||
<p id="minstrs">
|
||||
<%= form.label :special_instructions, t("shipping_instructions") %><br />
|
||||
<%= form.text_area :special_instructions, :cols => 40, :rows => 7 %>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-buttons">
|
||||
<input type="submit" class="continue button primary" value="<%=t("save_and_continue") %>"/>
|
||||
</div>
|
||||
36
app/views/order_mailer/confirm_email.text.erb
Normal file
36
app/views/order_mailer/confirm_email.text.erb
Normal file
@@ -0,0 +1,36 @@
|
||||
Dear Customer,
|
||||
|
||||
Please review and retain the following order information for your records.
|
||||
|
||||
============================================================
|
||||
Order Summary
|
||||
============================================================
|
||||
<% for item in @order.line_items %>
|
||||
<%= item.variant.sku %> <%=item.variant.product.name%> <%= variant_options(item.variant) %> (<%=item.quantity%>) @ <%= number_to_currency item.price %> = <%= number_to_currency(item.price * item.quantity) %>
|
||||
<% end %>
|
||||
============================================================
|
||||
Subtotal: <%= number_to_currency @order.item_total %>
|
||||
<% @order.adjustments.each do |adjustment| %>
|
||||
<%= "#{adjustment.label}: #{number_to_currency adjustment.amount}"%>
|
||||
<% end %>
|
||||
Order Total: <%= number_to_currency @order.total %>
|
||||
|
||||
============================================================
|
||||
Download links for digital products
|
||||
============================================================
|
||||
|
||||
ATTENTION! Each link will only work a SINGLE TIME!
|
||||
Also, they will only work WITHIN 24 HOURS!
|
||||
|
||||
<% for item in @order.line_items %>
|
||||
<% if item.digital? %>
|
||||
<%= item.variant.name %>:
|
||||
<% for link in item.digital_links %>
|
||||
<%= digital_url :protocol => 'https', :host => "yourdomainxygoeshere.com", :secret => link.secret %>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
<% end %>
|
||||
============================================================
|
||||
|
||||
Thank you for your business.
|
||||
12
config/locales/en.yml
Normal file
12
config/locales/en.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
en:
|
||||
digital_versions: Digital Versions
|
||||
|
||||
current_file: Current File
|
||||
new_file: New File
|
||||
upload: Upload
|
||||
delete_file: Delete this file
|
||||
broken_file: Warning! this file is broken
|
||||
delete_file_cofirmation: Are you sure you want to delete the file %{filename}?
|
||||
|
||||
digital_shipping: Delivery per email to <strong>%{email}</strong>
|
||||
|
||||
11
config/routes.rb
Normal file
11
config/routes.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
Rails.application.routes.draw do
|
||||
|
||||
namespace :admin do
|
||||
resources :products do
|
||||
resources :digitals
|
||||
end
|
||||
end
|
||||
|
||||
get '/digital/:secret', :to => 'digitals#show', :via => :get, :as => 'digital', :constraints => { :secret => /[a-zA-Z0-9]{30}/ }
|
||||
|
||||
end
|
||||
30
db/migrate/20110410134726_create_digitals.rb
Normal file
30
db/migrate/20110410134726_create_digitals.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
class CreateDigitals < ActiveRecord::Migration
|
||||
|
||||
def self.up
|
||||
create_table :digitals do |t|
|
||||
t.integer :variant_id
|
||||
t.string :attachment_file_name
|
||||
t.string :attachment_content_type
|
||||
t.integer :attachment_file_size
|
||||
t.timestamps
|
||||
end
|
||||
add_index :digitals, :variant_id
|
||||
|
||||
create_table :digital_links, :force => true do |t|
|
||||
t.integer :digital_id
|
||||
t.integer :line_item_id
|
||||
t.string :secret
|
||||
t.integer :access_counter
|
||||
t.timestamps
|
||||
end
|
||||
add_index :digital_links, :digital_id
|
||||
add_index :digital_links, :line_item_id
|
||||
add_index :digital_links, :secret
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :digitals
|
||||
drop_table :digital_links
|
||||
end
|
||||
|
||||
end
|
||||
BIN
doc/tables.png
Normal file
BIN
doc/tables.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
17
lib/spree_digital.rb
Normal file
17
lib/spree_digital.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
require 'spree_core'
|
||||
require 'spree_digital_hooks'
|
||||
|
||||
module SpreeDigital
|
||||
class Engine < Rails::Engine
|
||||
|
||||
config.autoload_paths += %W(#{config.root}/lib)
|
||||
|
||||
def self.activate
|
||||
Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator*.rb")) do |c|
|
||||
Rails.env.production? ? require(c) : load(c)
|
||||
end
|
||||
end
|
||||
|
||||
config.to_prepare &method(:activate).to_proc
|
||||
end
|
||||
end
|
||||
11
lib/spree_digital_hooks.rb
Normal file
11
lib/spree_digital_hooks.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class SpreeDigitalHooks < Spree::ThemeSupport::HookListener
|
||||
|
||||
insert_after :admin_product_tabs do
|
||||
<<-END
|
||||
<li<%== ' class="active"' if current == "Digital Versions" %>>
|
||||
<%= link_to t("digital_versions"), admin_product_digitals_path(@product) %>
|
||||
</li>
|
||||
END
|
||||
end
|
||||
|
||||
end
|
||||
18
lib/tasks/install.rake
Normal file
18
lib/tasks/install.rake
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace :spree_digital do
|
||||
|
||||
desc "Copies all migrations (NOTE: This will be obsolete with Rails 3.1)"
|
||||
task :install do
|
||||
Rake::Task['spree_digital:install:migrations'].invoke
|
||||
end
|
||||
|
||||
namespace :install do
|
||||
|
||||
desc "Copies all migrations (NOTE: This will be obsolete with Rails 3.1)"
|
||||
task :migrations do
|
||||
source = File.join(File.dirname(__FILE__), '..', '..', 'db')
|
||||
destination = File.join(Rails.root, 'db')
|
||||
Spree::FileUtilz.mirror_files(source, destination)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
1
lib/tasks/spree_digital.rake
Normal file
1
lib/tasks/spree_digital.rake
Normal file
@@ -0,0 +1 @@
|
||||
# add custom rake tasks here
|
||||
6
spec/factories.rb
Normal file
6
spec/factories.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
require 'factory_girl'
|
||||
|
||||
Dir["#{File.dirname(__FILE__)}/factories/**"].each do |f|
|
||||
fp = File.expand_path(f)
|
||||
require fp
|
||||
end
|
||||
3
spec/factories/digital_factory.rb
Normal file
3
spec/factories/digital_factory.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
Factory.define :digital do |f|
|
||||
f.variant { |p| p.association(:variant) }
|
||||
end
|
||||
4
spec/factories/digital_link_factory.rb
Normal file
4
spec/factories/digital_link_factory.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
Factory.define :digital_link do |f|
|
||||
f.digital { |p| p.association(:digital) }
|
||||
f.line_item { |p| p.association(:line_item) }
|
||||
end
|
||||
98
spec/models/digital_link_spec.rb
Normal file
98
spec/models/digital_link_spec.rb
Normal file
@@ -0,0 +1,98 @@
|
||||
require File.dirname(__FILE__) + '/../spec_helper'
|
||||
|
||||
describe DigitalLink do
|
||||
|
||||
context 'validation' do
|
||||
it { should belong_to(:digital) }
|
||||
it { should belong_to(:line_item) }
|
||||
it { should have_valid_factory(:digital_link) }
|
||||
end
|
||||
|
||||
context "#create" do
|
||||
it "should have an appropriately long secret" do
|
||||
Factory(:digital_link).secret.length.should == 30
|
||||
end
|
||||
|
||||
it "should have the access counter being an Integer on zero" do
|
||||
Factory(:digital_link).access_counter.should == 0
|
||||
end
|
||||
end
|
||||
|
||||
context "#update" do
|
||||
it "should not change the secret when updated" do
|
||||
digital_link = Factory(:digital_link)
|
||||
secret = digital_link.secret
|
||||
digital_link.increment(:access_counter).save
|
||||
digital_link.secret.should == secret
|
||||
end
|
||||
|
||||
it "should enforce to have an associated digital" do
|
||||
link = Factory(:digital_link)
|
||||
lambda { link.update_attributes!(:digital => nil) }.should raise_error(ActiveRecord::RecordInvalid)
|
||||
end
|
||||
|
||||
it "should not allow an empty or too short secret" do
|
||||
link = Factory(:digital_link)
|
||||
lambda { link.update_attributes!(:secret => nil) }.should raise_error(ActiveRecord::RecordInvalid)
|
||||
lambda { link.update_attributes!(:secret => 'x' * 25) }.should raise_error(ActiveRecord::RecordInvalid)
|
||||
end
|
||||
end
|
||||
|
||||
#context "authorization" do
|
||||
# it "should increment the counter using #authorize!" do
|
||||
# link = Factory(:digital_link)
|
||||
# link.authorize!
|
||||
# link.access_counter.should == 1
|
||||
# end
|
||||
#
|
||||
# it "should not be #authorized? when the access_counter is too high" do
|
||||
# link = Factory(:digital_link)
|
||||
# link.stub(:access_counter => 2)
|
||||
# link.authorized?.should be_true
|
||||
# link.stub(:access_counter => 3)
|
||||
# link.authorized?.should be_false
|
||||
# end
|
||||
#
|
||||
# it "should not be #authorize! when the access_counter is too high" do
|
||||
# link = Factory(:digital_link)
|
||||
# link.stub(:access_counter => 2)
|
||||
# link.authorize!.should be_true
|
||||
# link.stub(:access_counter => 3)
|
||||
# link.authorize!.should be_false
|
||||
# end
|
||||
#
|
||||
# it "should not be #authorized? when the created_at date is too far in the past" do
|
||||
# link = Factory(:digital_link)
|
||||
# link.authorized?.should be_true
|
||||
# link.stub(:created_at => 23.hours.ago)
|
||||
# link.authorized?.should be_true
|
||||
# link.stub(:created_at => 25.hours.ago)
|
||||
# link.authorized?.should be_false
|
||||
# end
|
||||
#
|
||||
# it "should not be #authorize! when the created_at date is too far in the past" do
|
||||
# link = Factory(:digital_link)
|
||||
# link.authorize!.should be_true
|
||||
# link.stub(:created_at => 23.hours.ago)
|
||||
# link.authorize!.should be_true
|
||||
# link.stub(:created_at => 25.hours.ago)
|
||||
# link.authorize!.should be_false
|
||||
# end
|
||||
#
|
||||
# it "should not be #authorized? when both access_counter and created_at are invalid" do
|
||||
# link = Factory(:digital_link)
|
||||
# link.authorized?.should be_true
|
||||
# link.stub(:access_counter => 3, :created_at => 25.hours.ago)
|
||||
# link.authorized?.should be_false
|
||||
# end
|
||||
#
|
||||
# it "should not be #authorize! when both access_counter and created_at are invalid" do
|
||||
# link = Factory(:digital_link)
|
||||
# link.authorize!.should be_true
|
||||
# link.stub(:access_counter => 3, :created_at => 25.hours.ago)
|
||||
# link.authorize!.should be_false
|
||||
# end
|
||||
#end
|
||||
|
||||
end
|
||||
|
||||
25
spec/models/digital_spec.rb
Normal file
25
spec/models/digital_spec.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
require File.dirname(__FILE__) + '/../spec_helper'
|
||||
|
||||
describe Digital do
|
||||
|
||||
context 'validation' do
|
||||
it { should belong_to(:variant) }
|
||||
it { should have_valid_factory(:digital) }
|
||||
end
|
||||
|
||||
context "#create" do
|
||||
|
||||
end
|
||||
|
||||
context "#destroy" do
|
||||
#it "should destroy associated digital_links" do
|
||||
# digital = Factory(:digital)
|
||||
# 3.times { digital.digital_links.create! :order => Factory(:order) }
|
||||
# DigitalLink.count.should == 3
|
||||
# digital.destroy
|
||||
# DigitalLink.count.should == 0
|
||||
#end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
33
spec/models/line_item_spec.rb
Normal file
33
spec/models/line_item_spec.rb
Normal file
@@ -0,0 +1,33 @@
|
||||
require File.dirname(__FILE__) + '/../spec_helper'
|
||||
|
||||
describe LineItem do
|
||||
|
||||
context "#save" do
|
||||
it "should create one link for a single digital Variant" do
|
||||
digital_variant = Factory(:variant, :digital => Factory(:digital))
|
||||
line_item = Factory(:line_item, :variant => digital_variant)
|
||||
links = digital_variant.digital.digital_links
|
||||
links.all.size.should == 1
|
||||
links.first.line_item.should == line_item
|
||||
end
|
||||
|
||||
it "should create a link for each quantity of a digital Variant, even when quantity changes later" do
|
||||
digital_variant = Factory(:variant, :digital => Factory(:digital))
|
||||
line_item = Factory(:line_item, :variant => digital_variant, :quantity => 5)
|
||||
links = digital_variant.digital.digital_links
|
||||
links.all.size.should == 5
|
||||
links.each { |link| link.line_item.should == line_item }
|
||||
# quantity update
|
||||
line_item.quantity = 8
|
||||
line_item.save
|
||||
links = digital_variant.digital.digital_links
|
||||
links.all.size.should == 8
|
||||
links.each { |link| link.line_item.should == line_item }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
54
spec/models/order_spec.rb
Normal file
54
spec/models/order_spec.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
require File.dirname(__FILE__) + '/../spec_helper'
|
||||
|
||||
describe Order do
|
||||
|
||||
context 'validation' do
|
||||
it { should have_valid_factory(:order) }
|
||||
end
|
||||
|
||||
context "#add_variant" do
|
||||
it "should add digital Variants of quantity 1 to an order" do
|
||||
order = Factory(:order)
|
||||
order.add_variant variant1 = Factory(:variant, :digital => Factory(:digital))
|
||||
order.add_variant variant2 = Factory(:variant, :digital => Factory(:digital))
|
||||
order.add_variant variant3 = Factory(:variant, :digital => Factory(:digital))
|
||||
order.line_items.first.variant.should == variant1
|
||||
order.line_items.second.variant.should == variant2
|
||||
order.line_items.third.variant.should == variant3
|
||||
end
|
||||
|
||||
it "should handle quantity higher than 1 when adding one specific digital Variant" do
|
||||
order = Factory(:order)
|
||||
digital_variant = Factory(:variant, :digital => Factory(:digital))
|
||||
order.add_variant digital_variant, 3
|
||||
order.line_items.first.quantity.should == 3
|
||||
order.add_variant digital_variant, 2
|
||||
order.line_items.first.quantity.should == 5
|
||||
end
|
||||
end
|
||||
|
||||
context "line_item analysis" do
|
||||
it "should understand that all products are digital" do
|
||||
order = Factory(:order)
|
||||
3.times do
|
||||
order.add_variant Factory(:variant, :digital => Factory(:digital))
|
||||
end
|
||||
order.digital?.should be_true
|
||||
order.add_variant Factory(:variant, :digital => Factory(:digital)), 4
|
||||
order.digital?.should be_true
|
||||
end
|
||||
|
||||
it "should understand that not all products are digital" do
|
||||
order = Factory(:order)
|
||||
3.times do
|
||||
order.add_variant Factory(:variant, :digital => Factory(:digital))
|
||||
end
|
||||
order.add_variant Factory(:variant) # this is the analog product
|
||||
order.digital?.should be_false
|
||||
order.add_variant Factory(:variant, :digital => Factory(:digital)), 4
|
||||
order.digital?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
61
spec/spec_helper.rb
Normal file
61
spec/spec_helper.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
require File.expand_path("../factories", __FILE__)
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
# NOTE: The rest of file is the same as spree/core/spec/spec_helper.rb #
|
||||
########################################################################
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# This file is copied to ~/spec when you run 'ruby script/generate rspec'
|
||||
# from the project root directory.
|
||||
ENV["RAILS_ENV"] ||= 'test'
|
||||
require File.expand_path("../test_app/config/environment", __FILE__)
|
||||
require 'rspec/rails'
|
||||
|
||||
# Requires supporting files with custom matchers and macros, etc,
|
||||
# in ./support/ and its subdirectories.
|
||||
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
||||
|
||||
require 'spree_core/testing_support/factories'
|
||||
|
||||
RSpec.configure do |config|
|
||||
# == Mock Framework
|
||||
#
|
||||
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
||||
#
|
||||
# config.mock_with :mocha
|
||||
# config.mock_with :flexmock
|
||||
# config.mock_with :rr
|
||||
config.mock_with :rspec
|
||||
|
||||
config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
||||
|
||||
#config.include Devise::TestHelpers, :type => :controller
|
||||
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
||||
# examples within a transaction, comment the following line or assign false
|
||||
# instead of true.
|
||||
config.use_transactional_fixtures = true
|
||||
end
|
||||
|
||||
@configuration ||= AppConfiguration.find_or_create_by_name("Default configuration")
|
||||
|
||||
PAYMENT_STATES = Payment.state_machine.states.keys unless defined? PAYMENT_STATES
|
||||
SHIPMENT_STATES = Shipment.state_machine.states.keys unless defined? SHIPMENT_STATES
|
||||
ORDER_STATES = Order.state_machine.states.keys unless defined? ORDER_STATES
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# context "factory" do
|
||||
# it { should have_valid_factory(:address) }
|
||||
# end
|
||||
RSpec::Matchers.define :have_valid_factory do |factory_name|
|
||||
match do |model|
|
||||
Factory(factory_name).new_record?.should be_false
|
||||
end
|
||||
end
|
||||
15
spree_digital.gemspec
Normal file
15
spree_digital.gemspec
Normal file
@@ -0,0 +1,15 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.name = 'spree_digital'
|
||||
s.version = '0.60.99'
|
||||
s.summary = ''
|
||||
s.description = 'This gem is supposed to be used in connection with spree_core. It was tested with spree_core 0.66.99 but it might work with newer versions as well.'
|
||||
s.author = 'funkensturm.'
|
||||
s.homepage = 'http://www.funkensturm.com'
|
||||
s.files = `git ls-files`.split("\n")
|
||||
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
||||
s.require_path = 'lib'
|
||||
s.requirements << 'none'
|
||||
s.required_ruby_version = '>= 1.8.7'
|
||||
s.add_dependency('spree_core')
|
||||
end
|
||||
48
spree_digital.tmproj
Normal file
48
spree_digital.tmproj
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>currentDocument</key>
|
||||
<string>Gemfile</string>
|
||||
<key>documents</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>expanded</key>
|
||||
<true/>
|
||||
<key>name</key>
|
||||
<string>spree_digital</string>
|
||||
<key>regexFolderFilter</key>
|
||||
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
|
||||
<key>sourceDirectory</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>fileHierarchyDrawerWidth</key>
|
||||
<integer>262</integer>
|
||||
<key>metaData</key>
|
||||
<dict>
|
||||
<key>Gemfile</key>
|
||||
<dict>
|
||||
<key>caret</key>
|
||||
<dict>
|
||||
<key>column</key>
|
||||
<integer>19</integer>
|
||||
<key>line</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
<key>firstVisibleColumn</key>
|
||||
<integer>0</integer>
|
||||
<key>firstVisibleLine</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>openDocuments</key>
|
||||
<array>
|
||||
<string>Gemfile</string>
|
||||
</array>
|
||||
<key>showFileHierarchyDrawer</key>
|
||||
<true/>
|
||||
<key>windowFrame</key>
|
||||
<string>{{8, 81}, {992, 680}}</string>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user