Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add shops and shop_inventory_items #6

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0aa6ca5
add shops
mlapeter May 8, 2013
93ad07b
added ShopInventoryItems
mlapeter May 8, 2013
9e08d44
adding missing files
mlapeter May 9, 2013
773ab6e
schema
May 9, 2013
492ca39
Merge branch 'add-shops' of https://github.com/OpenSourcePortland/obe…
May 9, 2013
bc36af2
added good model and beginning to write wrapper methods for inventory…
May 9, 2013
d0c519a
finish adding wrapper methods to shop
mlapeter May 10, 2013
df4c7ff
add buy and sell actions to store
mlapeter May 14, 2013
c1bf794
fixing schema issue
May 15, 2013
943952c
added character_inventory_item table and added tests
May 15, 2013
5f694db
added character_inventory_item table and added tests
May 15, 2013
6a32f30
fixing merge conflict
May 15, 2013
93b198f
adding reminder to finish shop#sell method
May 15, 2013
cdf8ca6
completed buy and sell methods for Shop
mlapeter May 16, 2013
020506f
mark added Qaurtermaster model and transactionable
mlapeter May 16, 2013
25fd746
working on tests for quartermaster and transactionable
mlapeter May 17, 2013
57af5d3
finishing refactoring of quartermaster and transactionable
bethtabler May 17, 2013
c055762
created polymorphic possessions model and owning module
May 17, 2013
54a79a9
created polymorphic possessions model and owning module
May 17, 2013
b229986
Merge branch 'add-shops' of https://github.com/OpenSourcePortland/obe…
May 17, 2013
15774a6
fix test failure due to same name method in owning and transactionable
mlapeter May 20, 2013
1a14e6c
fix merge conflict between master and add-shops
mlapeter May 20, 2013
2c70efb
in progress cleaning up character shop specs
mlapeter May 20, 2013
916f138
adding support specs
mlapeter May 20, 2013
7631c78
getting owning specs running as shared examples for shops and characters
May 20, 2013
a2a3d52
cleaning up shop and character inventory_items
May 20, 2013
e77651e
removing unused files
May 20, 2013
636d2e9
adding polymorphic wares table
May 20, 2013
ccf0c1d
adding vending module and tests
May 21, 2013
b31a804
adding vending module and tests
May 21, 2013
eb3299b
Merge branch 'add-shops' of https://github.com/OpenSourcePortland/obe…
May 21, 2013
c27a589
Merge branch 'add-shops' of https://github.com/OpenSourcePortland/obe…
May 21, 2013
1767d45
Merge branch 'add-shops' of https://github.com/OpenSourcePortland/obe…
May 21, 2013
8e7430f
Move nil-fixing code from migration to separate maintenance rake task
marktabler May 21, 2013
5f8c855
mayday mayday mayday
mlapeter May 22, 2013
2233956
converting owning and vending into transactionable
May 22, 2013
5a95e9f
transactionable and quartermaster working
mlapeter May 22, 2013
da858e1
adding tests
mlapeter May 23, 2013
3e61695
done with transactionable and quartermaster and full tests
mlapeter May 23, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/assets/javascripts/goods.js.coffee
@@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
3 changes: 3 additions & 0 deletions app/assets/javascripts/shop_inventory_items.js.coffee
@@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
3 changes: 3 additions & 0 deletions app/assets/javascripts/shops.js.coffee
@@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
3 changes: 3 additions & 0 deletions app/assets/stylesheets/goods.css.scss
@@ -0,0 +1,3 @@
// Place all the styles related to the Goods controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
3 changes: 3 additions & 0 deletions app/assets/stylesheets/shop_inventory_items.css.scss
@@ -0,0 +1,3 @@
// Place all the styles related to the ShopInventoryItems controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
3 changes: 3 additions & 0 deletions app/assets/stylesheets/shops.css.scss
@@ -0,0 +1,3 @@
// Place all the styles related to the Shops controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
2 changes: 2 additions & 0 deletions app/controllers/goods_controller.rb
@@ -0,0 +1,2 @@
class GoodsController < ApplicationController
end
1 change: 1 addition & 0 deletions app/controllers/locations_controller.rb
Expand Up @@ -9,6 +9,7 @@ def index

def show
@location = Location.find(params[:id])
@shops = @location.shops

respond_to do |format|
format.html
Expand Down
103 changes: 103 additions & 0 deletions app/controllers/shops_controller.rb
@@ -0,0 +1,103 @@
class ShopsController < ApplicationController

def index
@shops = Shop.all

respond_to do |format|
format.html # index.html.erb
format.json { render json: @shops }
end
end

def show
@shop = Shop.find(params[:id])

respond_to do |format|
format.html # show.html.erb
format.json { render json: @shop }
end
end

def new
@shop = Shop.new

respond_to do |format|
format.html # new.html.erb
format.json { render json: @shop }
end
end

def edit
@shop = Shop.find(params[:id])
end

def create
@shop = Shop.new(params[:shop])

