90 Commits

Author SHA1 Message Date
efb07409e9 Fix some translations. Improve style for menu, fix layout in digitals page. Working version. 2013-05-26 12:57:21 +01:00
868fddd7bc only two tests need a fix. Starting to clear deprecation warnings. 2013-05-24 15:48:54 +01:00
f3c13a7175 Port halo/spree_digital to Spree version 2.0.0. Move factories to the correct place (factories.rb) so it can be used/overriden by other modules. 2013-05-24 14:06:15 +01:00
Jeff Dutil
7dabf1a520 Support Spree 1.3.x, add default unauthorized page, and organize locales. 2013-03-10 09:46:06 -04:00
Michael Bianco
52e7009c17 Running tests, and fixing breaking changes, for 1.2.3 2013-03-10 09:35:48 -04:00
Michael Bianco
8d44a25199 Note about improvement to create_digital_links 2013-03-10 09:35:48 -04:00
Michael Bianco
e6f8d6b4cc Merge pull request #35 from nitsujri/master
Fix for digital_controller throwing an error with unavailable.html.erb
2013-03-10 06:29:03 -07:00
Justin Louie
1d9fab3942 Fixing controller for current_order bug
unavailable.html.erb throws an error missing current_order

https://groups.google.com/forum/?fromgroups=#!topic/spree-user/mbEVVManQY8
2013-03-10 14:08:04 +08:00
Michael Bianco
f6d5812379 Merge pull request #32 from jdutil/patch-1
Fix README link.
2013-02-12 13:06:25 -08:00
Jeff Dutil
e2f33cd006 Fix README link.
Also add shell syntax highlighting.
2013-02-09 14:01:39 -05:00
Michael Bianco
9360c63503 Fixing deface name typo 2013-01-28 10:01:39 -05:00
Dave Mitchell
b381ede8e1 Convert 1.9 to 1.8 syntax. Fixes #23 2013-01-08 13:41:07 -05:00
Michael Bianco
37fe465dd9 Updating documentation. Adding nginx send_file info 2012-11-06 11:34:28 -05:00
Michael Bianco
23317eb0fb Adding button to order admin to reset digital downloads
* Resets expire time
* Resets download count
2012-11-01 17:36:47 -04:00
Michael Bianco
4dc2d9d54f Minor improvement to digital factory 2012-10-29 13:05:35 -04:00
Michael Bianco
446a23ce3a Adding Spree::Order#reset_digital_links! to ease in handling customer support
Hope to add this as a button to the admin soon.
2012-10-29 13:04:39 -04:00
Michael Bianco
fdd8536b5b Tests passing on 1.2 2012-10-29 12:55:37 -04:00
Michael Bianco
7f0de10150 Versionfile typo fix 2012-10-29 12:16:32 -04:00
Michael Bianco
bbdabd8c53 Version bump and version file update 2012-10-29 12:15:07 -04:00
Michael Bianco
b59415efcf Code, gemspec, and Gemfile cleanup 2012-10-29 12:11:20 -04:00
Michael Bianco
949bd9c6fa Merge branch 'delete-protection' into funkensturm/master
* delete-protection:
  Added config to optionally disable deleting digitals on variant deletion
  Revert "Remove delete digitals on soft delete of variant"
  Adding failing variant spec
  Fixed broken Digital#destroy spec
  Remove delete digitals on soft delete of variant
