Properly modeling monetary transactions in Rails 4 - ruby-on-rails

I am making a toy application to learn Rails 4 (without just cloning a tutorial).
Users sign up (I'm using the Devise gem to take care of user authentication), and a BTC pub/prv keypair is generated, and an address is computed and displayed to the user (in a flash message), so they can top off their account. Other Users sign up, and anyone can search for anyone and a dropdown is dynamically populated with every single user, but filters down names as a User types the name of their friend/associate, whoever they want to send Bitcoin to. I am only using testnet for this idea at the moment, no real BTC (don't worry!).
Anyways, here is my idea for modeling this application:
class User < ActiveRecord::Base
has_many :account
end
class Tx < ActiveRecord::Base
has_one :receiver, class => "account"
belongs_to :user, through :account
end
class Account < ActiveRecord::Base
belongs_to :user
has_many :tx
end
The reason why I don't like the above is because in my mind it seems that a Tx (short for transaction since transaction is a reserved word in Rails) actually belongs to two users, but my readings seem to indicate that I can't have something like this:
class User < ActiveRecord::Base
has_many :tx
end
class Tx < ActiveRecord::Base
has_one :receiver, class => "user"
has_one :sender, class => "user
end
Which of these implementations is better? I appreciate any insight into this model.

I'd go with the second method. I went with "transfers" instead of "tx", for readability - but you can name it as you please.
class User < ActiveRecord::Base
has_many :transfers
has_many :received_transfers, :class_name => "Transfer", :foreign_key => "receiver_id"
end
class Transfer < ActiveRecord::Base
belongs_to :user # Sender
belongs_to :receiver, :class => "User"
end
Testing it:
>> Transfer.create(:user_id => 1, :receiver_id => 2, :amount => 4.00)
=> #<Transfer id: 1, user_id: 1, receiver_id: 2, amount: #<BigDecimal:7fb3bd9ba668,'0.4E1',9(36)>, created_at: "2014-09-03 04:35:47", updated_at: "2014-09-03 04:35:47">
>> User.first.transfers
=> #<ActiveRecord::Associations::CollectionProxy [#<Transfer id: 1, user_id: 1, receiver_id: 2, amount: #<BigDecimal:7fb3c10682f0,'0.4E1',9(18)>, created_at: "2014-09-03 04:35:47", updated_at: "2014-09-03 04:35:47">]>
>> User.last.received_transfers
=> #<ActiveRecord::Associations::CollectionProxy [#<Transfer id: 1, user_id: 1, receiver_id: 2, amount: #<BigDecimal:7fb3bdabace8,'0.4E1',9(18)>, created_at: "2014-09-03 04:35:47", updated_at: "2014-09-03 04:35:47">]>
Happy coding!

Related

Dealing with has_one and has_many associations with one user model

I currently have a User model
class User < ApplicationRecord
has_one :leases
end
a Lease model
class Lease < ApplicationRecord
belongs_to :tenant, class_name: 'User'
belongs_to :landlord, class_name: 'User'
end
and a Rental model
class Rental < ApplicationRecord
has_one :lease, dependent: :destroy
end
and I only have one user model supporting tenants and landlords.
The problem I'm facing is that a landlord can have multiple leases with many different tenants, but tenants can only have one lease at a time.
I'm somewhat confused on how I should structure this properly. Should I have a has_many relation with the User model and the Lease model instead of the has_one and then just a method say lease on the User model to get the lease for a tenant?
What I would like to have is something like
tenant.lease
and
landlord.leases
Could I just do?
class User < ApplicationRecord
has_one :lease, foreign_key: "tenant_id"
has_many :leases, foreign_key: "landlord_id"
end
this seems to work, but I'm not sure if its the right way to go.
Using STI, you could have the following models
==> user.rb <==
class User < ApplicationRecord
end
==> landlord.rb <==
class Landlord < User
has_many :leases
end
==> tenant.rb <==
class Tenant < User
has_one :lease
end
==> lease.rb <==
class Lease < ApplicationRecord
belongs_to :tenant
belongs_to :landlord
end
Then you can do things like
irb(main):003:0> landlord = Landlord.create(name: 'the landlord')
=> #<Landlord id: 5, name: "the landlord", type: "Landlord", created_at: "2022-01-03 22:16:56", updated_at: "2022-01-03 22:16:56">
irb(main):004:0> tenant = Tenant.create(name: 'the tenant')
=> #<Tenant id: 6, name: "the tenant", type: "Tenant", created_at: "2022-01-03 22:17:09", updated_at: "2022-01-03 22:17:09">
irb(main):005:0> lease = Lease.create(tenant: tenant, landlord: landlord)
=> #<Lease id: 3, tenant_id: 6, landlord_id: 5, created_at: "2022-01-03 22:17:28", updated_at: "2022-01-03 22:17:28">
irb(main):006:0> tenant.lease
=> #<Lease id: 3, tenant_id: 6, landlord_id: 5, created_at: "2022-01-03 22:17:28", updated_at: "2022-01-03 22:17:28">
irb(main):007:0> landlord.leases
=> #<ActiveRecord::Associations::CollectionProxy [#<Lease id: 3, tenant_id: 6, landlord_id: 5, created_at: "2022-01-03 22:17:28", updated_at: "2022-01-03 22:17:28">]>
To accomplish this, you only need to add a type column to your User model and Rails will take care of the rest.
In my opinion, you should go with has_many approach. As landlord and tenant both are users. in place of using tenant_id and landlord_id, you have to use 'user_id' and to identify user, use user_type i.e. tenant and lanlord.
Just an opinion !!

Rails 4 converting a polymorphic association to has_many through

A Manager has many contacts via polymorphic association
class Manager
has_many :contacts, as: :contactable
end
class Contact
belongs_to :contactable, polymorphic: true
end
The relation works fine but now a contact can be associated to many managers.
So, added a new model Contactable, a joins table 'contactables' and moved contactable_id and contactable_type fields from contacts table to contactables table.
class Contactable
belongs_to :contact
belongs_to :contactable, polymorphic: true
end
Now confused about the Manager and Contact relation that how it would be defined in models correctly to make it working. Tried the following but it doesn't work:
class Manager
has_many :contacts, through: :contactables, source: :contactable, source_type: "Contact"
end
So I checked this interesting topic and will tell what I know.
When you create objects as usual in has_many :through:
class Contact
has_many :contactables
has_many :managers, :through => :contactables
end
class Manager
has_many :contactables
has_many :contacts, :through => :contactables
end
class Client
has_many :contactables
has_many :contacts, :through => :contactables
end
class Contactable
belongs_to :contact
belongs_to :manager
belongs_to :client
end
You get to use foreign keys fro each referenced object. Polymorphic looks like a great solution. So:
class Contactable
belongs_to :contact
belongs_to :polymorphic_model, polymorphic: true
end
class Contact
has_many :contactables
has_many :managers, :through => :contactables, :source => :polymorphic_model, :source_type => 'Manager'
end
class Manager
has_many :contactables, :as => :polymorphic_model
has_many :contacts, :through => :contactables
end
Setting the :as option indicates that this is a polymorphic
association
:source => :polymorphic_model is used to tell Rails to get the related object from the subclass. :source means the same as :class_name. Without this option Rails would try to get associated Manager from the Contactables table, while it should be reached via virtual Polymorphic_model.
By adding belongs_to :polymorphic_model to Contactable you enable Contact (witch already sits there, because of belongs_to :contact) to be associated with a Manager or Client, because thats what Polymorphic association does - references two or more parent tables. And because Contact have_many Contactables, the same Contact object can be associated with many managers or clients. So after you understand it, it looks really simple - Joined model belongs to Contact and Joined model also holds references to Manager and Client through Polymorphic association. So in order for Contact to have many managers, you create another Contactable object that belongs to the same Contact, but different Manager. Doesn't look super efficient, but personally me, not knowing a better way..
Here is a tested proof:
Manager.create!(name: "Bravo")
=> #<Manager id: 1, created_at: "2017-04-12 12:17:41", updated_at: "2017-04-12 12:17:41", name: "Bravo">
Manager.create!(name: "Johnny")
=> #<Manager id: 2, created_at: "2017-04-12 12:18:24", updated_at: "2017-04-12 12:18:24", name: "Johnny">
Contact.create!(number:"123")
=> #<Contact id: 1, created_at: "2017-04-12 12:18:59", updated_at: "2017-04-12 12:18:59", number: 123>
c = Contactable.new
c.contact = Contact.first
c.unit = Manager.first
c
=> #<Contactable id: nil, unit_type: "Manager", created_at: nil, updated_at: nil, unit_id: 1, contact_id: 1>
Now to set another Manager to the same contact, we create a new Contactable:
cc = Contactable.new
cc.contact = Contact.first
cc.unit = Manager.last
cc
=> #<Contactable id: nil, unit_type: "Manager", created_at: nil, updated_at: nil, unit_id: 4, contact_id: 1>
And to get all associated:
Contact.first.managers
Contactable's database:
contact_id
unit_id
unit_type
And one interesting quote by #Bill Karwin:
The Polymorphic Associations design breaks rules of relational
database design. I don't recommend using it.
But he wrote this long time ago. Probably irrelevant now.
Why can you not have a foreign key in a polymorphic association?

Rails Find based on a set existing in through a relation

I am trying to select an instance based on a relation of that instance containing a set. A simplified example follows:
class Product::Variation < ActiveRecord::Base
attr_accessible :name, :product_id, :quantity
belongs_to :product
has_many :bids, :foreign_key => :product_variation_id
has_many :product_variation_property_values, :class_name => 'Product::Variation::PropertyValue'
has_many :property_values, :through => :product_variation_property_values, :class_name => 'Property::Value'
end
class Product::Variation::PropertyValue < ActiveRecord::Base
attr_accessible :property_value_id, :variation_id, :property_id
belongs_to :variation
belongs_to :property_value, :class_name => 'Property::Value'
end
class Property < ActiveRecord::Base
attr_accessible :name
has_many :values, :class_name => 'Property::Value'
end
class Property::Value < ActiveRecord::Base
attr_accessible :content
belongs_to :property
belongs_to :partner
end
So now I want to do something like the following (in psuedo code):
Variation.where(:property_values includes [Property::Value.find(1), Property::Value.find(2)])
Is there a way to do this using ActiveRecord?
Thanks and let me know if you need more info.
More Info
I tried the following:
Product::Variation.joins(:property_values).where('property_values.id' => [Property::Value.find(1).id, Property::Value.find(2).id]).first
...which is the following SQL...
SELECT "product_variations".* FROM "product_variations" INNER JOIN "product_variation_property_values" ON "product_variation_property_values"."variation_id" = "product_variations"."id" INNER JOIN "property_values" ON "property_values"."id" = "product_variation_property_values"."property_value_id" WHERE "property_values"."id" IN (1, 2)
...and this returns...
#<Product::Variation id: 25, product_id: 1, quantity: 39, created_at: "2013-11-18 00:18:45", updated_at: "2013-11-18 00:18:45">
But if I do:
Product::Variation.find(25).property_values.inspect
...I get...
[#<Property::Value id: 1, property_id: 1, content: "XS", created_at: "2013-11-18 00:18:45", updated_at: "2013-11-18 00:18:45", color: nil, color_texture: nil, secondary_color: nil>, #<Property::Value id: 6, property_id: 2, content: "Dark Wood", created_at: "2013-11-18 00:18:45", updated_at: "2013-11-18 00:18:45", color: "#855E42", color_texture: "striped", secondary_color: "#FFB90F">]
But I'm looking for the Product::Variation that contains both Property::Value 1 and 2. This is returning those that contain 1 or 2.
This should be doable with a join query. Hard to write form heart, but should be something like this:
Variation.joins(propert_values: :values).where('values.id' => [Property::Value.find(1).id, Property::Value.find(2).id]).first

creating instance of belongs_to model not showing up in it's associated objects list

I've created two models with the below associations
class User < ActiveRecord::Base
has_many :roles, :dependent => :destroy
end
class Role < ActiveRecord::Base
belongs_to :user
end
class Student < Role
end
class Tutor < Role
end
However when I create a new child role, I assume it would get associated to the model it has the belongs to for.
Such as:
Tutor.create(:user_id => user_id)
I would expect:
#some user #user
#user.roles
to have an array containing a Tutor. However, it doesn't seem to be working. Any ideas what I'm doing wrong?
Once you start using Single Table Inheritance, than the Tutor that you created isn't a role, as far as active-record is concerned for this type of query.
class User < ActiveRecord::Base
has_many :roles
has_many :tutors
end
#user = User.first
#user.roles
=> []
#user.tutors
=> [#<Tutor id: 1, user_id: 1, type: "Tutor", created_at: "2012-10-26 18:15:16", updated_at: "2012-10-26 18:15:16">]
If you want to get a list of all roles that your users may have:
Role.where(user_id: #user.id).all
[#<Tutor id: 1, user_id: 1, type: "Tutor", created_at: "2012-10-26 18:15:16", updated_at: "2012-10-26 18:15:16">, #<Student id: 2, user_id: 1, type: "Student", created_at: "2012-10-26 18:18:32", updated_at: "2012-10-26 18:18:32">]

rails basic polymorphism

i'm trying to create a polymorphic relationship between votes can be submitted by users and apply to articles. my code
class Vote < ActiveRecord::Base
attr_accessible :value, :voteable_id, :voteable_type
belongs_to :voteable, :polymorphic => true
end
class User < ActiveRecord::Base
has_many :votes, :as => :voteable
end
class Article < ActiveRecord::Base
has_many :votes, :as => :voteable
end
<Vote id: 1, value: 1, created_at: "2012-07-27 03:13:14", updated_at: "2012-07-27 03:13:14", voteable_id: nil, voteable_type: nil>
From looking at the rails documentation via http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
I feel that my code is set correctly but i'm having a bit of trouble triggering it correctly, ie, how do I actually create a vote object with a properly define relationship to either an article or user?
Is votable_type is string?
Next example should work correctly..
#user.votes.new :value => 1
#user.save
.
I was able to make this work, I was setting the voteable_type attribute incorrectly.

Resources