respond_to do |format|
if @shop.save
format.html { redirect_to @shop, notice: 'Shop was successfully created.' }
format.json { render json: @shop, status: :created, location: @shop }
else
format.html { render action: "new" }
format.json { render json: @shop.errors, status: :unprocessable_entity }
end
end
end


def update
@shop = Shop.find(params[:id])

respond_to do |format|
if @shop.update_attributes(params[:shop])
format.html { redirect_to @shop, notice: 'Shop was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @shop.errors, status: :unprocessable_entity }
end
end
end


def destroy
@shop = Shop.find(params[:id])
@shop.destroy

respond_to do |format|
format.html { redirect_to shops_url }
format.json { head :no_content }
end
end

def sell_to_character
shop = Shop.find(params[:id])
character = Character.first
item = ShopInventoryItem.find(params[:item_id])
quantity = params[:quantity].to_i
respond_to do |format|
if shop.sell(character, item, quantity)
format.html { redirect_to shop, notice: 'Item sold to character!' }
else
format.html { redirect_to shop, notice: 'Item not sold!' }
end
end
end

def buy_from_character
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope nope nope nope. :)

This is too much logic for a controller. We will definitely need an endpoint to handle this kind of thing, but it might not even live in the Shops controller. Here are a couple of possibilities:

  1. We make a Transactions controller - we can do this without creating a Transactions model if we want to. Transaction#create takes a Seller, a Buyer, and a Good, and delegates the underlying logic to a single selected model (i.e., all transactions are handled as a .buy method on the buyer or as a .sell method on the seller, but it's only one and it's the same every time)
  2. We make a "transact" action in the Shops controller. It looks up a Shop and a Customer, then passes relevant params (good, quantity, buy/sell instructions) and delegates the logic to the Shop model specifically.
  3. We make a "transfer" action in a Goods controller. It looks up a Good, then passes relevant params (buyer, seller, quantity) and delegates the logic to a single underlying model as above.

The important pattern is the "delegates the logic" bit. A controller should only support the channel over which a conversation is happening.

Have you ever had a relay call? It's where a person using a TDD is calling a person using a regular telephone. A TDD is text-based, of course, so there needs to be an adapter of some kind in the middle. That adapter is a Relay Operator, whose job is to read the text coming from the TDD out loud to the person on the phone, then transcribe the audio from the phone as text to the person using the TDD.

The Relay Operator is kind of like a controller. They can't actually participate in the conversation - they can only facilitate it. They are limited to helping with the actual channel of the conversation - you can ask them to re-read the last sentence, and you can ask them to spell a word as it was typed, but nothing else. I remember asking once whether a relay service as billable per minute, per call, or free, because it made a difference as to how I was going to handle the call; the operator duly typed the question to the customer to let them explain (it's a free, nationally-provided service).

So, in this buy/sell situation, our Controller should pass along the message between the right parties, but only that - incoming parameters, outgoing data. When asked questions like "What does buying a thing actually mean" or "how many parties are involved," the controller shouldn't know. It should know "Shop#buy: I send a good, a buyer, and a quantity to the correct shop. If successful, I send this message back; if not successful I send this other message back."

shop = Shop.find(params[:id])
character = Character.first
item = ShopInventoryItem.find(params[:item_id])
quantity = params[:quantity].to_i
respond_to do |format|
if shop.buy(character, item, quantity)
format.html { redirect_to shop, notice: 'Item purchased from character!' }
else
format.html { redirect_to shop, notice: 'Item not purchased!' }
end
end
end


end
37 changes: 37 additions & 0 deletions app/controllers/transactions_controller.rb
@@ -0,0 +1,37 @@
class TransactionsController < ApplicationController

def create
find_resources
Quartermaster.transact(@buyer, @seller, @good, params[:quantity])
end


private

def find_resources
find_buyer
find_seller
find_good
end

def find_buyer
if params[:buyer_type] == "character"
@buyer = Character.find(params[:buyer_id])
else
@buyer = Shop.find(params[:buyer_id])
end
end

def find_seller
if params[:seller_type] == "character"
@seller = Character.find(params[:seller_id])
else
@seller = Shop.find(params[:seller_id])
end
end

def find_good
@good = Good.find(params[:good_id])
end

end
2 changes: 2 additions & 0 deletions app/helpers/goods_helper.rb
@@ -0,0 +1,2 @@
module GoodsHelper
end
2 changes: 2 additions & 0 deletions app/helpers/shops_helper.rb
@@ -0,0 +1,2 @@
module ShopsHelper
end
49 changes: 47 additions & 2 deletions app/models/character.rb
@@ -1,19 +1,26 @@
class Character < ActiveRecord::Base
attr_accessible :gender, :name, :species, :location_id #:profile_attributes, :profiles_attributes
attr_accessible :gender, :name, :species, :location_id

has_one :profile
has_one :ship
belongs_to :location
has_many :properties

has_many :possessions, :as => :ownable
has_many :wares, :as => :vendable

PLAYER_ATTRIBUTES = [:compassion, :courage, :dependability,
:endurance, :honesty, :honor,
:charisma, :leadership, :logistics,
:perception, :pilot, :quickness,
:tactical, :technical, :wit]

PLAYER_TURNS = 2016