2012-10-29 11:50:23 -04:00
Michael Bianco
d1536aa741 Added config to optionally disable deleting digitals on variant deletion 2012-10-29 11:43:47 -04:00
Michael Bianco
c5b1b87617 Revert "Remove delete digitals on soft delete of variant"
This reverts commit d23462fadb.
2012-10-29 11:10:06 -04:00
Michael Bianco
aa71a13c0d Adding failing variant spec 2012-10-29 11:09:41 -04:00
Michael Bianco
6f2ad538a1 Fixed broken Digital#destroy spec 2012-10-29 10:53:08 -04:00
Michael Bianco
c13292de2f Merge pull request #30 from tommyblue/master
Italian locale
2012-09-12 03:17:47 -07:00
Tommaso Visconti
299bd97217 Italian locale 2012-09-12 12:14:09 +02:00
funkensturm
359867eddd Merge pull request #28 from iloveitaly/additional-tests
Additional Tests
2012-08-08 06:58:11 -07:00
Michael Bianco
d23462fadb Remove delete digitals on soft delete of variant 2012-08-07 11:18:12 -04:00
Michael Bianco
156f54e909 Adding specs for deletion/destroy on Digital and DigitalLink 2012-08-07 11:16:24 -04:00
Michael Bianco
7839238026 Controller tests passing 2012-08-07 10:55:05 -04:00
Chris Lindensmith
ae106dd280 fix admin display of digital products
copied from funkensturm's fix.
2012-08-07 10:50:17 -04:00
Michael Bianco
c997f03bc1 Adding initial controller specs. Both failing 2012-08-07 10:48:36 -04:00
funkensturm
c6d1aa0efc Merge pull request #21 from iloveitaly/spree-1.1
Digitals#show Logic Tweak
2012-07-30 08:46:43 -07:00
Michael Bianco
27ca264d23 Fixed variant deletion bug 2012-07-15 23:14:25 -03:00
Michael Bianco
95e9b97abe Spree 1.1.2 compat. Adding specs for digital shipping 2012-07-09 13:32:45 -04:00
Michael Bianco
cdbc636e79 Don't authorize! until we know the digital file exists 2012-07-03 13:21:33 -04:00
Michael Bianco
f8da0db2b7 Adding note about unauthorized.html.erb 2012-07-03 10:32:51 -04:00
funkensturm
49ac794ba7 Merge pull request #20 from GeekOnCoffee/patch-1
readme migration command fixes
2012-06-27 11:49:23 -07:00
funkensturm
82d4378699 Merge pull request #19 from iloveitaly/spree-1.1
Refactoring digital shipping calculation and display
2012-06-27 11:48:21 -07:00
Michael Bianco
2c7e6adc47 Missing translation 2012-06-27 13:03:36 -04:00
Michael Bianco
061b6e2e48 Adding download buttons to invoice (orders#show) 2012-06-27 09:41:22 -04:00
Andrew Hooker
547185baa2 readme migration command fixes 2012-06-24 22:04:41 -03:00
Michael Bianco
049770056d Refactoring digital shipping calculation and display
* removed the deface override that modified the checkout/_delivery partial
* created custom digital delivery calculator
* removed digital_shipping_method from Spree::Order. Moved logic to available_shipping_methods monkey patch

The main goal was to eliminate the shipping method detection logic from the _delivery partial.
This was a bit of a hack all along and didn't really present the correct information to the underlying
order logic (i.e. rate_hash.blank? would return true when there was no shipping options available).
This should be more future proof and make overriding another aspect of the delivery process easier.
2012-06-23 19:29:21 -04:00
funkensturm
b4a4135415 Merge pull request #18 from iloveitaly/spree-1.1
Multiple Digital Downloads for a Single Variant
2012-06-23 05:47:28 -07:00
Michael Bianco
71340444c1 Tests are passing 2012-06-18 16:58:24 -04:00
Michael Bianco
12281630d1 Allow multiple digital files to be attached to a single variant.
Useful for multi-format ebook downloads
2012-06-18 16:54:12 -04:00
funkensturm
3b9ae9c297 Merge pull request #17 from iloveitaly/spree-1.1
Spree 1.1 Compatibility
2012-06-08 11:40:44 -07:00
Michael Bianco
094b648e2f Versionfile updates 2012-06-07 10:55:48 -04:00
Michael Bianco
346322b8d3 Spec helper typo. Tests are passing. 2012-06-07 10:54:22 -04:00
Michael Bianco
2a0d165d93 Merge branch 'trahfo/master' into spree-1.1
* trahfo/master:
  specify available attributes for update
  specify available attributes for update
  moved partial into other directory
  specify available attributes for update
  let's see if it also works with spree 1.1.0

Conflicts:
	spree_digital.gemspec
2012-06-07 10:52:43 -04:00
Michael Bianco
5cfa1175af Updated testing to use spree 1.1 and new factorygirl syntax 2012-06-07 10:50:40 -04:00
Jakob
1fd0cd2916 specify available attributes for update 2012-05-14 15:54:57 +02:00
Jakob
831b513e6f specify available attributes for update 2012-05-14 15:48:00 +02:00
Jakob
3025683638 moved partial into other directory 2012-05-14 15:05:21 +02:00
Jakob
b7ef0a5b9d specify available attributes for update 2012-05-14 14:56:38 +02:00
Jakob
9b193c217d let's see if it also works with spree 1.1.0 2012-05-14 10:58:05 +02:00
Michael Bianco
a8c27750ef Spree 1.1 compatibility. Minor fix to shipping option filtering in checkout process 2012-05-10 10:18:56 -04:00
funkensturm
bafceda467 Merge pull request #15 from iloveitaly/spree-1.0
Deface Instead of Copied Delivery Partials
2012-05-04 13:48:29 -07:00
Michael Bianco
c5f0c79a03 Present message to user when no shipping methods are available 2012-05-03 10:59:32 -04:00
Michael Bianco
65de5ee7d7 Deleting old delivery partials 2012-05-02 16:09:19 -04:00
Michael Bianco
c780e6f756 Replacing _delivery partial overrides with deface 2012-05-02 16:05:12 -04:00
Michael Bianco
e737739f51 Removing explicit overrides load. This is done automatically 2012-05-02 16:00:00 -04:00
Michael Bianco
596006e8e1 Updating copied templates from spree 2012-05-01 11:45:53 -04:00
Michael Bianco
648f412ceb Resolving internationalization conflict with spree_essentials 2012-05-01 11:35:43 -04:00
funkensturm
8674cf777f Merge pull request #14 from iloveitaly/spree-1.0
Remove Assets
2012-04-11 23:59:23 -07:00
Michael Bianco
da1a13f472 Merge branch 'funkensturm/master' into spree-1.0
* funkensturm/master:
  Ruby 1.9.3 compatibility fix
2012-04-11 16:26:58 -04:00
Michael Bianco
ab049fb4bb Removing unneeded CSS + JS assets 2012-04-11 16:24:56 -04:00
funkensturm
6d57afb194 Merge pull request #13 from wakeless/ruby-1-9-3
Fixes broken digitals in ruby 1.9.3
2012-04-09 05:48:29 -07:00
funkensturm
7d7f21b737 Merge pull request #12 from iloveitaly/spree-1.0
Added preferences for click count and valid days on digital links. Added Authorization Tests
2012-04-09 05:38:06 -07:00
Michael Gall
8367745433 Ruby 1.9.3 compatibility fix 2012-04-05 12:25:43 +10:00
Michael Bianco
e652835a5f Added preferences for click amount and valid days on digital links. Tests for authorization are now running 2012-03-31 11:07:52 -04:00
funkensturm
13f60a6a1f Merge pull request #11 from iloveitaly/spree-1.0
Spree 1.0 Compatibility + Passing Tests
2012-03-31 02:31:17 -07:00
Michael Bianco
c4a85b71d7 Updating all tests. All tests are passing 2012-03-30 19:25:07 -04:00
Michael Bianco
62082f5a82 Updating Rakefile, Gemfile, etc for Spree 1.0 testing compatibilty 2012-03-30 19:23:58 -04:00
Michael Bianco
7f729bd402 Forgot to save merged changed. Minor deface fix 2012-03-28 21:51:16 -04:00
Michael Bianco
f53f2d2c63 Updated Versionfile 2012-03-28 20:29:40 -04:00
Michael Bianco
e7a7064aca Updating gemspec 2012-03-28 18:50:45 -04:00
Sam Davies
d2923b595f Removed rake tasks. Added generators. Modern engine dir structure 2012-03-28 18:43:27 -04:00
Michael Bianco
b7355fded8 Removing old hook 2012-03-28 18:38:57 -04:00
Michael Gall
76cc733755 Making compatible with later versions of spree 2012-03-28 18:37:13 -04:00
funkensturm
ca8d8ed610 Merge pull request #6 from iloveitaly/master
Adding 0.7.x Repo Reference in Versionfile
2012-01-02 22:57:36 -08:00
Michael Bianco
a8838761e0 Adding 0.7.x git repo reference 2011-12-27 14:51:06 -05:00
Michael Gall
e1b4787988 Search for digitals through variants 2011-12-19 18:46:11 +01:00
Michael Gall
4ecf99a4ae Using resource controller and namespace 2011-12-19 18:46:11 +01:00
Michael Gall
02fd4a64d1 inheriting from the correct controller 2011-12-19 18:46:11 +01:00
Michael Gall
c0255bdf87 inheriting from the correct controller 2011-12-19 18:46:11 +01:00
Michael Gall
414cf485ab Spree namespace changes 2011-12-19 18:46:11 +01:00
Michael Gall
191158639e Adding namespace support 2011-12-19 18:46:11 +01:00
Michael Gall
eddaea6395 Updating confirm email 2011-12-19 18:46:11 +01:00
71 changed files with 1025 additions and 704 deletions

7
.gitignore vendored
View File

@@ -4,8 +4,11 @@
.DS_Store
.idea
.project
.sass-cache
coverage
Gemfile.lock
tmp
nbproject
pkg
*.swp
spec/test_app
*.tmproj
spec/dummy

1
.rspec Normal file
View File

@@ -0,0 +1 @@
--color

1
.ruby-version Normal file
View File

@@ -0,0 +1 @@
1.9.3@digital2

50
Gemfile
View File

@@ -1,35 +1,17 @@
source 'http://rubygems.org'
source 'https://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
# Provides basic authentication functionality for testing parts of your engine
gem 'spree_auth_devise', :github => "spree/spree_auth_devise", :branch => '2-0-stable'
gemspec
group :test do
gem "shoulda-matchers"
end
# `rspec-rails` needs to be in the development group so that Rails generators work.
group :development, :test do
gem "rspec-rails", "~> 2.12"
end
gem 'therubyracer'

View File

@@ -1,8 +0,0 @@
GEM
remote: http://rubygems.org/
specs:
PLATFORMS
ruby
DEPENDENCIES

41
LICENSE
View File

@@ -1,21 +1,26 @@
The MIT License
Copyright (c) 2013 [name of plugin creator]
All rights reserved.
Copyright (c) 2011 funkensturm.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
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:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Spree nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
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.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

40
README.md Normal file
View File

@@ -0,0 +1,40 @@
SpreeDigital
============
Introduction goes here.
Installation
------------
Add spree_digital to your Gemfile:
```ruby
gem 'spree_digital'
```
Bundle your dependencies and run the installation generator:
```shell
bundle
bundle exec rails g spree_digital:install
```
Testing
-------
Be sure to bundle your dependencies and then create a dummy test app for the specs to run against.
```shell
bundle
bundle exec rake test_app
bundle exec rspec spec
```
When testing your applications integration with this extension you may use it's factories.
Simply add this require statement to your spec_helper:
```ruby
require 'spree_digital/factories'
```
Copyright (c) 2013 [name of extension creator], released under the New BSD License

View File

@@ -1,92 +0,0 @@
h1. 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.
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.
* File are uploaded to @rails_root/private@. Make sure it's symlinked in case you're using Capistrano.
* We use send_file to send the files on download. Yes, it goes through the entire stack right now.
h2. Installation
I assume that you already have a Rails 3 Spree application up and running. If not, the "Spree Documentation":http://spreecommerce.com/documentation will help you getting started.
To make use of @digital_spree@, you need to add this line to your @Gemfile@:
<pre>
gem 'spree_digital', :git => 'git://github.com/funkensturm/spree_digital.git', :branch => 'master'
</pre>
The following terminal commands will copy the file "spree_digital/db/migration/20110410134726_create_digitals.rb":http://github.com/funkensturm/spree_digital/blob/master/db/migrate/20110410134726_create_digitals.rb to the corresponding directory in your Rails application and apply the migration to your database.
<pre>
rake spree_digital:install
rake db:migrate
</pre>
This should be all there is to do.
h3. Important!
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.
h2. 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 "http://github.com/funkensturm/spree_digital/blob/master/LICENSE":LICENSE

View File

@@ -1,75 +1,25 @@
require 'rubygems'
require 'rake'
require 'rake/testtask'
require 'rake/packagetask'
require 'rake/gempackagetask'
require 'bundler'
Bundler::GemHelper.install_tasks
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/core/rake_task'
require 'spree/testing_support/extension_rake'
require 'rspec'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new
RSpec::Core::RakeTask.new
require 'cucumber/rake/task'
Cucumber::Rake::Task.new do |t|
t.cucumber_opts = %w{--format progress}
end
end
task :default => [:spec]
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"
desc 'Generates a dummy app for testing'
task :test_app do
require '../spree/lib/generators/spree/test_app_generator'
class SpreeDigitalTestAppGenerator < Spree::Generators::TestAppGenerator
ENV['LIB_NAME'] = 'spree_digital'
def install_gems
inside "test_app" do
run 'rake spree_core:install'
run 'rake spree_digital:install'
end
require File.join `bundle show spree_core`.chomp, 'lib/generators/spree/dummy/dummy_generator.rb'
Spree::DummyGenerator.class_eval do
def test_dummy_add_digital
# pulled from: http://jumph4x.net/post/20067515804/testing-spree-1-0-x-extensions-w-other-extension
puts "Installing #{ENV['LIB_NAME']} migrations [required for testing]"
directory File.join(`bundle show #{ENV['LIB_NAME']}`.chomp, 'db', 'migrate'), File.join(dummy_path, 'db')
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
Rake::Task['extension:test_app'].invoke
end

View File

@@ -1,7 +1,11 @@
# 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:
# Examples
#
# '1.2.x' => { :branch => 'master' }
# '1.1.x' => { :branch => '1-1-stable' }
# '1.0.x' => { :branch => '1-0-stable' }
# '0.70.x' => { :branch => '0-70-stable' }
# '0.40.x' => { :tag => 'v1.0.0', :version => '1.0.0' }
"0.60.x" => { :branch => "master" }
"0.50.x" => { :branch => "master" }

View File

@@ -0,0 +1 @@
//= require admin/spree_backend

View File

@@ -0,0 +1 @@
//= require store/spree_frontend

View File

@@ -0,0 +1,3 @@
/*
*= require admin/spree_backend
*/

View File

@@ -0,0 +1,3 @@
/*
*= require store/spree_frontend
*/

View File

@@ -1,12 +0,0 @@
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

View File

@@ -1,16 +0,0 @@
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

View File

@@ -0,0 +1,10 @@
module Spree
class Admin::DigitalsController < Spree::Admin::ResourceController
belongs_to "spree/product", :find_by => :permalink
protected
def location_after_save
admin_product_digitals_url(@product)
end
end
end

View File

@@ -0,0 +1,7 @@
Spree::Admin::OrdersController.class_eval do
def reset_digitals
@order.reset_digital_links!
flash[:notice] = t(:downloads_reset)
redirect_to admin_order_url(@order)
end
end

View File

@@ -0,0 +1,27 @@
module Spree
class DigitalsController < Spree::StoreController
ssl_required :show
def show
link = DigitalLink.find_by_secret(params[:secret])
if link.present? and link.digital.attachment.present?
attachment = link.digital.attachment
# don't authorize the link unless the file exists
# the logger error will help track down customer issues easier
if File.file?(attachment.path)
if link.authorize!
send_file attachment.path, :filename => attachment.original_filename, :type => attachment.content_type and return
end
else
Rails.logger.error "Missing Digital Item: #{attachment.path}"
end
end
render :unauthorized
end
end
end

View File

@@ -1,10 +0,0 @@
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

View File

@@ -1,27 +0,0 @@
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

View File

@@ -1,22 +0,0 @@
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

View File

@@ -1,26 +0,0 @@
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
# Is at least one product/variant digital?
def some_digital?
line_items.map { |item| return true if item.digital? }
false
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

View File

@@ -0,0 +1,18 @@
# https://github.com/spree/spree/issues/1439
require_dependency 'spree/calculator'
module Spree
class Calculator::DigitalDelivery < Calculator::FlatRate
def self.description
Spree.t(:digital_delivery)
end
def compute(object=nil)
self.preferred_amount
end
def available?(order)
order.digital?
end
end
end

View File

@@ -0,0 +1,13 @@
module Spree
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 :/
attr_accessible :variant_id, :attachment
end
end

View File

@@ -0,0 +1,34 @@
module Spree
class DigitalLink < ActiveRecord::Base
belongs_to :digital
belongs_to :line_item
validates_length_of :secret, :is => 30
validates :digital, :presence => true
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 > Spree::DigitalConfiguration[:authorized_days].day.ago and self.access_counter < Spree::DigitalConfiguration[:authorized_clicks]
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
attr_accessible :digital, :line_item, :secret
end
end

View File

@@ -0,0 +1,23 @@
Spree::LineItem.class_eval do
has_many :digital_links, :dependent => :destroy
after_save :create_digital_links, :if => :digital?
def digital?
variant.digital?
end
private
# TODO there is no reason to create the digital links until the order is complete
def create_digital_links
digital_links.delete_all
variant.digitals.each do |digital|
self.quantity.times do
digital_links.create!(:digital => digital)
end
end
end
end

View File

@@ -0,0 +1,31 @@
Spree::Order.class_eval do
# all products are digital
def digital?
line_items.map { |item| return false unless item.digital? }
true
end
def some_digital?
line_items.map { |item| return true if item.digital? }
false
end
def reset_digital_links!
line_items.select(&:digital?).map(&:digital_links).flatten.each do |digital_link|
digital_link.update_column :access_counter, 0
digital_link.update_column :created_at, Time.now
end
end
# UPGRADE_CHECK this works as of spree 1.2.0. check function for changes on upgrade
def available_shipping_methods(display_on = nil)
return [] unless ship_address
all_methods = Spree::ShippingMethod.all_available(self, display_on)
if self.digital?
all_methods.detect { |m| m.calculator.class == Spree::Calculator::DigitalDelivery }.try { |d| [d] } || all_methods
else
all_methods
end
end
end

View File

@@ -0,0 +1,3 @@
Spree::Product.class_eval do
has_many :digitals, :through => :variants_including_master
end

View File

@@ -0,0 +1,19 @@
Spree::Variant.class_eval do
has_many :digitals
after_save :destroy_digital, :if => :deleted?
# Is this variant to be downloaded by the customer?
def digital?
digitals.present?
end
private
# :dependent => :destroy needs to be handled manually
# spree does not delete variants, just marks them as deleted?
# optionally keep digitals around for customers who require continued access to their purchases
def destroy_digital
digitals.map &:destroy unless Spree::DigitalConfiguration[:keep_digitals]
end
end

View File

@@ -1,20 +0,0 @@
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

View File

@@ -0,0 +1,14 @@
Deface::Override.new(:virtual_path => "spree/shared/_order_details",
:name => "add_digital_downloads_to_invoice",
:insert_bottom => "td[data-hook='order_item_description']",
:text => %q{
<% if order.payment_state == 'paid' and item.variant.digital? %>
<%= content_tag(:p, :class => 'download_links') do %>
<% item.digital_links.each do |digital_link| %>
<% format = File.extname(digital_link.digital.attachment.path).downcase %>
<%= link_to Spree.t(:digital_download, :type => Spree.t("digital_format#{format}")), digital_url(:host => Spree::Config.get(:site_url), :secret => digital_link.secret), :class => "btn #{format}" %>
<% end %>
<% end %>
<% end %>
},
:disabled => false)

View File

@@ -0,0 +1,8 @@
Deface::Override.new(:virtual_path => "spree/admin/shared/_product_tabs",
:name => "add_digital_versions_to_admin_product_tabs",
:insert_bottom => "[data-hook='admin_product_tabs'], #admin_product_tabs[data-hook]",
:text => " <li<%== ' class=\"active\"' if current == \"Digital Versions\" %>>
<%= link_to_with_icon 'icon-tasks', Spree.t(:digital_versions), admin_product_digitals_path(@product) %>
</li>
",
:disabled => false)

View File

@@ -0,0 +1,7 @@
Deface::Override.new(:virtual_path => "spree/admin/shared/_order_tabs",
:name => "add_reset_digitals_to_admin_orders",
:insert_after => ".sidebar",
:text => %q{
<%= content_tag(:p, button_link_to(Spree.t(:reset_downloads), reset_digitals_admin_order_url(@order)), class: 'clear') if @order.digital? or true %>
},
:disabled => false)

View File

@@ -1,35 +0,0 @@
<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.model_name.human %> "<%= 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/>

View File

@@ -1,20 +0,0 @@
<%= 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 %>

View File

@@ -1,5 +0,0 @@
<%#
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' %>

View File

@@ -1,12 +0,0 @@
<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>

View File

@@ -1,32 +0,0 @@
<%#
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 @order.digital_shipping_method && 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>

View File

@@ -1,5 +1,5 @@
<% if digital.attachment_file_name.present? %>
<%= digital.attachment_file_name %> (<%= number_to_human_size(digital.attachment_file_size) %>)
<% else %>
<%=t 'broken_file' %>
<%=Spree.t 'broken_file' %>
<% end %>

View File

@@ -0,0 +1,35 @@
<div>
<%= form_for(:digital, :url => admin_product_digitals_path(@product), :method => :create, :multipart => true ) do |f| %>
<fieldset>
<legend><%= Spree::Variant.model_name.human %> "<%= variant.options_text %>"</legend>
<%= f.field_container :current_file do %>
<strong><%=Spree.t 'files' %>:</strong>
<% if variant.digital? %>
<ul>
<% variant.digitals.each do |digital| %>
<li>
<%= render digital %>&nbsp;&nbsp;
<%= link_to Spree.t(:delete_file), admin_product_digital_url(@product, digital), :confirm => Spree.t(:delete_file_cofirmation, :filename => digital.attachment_file_name), :method => :delete %>
</li>
<% end %>
</ul>
<% else %>
<%=Spree.t 'none' %>
<% end %>
<% end %>
<p class="form-buttons">
<%= f.field_container :file do %>
<%= f.label :file, Spree.t("new_file") %> <span class="required">*</span><br/>
<%= f.file_field :attachment %>
<% end %>
<%= hidden_field_tag 'digital[variant_id]', variant.id %>
<%= button Spree.t('spree_digital.upload') %>
</p>
</fieldset>
<% end %>
</div>

View File

@@ -0,0 +1,20 @@
<%= render :partial => 'spree/admin/shared/product_sub_menu' %>
<%= render :partial => 'spree/admin/shared/product_tabs', :locals => {:current => "Digital Versions"} %>
<% if @product.has_variants? %>
<% for variant in @product.variants do %>
<%= render 'form', :variant => variant %>
<% end %>
<% else %>
<%= Spree.t :product_without_variants %>
<% if @product.master.digital? %>
<%= Spree.t :digital_exists %>
<%= render @product.master.digitals %>
<% end %>
<%= render 'form', :variant => @product.master %>
<% end %>

View File

@@ -0,0 +1,5 @@
<% if digital.attachment_file_name.present? %>
<%= digital.attachment_file_name %> (<%= number_to_human_size(digital.attachment_file_size) %>)
<% else %>
<%=Spree.t 'broken_file' %>
<% end %>

View File

@@ -0,0 +1,9 @@
<% if defined?(SpreeFancy) %>
<% content_for :subheader do %>
<h1><%= t('.unauthorized') %></h1>
<% end %>
<% else %>
<h1><%= t('.unauthorized') %></h1>
<% end %>
<%= t('.explained') %>

View File

@@ -25,8 +25,7 @@ ATTENTION! Each link will only work a SINGLE TIME!
<% for item in @order.line_items %>
<% if item.digital? %>
<%= item.variant.name %>:
<%= item.digital_links.inspect %>
<%= raw item.variant.name %>:
<% for link in item.digital_links %>
<%= digital_url :host => Spree::Config.get(:site_url), :secret => link.secret %>
<% end %>

View File

@@ -1,12 +1,28 @@
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>
spree:
broken_file: Warning! this file is broken
current_file: Current File
delete_file: Delete this file
delete_file_cofirmation: Are you sure you want to delete the file %{filename}?
digital_delivery: Digital Delivery
digital_download: Download %{type} ↓
digital_download_links: Digital Download Links
digital_format:
mp3: Audio MP3
mobi: Kindle eBook
epub: ePub eBook
pdf: pdf eBook
gz: Archive
digital_versions: Digital Versions
downloads_reset: Digital Downloads Reset
new_file: New File
reset_downloads: Reset Digital Downloads
spree:
digitals:
unauthorized:
explained: This download link has expired.
unauthorized: Unauthorized
spree_digital:
upload: Upload
product_without_variants: This product has no variants.
digital_exists: 'A digital version of this product currently exists:'

23
config/locales/it.yml Normal file
View File

@@ -0,0 +1,23 @@
it:
spree:
broken_file: Attenzione! questo file sembra essere rotto
current_file: File Corrente
delete_file: Elimina questo file
delete_file_cofirmation: Sei sicuro di voler eliminare il file %{filename}?
digital_delivery: Consegna Digitale
digital_download: Download %{type} ↓
digital_download_links: Link al Download Digitale
digital_format:
mp3: Audio MP3
mobi: Kindle eBook
epub: ePub eBook
pdf: pdf eBook
digital_versions: Versioni Digitali
new_file: Nuovo File
spree:
digitals:
unauthorized:
explained: This download link has expired.
unauthorized: Unauthorized
spree_digital:
upload: Upload

View File

@@ -1,12 +1,15 @@
ja:
digital_versions: デジタル版
current_file: 現在のバージョン
new_file: 新しいファイル
upload: アップロード
delete_file: このファイルを削除
broken_file: 注意! このファイルが壊れている!
delete_file_cofirmation: 本当に「%{filename}」を削除しても宜しいですか?
digital_shipping: ダウンロードリンクを<strong>%{email}</strong>に送ります
spree:
broken_file: 注意! このファイルが壊れている!
current_file: 現在のバージョン
delete_file: このファイルを削除
delete_file_cofirmation: 本当に「%{filename}」を削除しても宜しいですか?
digital_shipping: ダウンロードリンクを<strong>%{email}</strong>に送ります
digital_versions: デジタル版
new_file: 新しいファイル
spree:
digitals:
unauthorized:
explained: This download link has expired.
unauthorized: Unauthorized
upload: アップロード

View File

@@ -1,11 +1,15 @@
Rails.application.routes.draw do
Spree::Core::Engine.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
resources :orders do
member do
get :reset_digitals
end
end
end
get '/digital/:secret', :to => 'digitals#show', :via => :get, :as => 'digital', :constraints => { :secret => /[a-zA-Z0-9]{30}/ }
end

View File

@@ -0,0 +1,6 @@
class RenameDigitalToNamespace < ActiveRecord::Migration
def change
rename_table :digitals, :spree_digitals
rename_table :digital_links, :spree_digital_links
end
end

View File

@@ -0,0 +1,31 @@
module SpreeDigital
module Generators
class InstallGenerator < Rails::Generators::Base
class_option :auto_run_migrations, :type => :boolean, :default => false
def add_javascripts
append_file 'app/assets/javascripts/store/all.js', "//= require store/spree_digital\n"
append_file 'app/assets/javascripts/admin/all.js', "//= require admin/spree_digital\n"
end
def add_stylesheets
inject_into_file 'app/assets/stylesheets/store/all.css', " *= require store/spree_digital\n", :before => /\*\//, :verbose => true
inject_into_file 'app/assets/stylesheets/admin/all.css', " *= require admin/spree_digital\n", :before => /\*\//, :verbose => true
end
def add_migrations
run 'bundle exec rake railties:install:migrations FROM=spree_digital'
end
def run_migrations
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
if run_migrations
run 'bundle exec rake db:migrate'
else
puts 'Skipping rake db:migrate, don\'t forget to run it!'
end
end
end
end
end

View File

@@ -0,0 +1,12 @@
module Spree
class SpreeDigitalConfiguration < Preferences::Configuration
# number of times a customer can download a digital file
preference :authorized_clicks, :integer, :default => 3
# number of days after initial purchase the customer can download a file
preference :authorized_days, :integer, :default => 2
# should digitals be kept around after the associated product is destroyed
preference :keep_digitals, :boolean, :default => false
end
end

View File

@@ -1,17 +1,2 @@
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
require 'spree_digital/engine'

View File

@@ -0,0 +1,30 @@
module SpreeDigital
class Engine < Rails::Engine
require 'spree/core'
isolate_namespace Spree
engine_name 'spree_digital'
config.autoload_paths += %W(#{config.root}/lib)
# use rspec for tests
config.generators do |g|
g.test_framework :rspec
end
initializer "spree.spree_digital.preferences", :after => "spree.environment" do |app|
Spree::DigitalConfiguration = Spree::SpreeDigitalConfiguration.new
end
initializer "spree.register.digital_shipping", :after => 'spree.register.calculators' do |app|
app.config.spree.calculators.shipping_methods << Spree::Calculator::DigitalDelivery
end
def self.activate
Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
Rails.configuration.cache_classes ? require(c) : load(c)
end
end
config.to_prepare &method(:activate).to_proc
end
end

View File

@@ -0,0 +1,28 @@
FactoryGirl.define do
# Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them.
#
# Example adding this to your spec_helper will load these Factories for use:
# require 'spree_digital/factories'
factory :digital, :class => Spree::Digital do |f|
# TODO good to assign variant association if no association is manually defined
# f.variant { |p| p.association(:variant) }
attachment_content_type 'application/octet-stream'
attachment_file_name 'a_great_book.epub'
end
factory :digital_link, :class => Spree::DigitalLink do |f|
f.digital { |p| p.association(:digital) }
f.line_item { |p| p.association(:line_item) }
end
factory :digital_shipping_calculator, class: Spree::Calculator::DigitalDelivery do |c|
after(:create) { |c| c.set_preference(:amount, 0) }
end
factory :digital_shipping_method, parent: :shipping_method do |f|
name "Digital Delivery"
calculator { FactoryGirl.build :digital_shipping_calculator }
end
end

View File

@@ -1,10 +0,0 @@
class SpreeDigitalHooks < Spree::ThemeSupport::HookListener
Deface::Override.new(:virtual_path => "admin/shared/_product_tabs",
:name => "converted_admin_product_tabs_986577829",
:insert_bottom => "[data-hook='admin_product_tabs'], #admin_product_tabs[data-hook]",
:text => " <li<%== ' class=\"active\"' if current == \"Digital Versions\" %>>
<%= link_to t(\"digital_versions\"), admin_product_digitals_path(@product) %>
</li>
",
:disabled => false)
end

View File

@@ -1,18 +0,0 @@
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

View File

@@ -1 +0,0 @@
# add custom rake tasks here

147
readme.md Normal file
View File

@@ -0,0 +1,147 @@
# Spree Digital
This is a spree extension to enable downloadable products (ebooks, MP3s, videos, etc).
The master branch is compatible with Spree 1.2.x. 1.0.x - 1.1.x versions are available, check the `Versionfile`.
This documentation is not complete and possibly out of date in some cases. There are features that have been implemented that are not documented here, please look at the source for complete documentation.
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.
* The table structure of spree_core is not touched. Spree digital lives parallel to spree_core and does change the 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. Adding download buttons to `OrdersController#show` is easy, [check out this gist](https://gist.github.com/3187793#file_add_spree_digital_buttons_to_invoice.rb).
* Once the order is checked-out, the download links will immediately be sent (i.e. in the order confirmation). You'll have to modify the system to support 'delayed' payments (like a billable account).
* You should create a ShippingMethod based on the Digital Delivery calculator type. The default cost for digital delivery is 0, but you can define a flat rate (creating a per-item digital delivery fee would be possible as well). Checkout the [source code](https://github.com/halo/spree_digital/blob/master/app/models/spree/calculator/digital_delivery.rb) for the Digital Delivery calculator for more information.
* 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.
* You can set how many times (clicks) the users downloads will work. You can also set how long the users links will work (expiration). For more information, [check out the preferences object](https://github.com/halo/spree_digital/blob/master/lib/spree/spree_digital_configuration.rb)
* The file `views/order_mailer/confirm_email.text.erb` is the only thing that should need customization. If you are looking for HTML emails, [this branch of spree-html-email](http://github.com/iloveitaly/spree-html-email) supports spree_digital
* 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.
* File are uploaded to `rails_root/private`. Make sure it's symlinked in case you're using Capistrano. If you want to change the upload path, [check out this gist](https://gist.github.com/3187793#file_spree_digital_path_change_decorator.rb)
* You must add a `views/spree/digitals/unauthorized.html.erb` file to customize an error message to the user if they exceed the download / days limit
* We use send_file to send the files on download. See below for instructions on how to push file downloading off to nginx.
## Installation
Add this line to your gemfile:
```shell
gem 'spree_digital', :git => 'git://github.com/dlage/spree_digital.git', :branch => 'master'
```
The following terminal commands will copy the migration files to the corresponding directory in your Rails application and apply the migrations to your database.
```shell
bundle exec rails g spree_digital:install
bundle exec rake db:migrate
```
Then set any preferences.
### Shipping Configuration
You should create a ShippingMethod based on the Digital Delivery calculator type. 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.
### Improving File Downloading: `send_file` + nginx
Without customization, all file downloading will route through the rails stack. This means that if you have two workers, and two customers are downloading files, your server is maxed out and will be unresponsive until the downloads have finished.
Luckily there is an easy way around this: pass off file downloading to nginx (or apache, etc). Take a look at [this article](http://blog.kiskolabs.com/post/637725747/nginx-rails-send-file) for a good explanation.
```
# in your app's source
# config/environments/production.rb
# Specifies the header that your server uses for sending files
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
# on your server
# /etc/nginx/sites-available/spree-secure
upstream unicorn_spree_secure {
server unix:/data/spree/shared/sockets/unicorn.sock fail_timeout=0;
}
server {
listen 443;
...
location / {
proxy_set_header X_FORWARDED_PROTO https;
...
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping /data/spree/shared/uploaded-files/digitals/=/digitals/;
...
}
location /digitals/ {
internal;
root /data/spree/shared/uploaded-files/;
}
...
}
```
References:
* [Gist of example config](https://gist.github.com/416004)
* [Change paperclip's upload / download path](https://gist.github.com/3187793#file_spree_digital_path_change_decorator.rb)
* ["X-Accel-Mapping header missing" in nginx error log](http://stackoverflow.com/questions/6237016/message-x-accel-mapping-header-missing-in-nginx-error-log)
* [Another good, but older, explanation](http://kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/)
## Usage
### Table Diagram
<img src="http://github.com/halo/spree_digital/raw/master/doc/tables.png">
### Installation
Get the spree framework and spree_digital extension for it:
```shell
git clone git://github.com/spree/spree.git
git clone git://github.com/dlage/spree_digital.git
```
Go into the spree directory and run the bundle command:
```shell
cd spree
bundle install
```
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!
```shell
cd spree_digital
bundle install
```
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.
```shell
rake test_app
rake spec
```
This link may be very helpful to you: [http://github.com/spree/spree](http://github.com/spree/spree)
### Authors
* [iloveitaly](http://github.com/iloveitaly/)
* [halo](http://github.com/halo)
* [dlage](http://github.com/dlage)
### License
Copyright (c) 2011 funkensturm.
Released under the MIT License
See [LICENSE](http://github.com/funkensturm/spree_digital/blob/master/LICENSE)

7
script/rails Normal file
View File

@@ -0,0 +1,7 @@
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
ENGINE_ROOT = File.expand_path('../..', __FILE__)
ENGINE_PATH = File.expand_path('../../lib/spree_digital/engine', __FILE__)
require 'rails/all'
require 'rails/engine/commands'

View File

@@ -0,0 +1,43 @@
require 'spec_helper'
describe Spree::Admin::DigitalsController do
before do
@product = FactoryGirl.create :product
controller.stub :authorize! => true
end
let(:product) { @product }
context "with variants" do
before do
# TODO add variants with digital
end
it "should display an empty page when no digitals exist" do
end
it "should display list of digitals when they exist" do
end
end
context "without variants" do
render_views
it "should display an empty page when no digitals exist" do
spree_get :index, "product_id" => product.permalink
response.code.should == "200"
response.body.should include("This product has no variants")
end
it "should display list of digitals when they exist" do
@digital = FactoryGirl.create :digital, :variant => product.master
spree_get :index, :product_id => product.permalink
response.code.should == "200"
end
end
end

View File

@@ -1,6 +0,0 @@
require 'factory_girl'
Dir["#{File.dirname(__FILE__)}/factories/**"].each do |f|
fp = File.expand_path(f)
require fp
end

View File

@@ -1,3 +0,0 @@
Factory.define :digital do |f|
f.variant { |p| p.association(:variant) }
end

View File

@@ -1,4 +0,0 @@
Factory.define :digital_link do |f|
f.digital { |p| p.association(:digital) }
f.line_item { |p| p.association(:line_item) }
end

View File

@@ -1,98 +1,75 @@
require File.dirname(__FILE__) + '/../spec_helper'
require 'spec_helper'
describe DigitalLink do
describe Spree::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
FactoryGirl.create(: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
FactoryGirl.create(: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)
digital_link = FactoryGirl.create(: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)
link = FactoryGirl.create(: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)
link = FactoryGirl.create(: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
context "authorization" do
it "should increment the counter using #authorize!" do
link = FactoryGirl.create(:digital_link)
link.access_counter.should == 0
link.authorize!
link.access_counter.should == 1
end
it "should not be #authorized? when the access_counter is too high" do
link = FactoryGirl.create(:digital_link)
link.stub(:access_counter => Spree::DigitalConfiguration[:authorized_clicks] - 1)
link.authorizable?.should be_true
link.stub(:access_counter => Spree::DigitalConfiguration[:authorized_clicks])
link.authorizable?.should be_false
end
it "should not be #authorize! when the created_at date is too far in the past" do
link = FactoryGirl.create(:digital_link)
link.authorize!.should be_true
link.stub(:created_at => (Spree::DigitalConfiguration[:authorized_days] * 24 - 1).hours.ago)
link.authorize!.should be_true
link.stub(:created_at => (Spree::DigitalConfiguration[:authorized_days] * 24 + 1).hours.ago)
link.authorize!.should be_false
end
it "should not be #authorized? when both access_counter and created_at are invalid" do
link = FactoryGirl.create(:digital_link)
link.authorizable?.should be_true
link.stub(:access_counter => Spree::DigitalConfiguration[:authorized_clicks], :created_at => (Spree::DigitalConfiguration[:authorized_days] * 24 + 1).hours.ago)
link.authorizable?.should be_false
end
end
end

View File

@@ -1,10 +1,9 @@
require File.dirname(__FILE__) + '/../spec_helper'
require 'spec_helper'
describe Digital do
describe Spree::Digital do
context 'validation' do
it { should belong_to(:variant) }
it { should have_valid_factory(:digital) }
end
context "#create" do
@@ -12,14 +11,13 @@ describe Digital 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
it "should destroy associated digital_links" do
digital = FactoryGirl.create(:digital)
3.times { digital.digital_links.create!({ :line_item => FactoryGirl.create(:line_item) }, :without_protection => true) }
Spree::DigitalLink.count.should == 3
digital.destroy
Spree::DigitalLink.count.should == 0
end
end
end

View File

@@ -1,29 +1,34 @@
require File.dirname(__FILE__) + '/../spec_helper'
require 'spec_helper'
describe LineItem do
describe Spree::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
digital_variant = FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)])
line_item = FactoryGirl.create(:line_item, :variant => digital_variant)
links = digital_variant.digitals.first.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
digital_variant = FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)])
line_item = FactoryGirl.create(:line_item, :variant => digital_variant, :quantity => 5)
links = digital_variant.digitals.first.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 = digital_variant.digitals.first.digital_links
links.all.size.should == 8
links.each { |link| link.line_item.should == line_item }
end
it "should create a link for digital variants with multiple digital downloads attached" do
end
end
end

View File

@@ -1,54 +1,85 @@
require File.dirname(__FILE__) + '/../spec_helper'
require 'spec_helper'
describe Order do
context 'validation' do
it { should have_valid_factory(:order) }
end
describe Spree::Order do
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 = FactoryGirl.create(:order)
order.contents.add variant1 = FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)]), 1
order.contents.add variant2 = FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)]), 1
order.contents.add variant3 = FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)]), 1
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 = FactoryGirl.create(:order)
digital_variant = FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)])
order.contents.add digital_variant, 3
order.line_items.first.quantity.should == 3
order.add_variant digital_variant, 2
order.contents.add 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)
order = FactoryGirl.create(:order)
3.times do
order.add_variant Factory(:variant, :digital => Factory(:digital))
order.contents.add FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)]), 1
end
order.digital?.should be_true
order.add_variant Factory(:variant, :digital => Factory(:digital)), 4
order.contents.add FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)]), 4
order.digital?.should be_true
end
it "should understand that not all products are digital" do
order = Factory(:order)
order = FactoryGirl.create(:order)
3.times do
order.add_variant Factory(:variant, :digital => Factory(:digital))
order.contents.add FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)]), 1
end
order.add_variant Factory(:variant) # this is the analog product
order.contents.add FactoryGirl.create(:variant), 1 # this is the analog product
order.digital?.should be_false
order.add_variant Factory(:variant, :digital => Factory(:digital)), 4
order.contents.add FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)]), 4
order.digital?.should be_false
end
end
context "digital shipping" do
before do
@order = FactoryGirl.create(:order)
# need shipp_address for rate_hash.count != 0
@order.ship_address = FactoryGirl.create :address
@order.bill_address = FactoryGirl.create :address
@order.save!
3.times { @order.contents.add FactoryGirl.create(:variant, :digitals => [FactoryGirl.create(:digital)]), 1 }
FactoryGirl.create :digital_shipping_method
s = FactoryGirl.create :shipping_method
s.calculator.set_preference(:amount, 10)
end
let(:order) { @order }
it "should only offer digital shipping if all items are digital" do
order.digital?.should be_true
shipments = order.create_proposed_shipments
shipments.count.should == 1
shipments.first.shipping_method.calculator.class.should == Spree::Calculator::DigitalDelivery
shipments.first.cost.should == 0.0
end
it "should not offer digital shipping if only some items are digital" do
order.digital?.should be_true
order.contents.add FactoryGirl.create(:variant), 1 # this is the analog product
order.digital?.should be_false
order.rate_hash.count.should == 1
order.rate_hash.first.shipping_method.calculator.class.should_not == Spree::Calculator::DigitalDelivery
order.rate_hash.first.cost.should == 10.0
end
end
end

View File

@@ -0,0 +1,38 @@
require 'spec_helper'
describe Spree::Variant do
context "#destroy" do
before do
@variant = FactoryGirl.create :variant
@digital = FactoryGirl.create :digital, :variant => @variant
end
let(:variant) { @variant }
let(:digital) { @digital }
it "should destroy associated digitals by default" do
# default is false
Spree::DigitalConfiguration[:keep_digitals] = false
Spree::Digital.count.should == 1
variant.digitals.present?.should be_true
variant.deleted_at = Time.now
variant.deleted?.should be_true
variant.save!
expect { digital.reload.present? }.to raise_error
Spree::Digital.count.should == 0
end
it "should conditionally keep associated digitals" do
Spree::DigitalConfiguration[:keep_digitals] = true
Spree::Digital.count.should == 1
variant.digitals.present?.should be_true
variant.deleted_at = Time.now
variant.save!
variant.deleted?.should be_true
expect { digital.reload.present? }.to_not raise_error
Spree::Digital.count.should == 1
end
end
end

View File

@@ -1,30 +1,51 @@
require File.expand_path("../factories", __FILE__)
# Run Coverage report
require 'simplecov'
SimpleCov.start do
add_group 'Controllers', 'app/controllers'
add_group 'Helpers', 'app/helpers'
add_group 'Mailers', 'app/mailers'
add_group 'Models', 'app/models'
add_group 'Views', 'app/views'
add_group 'Libraries', 'lib'
end
# Configure Rails Environment
ENV['RAILS_ENV'] = 'test'
require File.expand_path('../dummy/config/environment.rb', __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'
require 'database_cleaner'
require 'ffaker'
require 'shoulda-matchers'
# Requires supporting files with custom matchers and macros, etc,
# in ./support/ and its subdirectories.
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require f }
require 'spree_core/testing_support/factories'
# Requires factories defined in spree_core
require 'spree/testing_support/factories'
require 'spree/testing_support/controller_requests'
require 'spree/testing_support/authorization_helpers'
require 'spree/testing_support/url_helpers'
# Requires factories defined in lib/spree_digital/factories.rb
require 'spree_digital/factories'
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
# == URL Helpers
#
# Allows access to Spree's routes in specs:
#
# visit spree.admin_path
# current_path.should eql(spree.products_path)
config.include Spree::TestingSupport::UrlHelpers
config.include Spree::TestingSupport::ControllerRequests, :type => :controller
config.include Spree::TestingSupport::AuthorizationHelpers
config.include Devise::TestHelpers, :type => :controller
# == Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
@@ -33,29 +54,32 @@ RSpec.configure do |config|
# config.mock_with :flexmock
# config.mock_with :rr
config.mock_with :rspec
config.color = true
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
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
# Capybara javascript drivers require transactional fixtures set to false, and we use DatabaseCleaner
# to cleanup after each test instead. Without transactional fixtures set to false the records created
# to setup a test will be unavailable to the browser, which runs under a seperate server instance.
config.use_transactional_fixtures = false
@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
# Ensure Suite is set to use transactions for speed.
config.before :suite do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with :truncation
end
# Before each spec check if it is a Javascript test and switch between using database transactions or not where necessary.
config.before :each do
DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction
DatabaseCleaner.start
end
# After each spec clean the database.
config.after :each do
DatabaseCleaner.clean
end
config.fail_fast = ENV['FAIL_FAST'] || false
end

View File

@@ -1,15 +1,31 @@
# encoding: UTF-8
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.platform = Gem::Platform::RUBY
s.name = 'spree_digital'
s.version = '2.0.0'
s.summary = 'Digital download functionality for spree'
s.description = 'Digital download functionality for spree'
s.required_ruby_version = '>= 1.9.3'
s.author = 'Dinis Lage'
s.email = 'dinis@senolage.com'
s.homepage = 'http://senolage.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
s.add_dependency 'spree_core', '~> 2.0.0'
s.add_development_dependency 'capybara', '~> 2.1'
s.add_development_dependency 'coffee-rails'
s.add_development_dependency 'database_cleaner'
s.add_development_dependency 'factory_girl', '~> 4.2'
s.add_development_dependency 'ffaker'
s.add_development_dependency 'rspec-rails', '~> 2.13'
s.add_development_dependency 'sass-rails'
s.add_development_dependency 'selenium-webdriver'
s.add_development_dependency 'simplecov'
s.add_development_dependency 'sqlite3'
end