bodega 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.rspec +1 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +85 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +16 -0
  7. data/Rakefile +29 -0
  8. data/VERSION +1 -0
  9. data/app/assets/images/bodega/.gitkeep +0 -0
  10. data/app/assets/javascripts/bodega/application.js +0 -0
  11. data/app/assets/stylesheets/bodega/application.css +13 -0
  12. data/app/controllers/bodega/application_controller.rb +4 -0
  13. data/app/controllers/bodega/orders_controller.rb +51 -0
  14. data/app/helpers/bodega/application_helper.rb +11 -0
  15. data/app/helpers/bodega/cart_helper.rb +28 -0
  16. data/app/models/bodega/order.rb +28 -0
  17. data/app/models/bodega/order_product.rb +47 -0
  18. data/app/models/bodega/product.rb +33 -0
  19. data/app/views/bodega/orders/new.html.erb +29 -0
  20. data/app/views/bodega/orders/show.html.erb +1 -0
  21. data/bodega.gemspec +84 -0
  22. data/config/routes.rb +16 -0
  23. data/db/migrate/20121111170337_create_bodega_orders.rb +13 -0
  24. data/db/migrate/20121111170420_create_bodega_order_products.rb +13 -0
  25. data/lib/bodega.rb +17 -0
  26. data/lib/bodega/engine.rb +20 -0
  27. data/lib/bodega/monetize.rb +15 -0
  28. data/lib/bodega/payment_method.rb +10 -0
  29. data/lib/bodega/payment_method/base.rb +31 -0
  30. data/lib/bodega/payment_method/paypal.rb +40 -0
  31. data/lib/bodega/version.rb +3 -0
  32. data/lib/generators/bodega/install/install_generator.rb +0 -0
  33. data/lib/generators/bodega/product/USAGE +9 -0
  34. data/lib/generators/bodega/product/product_generator.rb +19 -0
  35. data/lib/generators/bodega/product/templates/migration.rb +12 -0
  36. data/lib/generators/bodega/productize/USAGE +8 -0
  37. data/lib/generators/bodega/productize/productize_generator.rb +19 -0
  38. data/lib/generators/bodega/productize/templates/migration.rb +10 -0
  39. data/lib/tasks/bodega_tasks.rake +4 -0
  40. data/script/rails +8 -0
  41. metadata +137 -0
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.3@bodega --create
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'configurator2', '>= 0.1.1'
4
+
5
+ group :development, :test do
6
+ gem 'jeweler', '1.8.4'
7
+ gem 'pry', require: false
8
+ end
9
+
10
+ group :test do
11
+ gem 'rspec-rails'
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,85 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionpack (3.2.9)
5
+ activemodel (= 3.2.9)
6
+ activesupport (= 3.2.9)
7
+ builder (~> 3.0.0)
8
+ erubis (~> 2.7.0)
9
+ journey (~> 1.0.4)
10
+ rack (~> 1.4.0)
11
+ rack-cache (~> 1.2)
12
+ rack-test (~> 0.6.1)
13
+ sprockets (~> 2.2.1)
14
+ activemodel (3.2.9)
15
+ activesupport (= 3.2.9)
16
+ builder (~> 3.0.0)
17
+ activesupport (3.2.9)
18
+ i18n (~> 0.6)
19
+ multi_json (~> 1.0)
20
+ builder (3.0.4)
21
+ coderay (1.0.8)
22
+ configurator2 (0.1.1)
23
+ diff-lcs (1.1.3)
24
+ erubis (2.7.0)
25
+ git (1.2.5)
26
+ hike (1.2.1)
27
+ i18n (0.6.1)
28
+ jeweler (1.8.4)
29
+ bundler (~> 1.0)
30
+ git (>= 1.2.5)
31
+ rake
32
+ rdoc
33
+ journey (1.0.4)
34
+ json (1.7.5)
35
+ method_source (0.8.1)
36
+ multi_json (1.5.0)
37
+ pry (0.9.10)
38
+ coderay (~> 1.0.5)
39
+ method_source (~> 0.8)
40
+ slop (~> 3.3.1)
41
+ rack (1.4.1)
42
+ rack-cache (1.2)
43
+ rack (>= 0.4)
44
+ rack-ssl (1.3.2)
45
+ rack
46
+ rack-test (0.6.2)
47
+ rack (>= 1.0)
48
+ railties (3.2.9)
49
+ actionpack (= 3.2.9)
50
+ activesupport (= 3.2.9)
51
+ rack-ssl (~> 1.3.2)
52
+ rake (>= 0.8.7)
53
+ rdoc (~> 3.4)
54
+ thor (>= 0.14.6, < 2.0)
55
+ rake (0.9.2.2)
56
+ rdoc (3.12)
57
+ json (~> 1.4)
58
+ rspec-core (2.12.1)
59
+ rspec-expectations (2.12.0)
60
+ diff-lcs (~> 1.1.3)
61
+ rspec-mocks (2.12.0)
62
+ rspec-rails (2.12.0)
63
+ actionpack (>= 3.0)
64
+ activesupport (>= 3.0)
65
+ railties (>= 3.0)
66
+ rspec-core (~> 2.12.0)
67
+ rspec-expectations (~> 2.12.0)
68
+ rspec-mocks (~> 2.12.0)
69
+ slop (3.3.3)
70
+ sprockets (2.2.2)
71
+ hike (~> 1.2)
72
+ multi_json (~> 1.0)
73
+ rack (~> 1.0)
74
+ tilt (~> 1.1, != 1.3.0)
75
+ thor (0.16.0)
76
+ tilt (1.3.3)
77
+
78
+ PLATFORMS
79
+ ruby
80
+
81
+ DEPENDENCIES
82
+ configurator2 (>= 0.1.1)
83
+ jeweler (= 1.8.4)
84
+ pry
85
+ rspec-rails
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # Bodega
2
+
3
+ **Bodega allows any ActiveRecord::Base subclass to be purchased with a few simple steps:**
4
+
5
+ 1. Install Bodega (add `gem 'bodega'` to your Gemfile and bundle)
6
+ 2. For existing models:
7
+ 1. `rails g bodega:productize existing_class_name`
8
+ 2. Add `include Bodega::Product` to your class definition
9
+ 3. For new models:
10
+ 1. `rails g bodega:product new_class_name`
11
+ 4. Add `mount Bodega::Engine => 'cart'` to your `config/routes.rb` file
12
+ 5. Profit (literally, for once)
13
+
14
+ ## WIP
15
+
16
+ This is a work-in-progress and is currently only in use on [womannyc.com](http://www.womannyc.com). Play with it if you want. It's not that exciting; shut up.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env rake
2
+ # encoding: utf-8
3
+
4
+ require 'rubygems'
5
+ require 'bundler'
6
+
7
+ begin
8
+ Bundler.setup(:default, :development)
9
+ rescue Bundler::BundlerError => e
10
+ $stderr.puts e.message
11
+ $stderr.puts "Run `bundle install` to install missing gems"
12
+ exit e.status_code
13
+ end
14
+ require 'rake'
15
+
16
+ require 'jeweler'
17
+ Jeweler::Tasks.new do |gem|
18
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
19
+ gem.name = "bodega"
20
+ gem.homepage = "http://github.com/flipsasser/bodega"
21
+ gem.license = "MIT"
22
+ gem.summary = %Q{Bodega adds checkout logic to any model in your app!}
23
+ gem.description = %Q{Bodega adds checkout logic to any model in your app!}
24
+ gem.email = "flip@x451.com"
25
+ gem.authors = ["Flip Sasser"]
26
+ # dependencies defined in Gemfile
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
File without changes
File without changes
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ module Bodega
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,51 @@
1
+ class Bodega::OrdersController < ApplicationController
2
+ helper 'bodega/cart'
3
+ include Bodega::CartHelper
4
+ include Bodega::PaymentMethod
5
+
6
+ before_filter :find_order, only: [:show, :update]
7
+
8
+ def add
9
+ if product = params[:product]
10
+ update_cart(product)
11
+ end
12
+ redirect_to root_path
13
+ end
14
+
15
+ def complete
16
+ current_order.payment_id = payment_method.complete!
17
+ current_order.save!
18
+ redirect_to order_path(current_order)
19
+ end
20
+
21
+ def create
22
+ params[:products].each do |product|
23
+ update_cart(product)
24
+ end
25
+ if params[:checkout]
26
+ redirect_to payment_method.checkout_url(complete_url, root_url)
27
+ else
28
+ render :new
29
+ end
30
+ end
31
+
32
+ protected
33
+ def find_order
34
+ raise ActiveRecord::NotFound unless @order = Bodega::Order.where(id: params[:order_id] || params[:id]).first
35
+ end
36
+
37
+ def update_cart(product)
38
+ product_id = "#{product[:type]}.#{product[:id]}"
39
+ if product[:remove]
40
+ current_products.delete product_id
41
+ else
42
+ if current = current_products[product_id]
43
+ current_quantity = current[:quantity].to_i
44
+ else
45
+ current_quantity = 0
46
+ end
47
+ new_quantity = product[:quantity] ? product[:quantity] : current_quantity + 1
48
+ current_products[product_id] = product.merge(quantity: new_quantity)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ module Bodega
2
+ module ApplicationHelper
3
+ def button_to_cart(product, label = 'Add to Cart')
4
+ form_tag(bodega.add_path) do
5
+ hidden_field_tag('product[type]', product.class) +
6
+ hidden_field_tag('product[id]', product.id) +
7
+ button_tag(label)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ module Bodega
2
+ module CartHelper
3
+ protected
4
+ def current_order
5
+ @current_order ||= Bodega::Order.new.tap do |order|
6
+ #begin
7
+ if Bodega.config.customer_method
8
+ order.customer = send(Bodega.config.customer_method)
9
+ end
10
+ #rescue NoMethodError
11
+ raise "Please configure Bodega.config.customer_method to point to a valid method for accessing a customer record (default: current_user)"
12
+ #end
13
+ order.order_products = current_products.map do |type, product|
14
+ product = product.symbolize_keys
15
+ OrderProduct.new do |order_product|
16
+ order_product.product_type = product[:type]
17
+ order_product.product_id = product[:id]
18
+ order_product.quantity = product[:quantity]
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def current_products
25
+ session[:bodega_products] ||= {}
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module Bodega
2
+ class Order < ActiveRecord::Base
3
+ extend Bodega::Monetize
4
+
5
+ before_create :set_identifier
6
+
7
+ belongs_to :customer, polymorphic: true
8
+ has_many :order_products, class_name: 'Bodega::OrderProduct', dependent: :destroy
9
+ has_many :products, through: :order_products
10
+
11
+ monetize :subtotal
12
+ monetize :tax
13
+ monetize :total
14
+
15
+ def subtotal
16
+ order_products.inject(0) {|sum, order_product| sum += order_product.subtotal }
17
+ end
18
+
19
+ def to_param
20
+ identifier
21
+ end
22
+
23
+ protected
24
+ def set_identifier
25
+ self.identifier = "#{Time.now.to_i}--#{rand(12)}"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ module Bodega
2
+ class OrderProduct < ActiveRecord::Base
3
+ extend Bodega::Monetize
4
+
5
+ belongs_to :order, class_name: 'Bodega::Order'
6
+ belongs_to :product, polymorphic: true
7
+
8
+ delegate :price, to: :product
9
+
10
+ monetize :subtotal
11
+ monetize :tax
12
+ monetize :total
13
+
14
+ validates_numericality_of :quantity, allow_blank: true, minimum: 1
15
+ validates_presence_of :quantity
16
+
17
+ def decorated_product
18
+ product.respond_to?(:decorator) ? product.decorator.decorate(product) : product
19
+ end
20
+
21
+ def name
22
+ decorated_product.respond_to?(:name) ? decorated_product.name : product.to_s
23
+ end
24
+
25
+ def quantity_and_name
26
+ "#{quantity} x #{name.pluralize(quantity)}"
27
+ end
28
+
29
+ def subtotal
30
+ read_attribute(:subtotal) || price * quantity
31
+ end
32
+
33
+ def total
34
+ read_attribute(:total) || subtotal + calculate_tax
35
+ end
36
+
37
+ protected
38
+ def calculate_tax
39
+ self.tax = 0
40
+ end
41
+
42
+ def calculate_total
43
+ self.subtotal = price * quantity
44
+ self.total = subtotal + calculate_tax
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,33 @@
1
+ module Bodega
2
+ module Product
3
+ def self.included(base)
4
+ base.class_eval do
5
+ extend Bodega::Monetize
6
+
7
+ has_many :order_products, as: :product, class_name: 'Bodega::OrderProduct'
8
+ has_many :orders, through: :order_products
9
+
10
+ monetize :price
11
+
12
+ scope :for_sale, lambda {
13
+ where('for_sale IS TRUE OR ((for_sale_at >= :today OR for_sale_at IS NULL) AND (not_for_sale_at <= :today OR not_for_sale_at IS NULL))', today: Date.today)
14
+ }
15
+
16
+ # TODO: Get this to use a regular JOIN
17
+ scope :popular, joins(%(LEFT JOIN "bodega_order_products" ON "bodega_order_products"."product_id" = "#{table_name}"."id" AND "bodega_order_products"."product_type" = '#{name}')).order('SUM(bodega_order_products.quantity) DESC').group("#{table_name}.id")
18
+ end
19
+ end
20
+
21
+ def in_stock?
22
+ if keep_stock?
23
+ number_in_stock > 0
24
+ else
25
+ true
26
+ end
27
+ end
28
+
29
+ def max_for_sale
30
+ keep_stock? ? number_in_stock : 1000
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ <%= form_for(current_order, url: root_path) do |form| %>
2
+ <table id="bodega-cart">
3
+ <thead>
4
+ <tr><th class="product-name" colspan="2"><%= Bodega.config.product_name.titleize %></th><th class="price">Price</th><th class="total">Total</th></tr>
5
+ </thead>
6
+ <tbody>
7
+ <% current_order.order_products.each do |order_product| -%>
8
+ <tr>
9
+ <td class="quantity-field">
10
+ <%= number_field_tag 'products[][quantity]', order_product.quantity, class: 'quantity', max: order_product.product.max_for_sale, min: 1 %>
11
+ </td>
12
+ <td class="product-name">
13
+ <%= order_product.name %>
14
+ <%= hidden_field_tag 'products[][type]', order_product.product_type %>
15
+ <%= hidden_field_tag 'products[][id]', order_product.product_id %>
16
+ </td>
17
+ <td class="price">
18
+ $<%= order_product.price %>
19
+ </td>
20
+ <td class="subtotal">
21
+ $<%= order_product.subtotal %>
22
+ </td>
23
+ </tr>
24
+ <% end -%>
25
+ </tbody>
26
+ </table>
27
+ <%= button_tag 'Update Cart', name: :update, value: 1 %>
28
+ <%= button_tag 'Checkout', name: :checkout, value: 1 %>
29
+ <% end =%>
@@ -0,0 +1 @@
1
+ <%= @order.inspect %>
data/bodega.gemspec ADDED
@@ -0,0 +1,84 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "bodega"
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Flip Sasser"]
12
+ s.date = "2012-12-26"
13
+ s.description = "Bodega adds checkout logic to any model in your app!"
14
+ s.email = "flip@x451.com"
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".rspec",
20
+ ".rvmrc",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "MIT-LICENSE",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "app/assets/images/bodega/.gitkeep",
28
+ "app/assets/javascripts/bodega/application.js",
29
+ "app/assets/stylesheets/bodega/application.css",
30
+ "app/controllers/bodega/application_controller.rb",
31
+ "app/controllers/bodega/orders_controller.rb",
32
+ "app/helpers/bodega/application_helper.rb",
33
+ "app/helpers/bodega/cart_helper.rb",
34
+ "app/models/bodega/order.rb",
35
+ "app/models/bodega/order_product.rb",
36
+ "app/models/bodega/product.rb",
37
+ "app/views/bodega/orders/new.html.erb",
38
+ "app/views/bodega/orders/show.html.erb",
39
+ "bodega.gemspec",
40
+ "config/routes.rb",
41
+ "db/migrate/20121111170337_create_bodega_orders.rb",
42
+ "db/migrate/20121111170420_create_bodega_order_products.rb",
43
+ "lib/bodega.rb",
44
+ "lib/bodega/engine.rb",
45
+ "lib/bodega/monetize.rb",
46
+ "lib/bodega/payment_method.rb",
47
+ "lib/bodega/payment_method/base.rb",
48
+ "lib/bodega/payment_method/paypal.rb",
49
+ "lib/bodega/version.rb",
50
+ "lib/generators/bodega/install/install_generator.rb",
51
+ "lib/generators/bodega/product/USAGE",
52
+ "lib/generators/bodega/product/product_generator.rb",
53
+ "lib/generators/bodega/product/templates/migration.rb",
54
+ "lib/generators/bodega/productize/USAGE",
55
+ "lib/generators/bodega/productize/productize_generator.rb",
56
+ "lib/generators/bodega/productize/templates/migration.rb",
57
+ "lib/tasks/bodega_tasks.rake",
58
+ "script/rails"
59
+ ]
60
+ s.homepage = "http://github.com/flipsasser/bodega"
61
+ s.licenses = ["MIT"]
62
+ s.require_paths = ["lib"]
63
+ s.rubygems_version = "1.8.24"
64
+ s.summary = "Bodega adds checkout logic to any model in your app!"
65
+
66
+ if s.respond_to? :specification_version then
67
+ s.specification_version = 3
68
+
69
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
70
+ s.add_runtime_dependency(%q<configurator2>, [">= 0.1.1"])
71
+ s.add_development_dependency(%q<jeweler>, ["= 1.8.4"])
72
+ s.add_development_dependency(%q<pry>, [">= 0"])
73
+ else
74
+ s.add_dependency(%q<configurator2>, [">= 0.1.1"])
75
+ s.add_dependency(%q<jeweler>, ["= 1.8.4"])
76
+ s.add_dependency(%q<pry>, [">= 0"])
77
+ end
78
+ else
79
+ s.add_dependency(%q<configurator2>, [">= 0.1.1"])
80
+ s.add_dependency(%q<jeweler>, ["= 1.8.4"])
81
+ s.add_dependency(%q<pry>, [">= 0"])
82
+ end
83
+ end
84
+
data/config/routes.rb ADDED
@@ -0,0 +1,16 @@
1
+ Bodega::Engine.routes.draw do
2
+ # Building orders
3
+ get '', as: :root, to: 'orders#new'
4
+ post '', to: 'orders#create'
5
+
6
+ # Add products to an order
7
+ post :add, to: 'orders#add'
8
+
9
+ # Processing orders
10
+ get :complete, to: 'orders#complete'
11
+ post :complete, to: 'orders#complete'
12
+
13
+ # Existing orders
14
+ get ':id', as: :order, to: 'orders#show'
15
+ post ':id', to: 'orders#update'
16
+ end
@@ -0,0 +1,13 @@
1
+ class CreateBodegaOrders < ActiveRecord::Migration
2
+ def change
3
+ create_table :bodega_orders do |t|
4
+ t.belongs_to :customer, polymorphic: true
5
+ t.string :identifier, limit: 20
6
+ t.string :payment_id
7
+ t.integer :subtotal_in_cents
8
+ t.integer :tax_in_cents
9
+ t.integer :total_in_cents
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class CreateBodegaOrderProducts < ActiveRecord::Migration
2
+ def change
3
+ create_table :bodega_order_products do |t|
4
+ t.belongs_to :order
5
+ t.belongs_to :product, polymorphic: true
6
+ t.integer :quantity
7
+ t.integer :price_in_cents
8
+ t.integer :subtotal_in_cents
9
+ t.integer :tax_in_cents
10
+ t.integer :total_in_cents
11
+ end
12
+ end
13
+ end
data/lib/bodega.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'bodega/engine'
2
+ require 'configurator'
3
+
4
+ module Bodega
5
+ autoload :PaymentMethod, 'bodega/payment_method'
6
+
7
+ extend Configurator
8
+ option :customer_method, :current_user
9
+ option :product_name, 'product'
10
+ # Auto-detect payment method. If a user has the Paypal gem installed,
11
+ # it'll use that. If a user has the Plinq gem installed, it'll use that.
12
+ # Otherwise, it'll be all, "HEY I NEED A PAYMENT METHOD" when checkout
13
+ # starts.
14
+ option :payment_method, lambda {
15
+ defined?(::Plinq) ? :plinq : defined?(::Paypal) ? :paypal : raise("No payment method detected. Please set one using `Bodega.config.payment_method=`")
16
+ }
17
+ end
@@ -0,0 +1,20 @@
1
+ module Bodega
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Bodega
4
+
5
+ initializer "bodega.hookses" do
6
+ ActiveSupport.on_load :action_controller do
7
+ #require 'bodega/action_controller'
8
+ include Bodega::CartHelper
9
+ end
10
+
11
+ ActiveSupport.on_load :active_record do
12
+ require 'bodega/monetize'
13
+ end
14
+
15
+ ActiveSupport.on_load :paypal_express do
16
+ raise 'w0tf'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ module Bodega
2
+ module Monetize
3
+ def monetize(attribute)
4
+ class_eval <<-monetize
5
+ def #{attribute}=(value)
6
+ self.#{attribute}_in_cents = (value.to_f * 100).to_i
7
+ end
8
+
9
+ def #{attribute}
10
+ (#{attribute}_in_cents || 0) / 100.0
11
+ end
12
+ monetize
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ module Bodega
2
+ module PaymentMethod
3
+ autoload :Paypal, 'bodega/payment_method/paypal'
4
+ autoload :Plinq, 'bodega/payment_method/plinq'
5
+
6
+ def payment_method
7
+ @payment_method ||= "Bodega::PaymentMethod::#{Bodega.config.payment_method.to_s.classify}".constantize.new(current_order, params)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ module Bodega
2
+ module PaymentMethod
3
+ class Base
4
+ class << self
5
+ def options(*new_options)
6
+ option_namespace = self.name.split('::').pop.underscore
7
+ Bodega.class_eval do
8
+ option option_namespace do
9
+ options(*new_options.flatten)
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ attr_accessor :options, :order
16
+
17
+ def checkout_url(success_url, cancel_url)
18
+ raise "Implement #{self.class.name}#checkout_url"
19
+ end
20
+
21
+ def complete!
22
+ raise "Implement #{self.class.name}#complete!"
23
+ end
24
+
25
+ def initialize(order, options)
26
+ self.order = order
27
+ self.options = options
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ require 'bodega/payment_method/base'
2
+
3
+ module Bodega
4
+ module PaymentMethod
5
+ class Paypal < Base
6
+ options :username, :password, :signature
7
+
8
+ def checkout_url(success_url, cancel_url)
9
+ response = client.setup(request, success_url, cancel_url)
10
+ response.redirect_uri
11
+ end
12
+
13
+ def complete!
14
+ response = client.checkout!(
15
+ options[:token],
16
+ options[:PayerID],
17
+ request
18
+ )
19
+ response.payment_info.last.transaction_id
20
+ end
21
+
22
+ protected
23
+ def client
24
+ @client ||= ::Paypal::Express::Request.new(
25
+ username: Bodega.config.paypal.username,
26
+ password: Bodega.config.paypal.password,
27
+ signature: Bodega.config.paypal.signature
28
+ )
29
+ end
30
+
31
+ def request
32
+ @request ||= ::Paypal::Payment::Request.new(
33
+ amount: order.subtotal,
34
+ description: order.order_products.map(&:quantity_and_name).to_sentence
35
+ )
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module Bodega
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Generate Bodega::Product compliant model
3
+
4
+ Example:
5
+ rails generate bodega:product skateboard
6
+
7
+ This will create:
8
+ app/models/skateboard.rb
9
+ db/migrate/XXXXXX_create_skateboards.rb
@@ -0,0 +1,19 @@
1
+ require 'rails/generators/migration'
2
+ require 'rails/generators/active_record/migration'
3
+
4
+ module Bodega
5
+ module Generators
6
+ class ProductGenerator < Rails::Generators::Base
7
+ argument :product_name, type: :string
8
+
9
+ include Rails::Generators::Migration
10
+ extend ActiveRecord::Generators::Migration
11
+
12
+ source_root File.expand_path('../templates', __FILE__)
13
+
14
+ def copy_migration
15
+ migration_template("migration.rb", "db/migrate/bodegaize_#{product_name.tableize}")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ class Create<%= product_name.classify.pluralize %> < ActiveRecord::Migration
2
+ def change
3
+ create_table :<%= product_name.tableize %> do
4
+ t.integer :price_in_cents
5
+ t.boolean :for_sale, default: true
6
+ t.boolean :keep_stock, default: false
7
+ t.integer :number_in_stock
8
+ t.datetime :for_sale_at
9
+ t.datetime :not_for_sale_at
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Adds Bodega::Product columns to an already-created model. Use this to make any model purchasable through Bodega.
3
+
4
+ Example:
5
+ rails generate bodega:productize skateboards
6
+
7
+ This will create:
8
+ db/migrate/XXXXXX_bodegaize_skateboards.rb
@@ -0,0 +1,19 @@
1
+ require 'rails/generators/migration'
2
+ require 'rails/generators/active_record/migration'
3
+
4
+ module Bodega
5
+ module Generators
6
+ class ProductizeGenerator < Rails::Generators::Base
7
+ argument :product_name, type: :string
8
+
9
+ include Rails::Generators::Migration
10
+ extend ActiveRecord::Generators::Migration
11
+
12
+ source_root File.expand_path('../templates', __FILE__)
13
+
14
+ def copy_migration
15
+ migration_template("migration.rb", "db/migrate/bodegaize_#{product_name.tableize}")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ class Bodegaize<%= product_name.classify.pluralize %> < ActiveRecord::Migration
2
+ def change
3
+ add_column :<%= product_name.tableize %>, :price_in_cents, :integer
4
+ add_column :<%= product_name.tableize %>, :for_sale, :boolean, default: true
5
+ add_column :<%= product_name.tableize %>, :keep_stock, :boolean, default: false
6
+ add_column :<%= product_name.tableize %>, :number_in_stock, :integer
7
+ add_column :<%= product_name.tableize %>, :for_sale_at, :datetime
8
+ add_column :<%= product_name.tableize %>, :not_for_sale_at, :datetime
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :bodega do
3
+ # # Task goes here
4
+ # end
data/script/rails ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/bodega/engine', __FILE__)
6
+
7
+ require 'rails/all'
8
+ require 'rails/engine/commands'
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bodega
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Flip Sasser
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: configurator2
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.1.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: jeweler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.8.4
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.8.4
46
+ - !ruby/object:Gem::Dependency
47
+ name: pry
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Bodega adds checkout logic to any model in your app!
63
+ email: flip@x451.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files:
67
+ - README.md
68
+ files:
69
+ - .rspec
70
+ - .rvmrc
71
+ - Gemfile
72
+ - Gemfile.lock
73
+ - MIT-LICENSE
74
+ - README.md
75
+ - Rakefile
76
+ - VERSION
77
+ - app/assets/images/bodega/.gitkeep
78
+ - app/assets/javascripts/bodega/application.js
79
+ - app/assets/stylesheets/bodega/application.css
80
+ - app/controllers/bodega/application_controller.rb
81
+ - app/controllers/bodega/orders_controller.rb
82
+ - app/helpers/bodega/application_helper.rb
83
+ - app/helpers/bodega/cart_helper.rb
84
+ - app/models/bodega/order.rb
85
+ - app/models/bodega/order_product.rb
86
+ - app/models/bodega/product.rb
87
+ - app/views/bodega/orders/new.html.erb
88
+ - app/views/bodega/orders/show.html.erb
89
+ - bodega.gemspec
90
+ - config/routes.rb
91
+ - db/migrate/20121111170337_create_bodega_orders.rb
92
+ - db/migrate/20121111170420_create_bodega_order_products.rb
93
+ - lib/bodega.rb
94
+ - lib/bodega/engine.rb
95
+ - lib/bodega/monetize.rb
96
+ - lib/bodega/payment_method.rb
97
+ - lib/bodega/payment_method/base.rb
98
+ - lib/bodega/payment_method/paypal.rb
99
+ - lib/bodega/version.rb
100
+ - lib/generators/bodega/install/install_generator.rb
101
+ - lib/generators/bodega/product/USAGE
102
+ - lib/generators/bodega/product/product_generator.rb
103
+ - lib/generators/bodega/product/templates/migration.rb
104
+ - lib/generators/bodega/productize/USAGE
105
+ - lib/generators/bodega/productize/productize_generator.rb
106
+ - lib/generators/bodega/productize/templates/migration.rb
107
+ - lib/tasks/bodega_tasks.rake
108
+ - script/rails
109
+ homepage: http://github.com/flipsasser/bodega
110
+ licenses:
111
+ - MIT
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ segments:
123
+ - 0
124
+ hash: 640289259444713481
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ! '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 1.8.24
134
+ signing_key:
135
+ specification_version: 3
136
+ summary: Bodega adds checkout logic to any model in your app!
137
+ test_files: []