#include Owning
#include Vending
include Transactionable

def increment_turn(turns)
self.turns_spent += turns
end
Expand All @@ -29,6 +36,44 @@ def turns_remaining
def game_over?
turns_remaining == 0 #true
end

def inventory_items
possessions
end

def will_buy?(good)
true
end
#
# def initialize_inventory_item(good, quantity)
# character_inventory_items.create!(good_id: good.id, quantity: quantity)
# end
#
# def increase_inventory_item(good, quantity)
# add_good(good, quantity)
# end

# def quantity_of(good)
# character_inventory_items.find_by_good_id(good.id) ? character_inventory_items.find_by_good_id(good.id).quantity : 0
# end

# def buy_good(good, quantity)
# stock(good, quantity)
# end

# def valid_quantity?(good, quantity)
# character_inventory_items.find_by_good_id(good.id) && quantity_of(good) >= quantity
# end

# def sell_good(good, quantity)
# if valid_quantity?(good, quantity)
# item = character_inventory_items.find_by_good_id(good.id)
# updated_quantity = item.quantity - quantity
# item.update_attribute(:quantity, updated_quantity)
# else
# false #mark review - should these be exceptions instead?
# end
# end

def enough_turns?(turns_required)
turns_remaining >= turns_required
Expand Down
9 changes: 9 additions & 0 deletions app/models/good.rb
@@ -0,0 +1,9 @@
class Good < ActiveRecord::Base
attr_accessible :name

has_many :shops, :through => :shop_inventory_items
has_many :shop_inventory_items
has_many :character_inventory_items
has_many :characters, :through => :character_inventory_items

end
1 change: 1 addition & 0 deletions app/models/location.rb
Expand Up @@ -2,6 +2,7 @@ class Location < ActiveRecord::Base
attr_accessible :category, :name, :x, :y, :z

has_many :properties
has_many :shops

validates :x, :y, :z, :presence => true

Expand Down
9 changes: 9 additions & 0 deletions app/models/possession.rb
@@ -0,0 +1,9 @@
class Possession < ActiveRecord::Base
attr_accessible :good_id, :ownable_id, :ownable_type, :quantity

belongs_to :ownable, polymorphic: true

def should_be_destroyed?
quantity <= 0
end
end
106 changes: 106 additions & 0 deletions app/models/quartermaster.rb
@@ -0,0 +1,106 @@
class Quartermaster

#Quartermaster.transact(Character.first, Shop.first, Good.first, 20)

def self.transact(buyer, seller, good, quantity)
self.new.transact(buyer, seller, good, quantity)
end

def transact(buyer, seller, good, quantity)
@buyer = buyer
@seller = seller
@good = good
@quantity = quantity
perform_transaction
end

def perform_transaction
if transaction_valid?
@buyer.add_good(@good, @quantity)
@seller.reduce_good(@good, @quantity)
true
else
false
end
end

def transaction_valid?
validate_stock && validate_quantity && validate_can_afford && validate_will_buy
end

def validate_shop_trades_in
@buyer.trades_in?(@good)
end

def validate_stock
@seller.has_enough?(@good, @quantity)
end

def validate_quantity
@quantity >= 0
end

def validate_can_afford
true
# Pending implementation of currency
end

def validate_will_buy
@buyer.will_buy?(@good)
end

end



# def character.buy_good(character, good_receiving, quantity_receiving, quanity_giving)
# trade(giver, getter, giving, quantity_giving, getting, getting_quantity)
# end
#
#
#
#
# 50 gold
#
# def buy_good(character, good_receiving, quantity_receiving, quanity_giving) #from store
# trade(giver, getter, gold, quantity_giving, getting, getting_quantity)
# end
#
# def sell_good() #to store
# trade(giver, getter, giving, quantity_giving, gold, getting_quantity)
# end
#
# def transact(giver, getter, giving, quantity_giving, getting, getting_quantity)
# #transact
# end
#
# def offer_trade(offering, offering_quantity, getting, getting_quantity)
#
# end
#
# # store buys from character joe (buy_price)
# # character joe sells_to_store #store dictates (buy_price)
# joe.reduce_good(good, 10)
# store.add_good(good, 10)
# joe.add_good(gold, store.buy_price_of(good))
# store.reduce_good(gold, store.buy_price_of(good))
#
# # character buys from store #store dictates (sell_price)
# joe.add_good(good, 10)
# store.reduce_good(good, 10)
# store.add_good(gold, store.sell_price_of(good))
# joe.reduce_good(gold, store.sell_price_of(good))
#
# # mary buys from joe
# # character joe sells_to_character mary # selling character joe dictates (sell_price)
# joe.reduce_good(good, 10)
# mary.add_good(good, 10)
# joe.add_good(gold, joe.sell_price_of(good))
# mary.reduce_good(gold, joe.sell_price_of(good))
#
# # character joe buys from character mary # selling character mary dictates (sell_price)
# joe.add_good(good, 10)
# mary.reduce_good(good, 10)
# joe.reduce_good(gold, mary.sell_price_of(good))
# mary.increase_good(gold, mary.sell_price_of(good))
#