bodega 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []