Rails 4 delegate - ruby-on-rails

I am really struggling with the delegate concept and how to put it into practice.
I have a Contract model which has the following fields:
company_id,
contract_type (Standard, Unlimited, Guaranteed etc etc),
fixed_cost,
cost_per_hour,
included_time,
time_credit,
there is an Invoice model which belongs to Contract and has the following fields:
contract_id,
startdate,
enddate,
total_time_used,
start_credit,
end_credit,
total_cost
I want to run a service object to create an Invoice instance, populate the total_time_used(from another table), start_credit (from the Contract table) and then calculate the total_cost. This is calculated using data from the Contract table and the methodology is different depending on the contract_type in the Contract model e.g. Standard, Unlimited.
I don't want a big if statement in the service object as there may end up being multiple types of contract. I see that I can have Standard, Unlimited classes with the same named method but different functionality e.g. calculate_cost but I can't quite see how to actually do it such that this method also uses data retrieved from the Contract table.
Does that make sense - probably not but if it does any guidance will be gratefully received.

Your service object class should look like:
class Service
attr_reader :contract
delegate :method1, :method2, to: :contract
def initialize(contract)
#contract = contract
end
end
Then you can call method1 and method2 directly inside Service

Delegate? You mean inheritance? It seems like your problem is calculating total cost, based on the type of contract. This is typically solved using inheritance on Contract. e.g. define a StandardContract and UnlimitedContract which both derive from Contract. Problem here is: when using ActiveRecord models you will have to switch to single table inheritance (STI).
An alternative is just extracting the calculation. So in lib/contract_calculator.rb you write
class ContractCalculator
def initialize(contract)
#contract = contract
end
def total_cost(invoice)
# default implementation or empty?
end
end
and then you can add your sub-classes. In lib\standard_contract_calculator.rb
class StandardContractCalculator < ContractCalculator
def total_cost(invoice)
# do the calculation for a standard-contract
end
end
In lib\unlimited_contract_calculator.rb
class UnlimitedContractCalculator < ContractCalculator
def total_cost(invoice)
# do the calculation for a standard-contract
end
end
Now you can add the following code to Contract
def calculator
#calculator = if contract_type == 'Standard'
StandardContractCalculator.new(self)
elsif contract_type == 'Unlimited'
UnlimitedContractCalculator.new(self)
else
ContractCalculator.new(self)
end
end
def total_cost(invoice)
calculator.total_cost(invoice)
end
I made some shortcuts, like I have no idea how you decide what type of contract it is. So that "factory" code (creating the correct calculator based on the contract) you will definitely have to adapt. I assumed invoice to be a parameter, for simplicity. You could also hand it down in the constructor?
Hopefully this will get you started in the right direction.

Related

Rails generic method for multiple classes

I have model named Order which belongs to User, Admin, Device etc.
I want to see total of orders for specific object like user.
so I have to write in user.rb model
def total_sales
// there are some dates & status conditions too
orders.sum(:total)
end
but for admin, device etc. I have to write exact same code in admin.rb & device.rb
I want to write code on just one place & write everywhere,
I was thinking to write a generic class like
class Calculate
def initialize(object)
#object = object
end
def total_sales
// there are some dates & status conditions too
#object.orders.sum(:total)
end
end
and than call it like
//sales of user
object = Calculate.new(user)
object.total_sales
//sales of admin
object = Calculate.new(admin)
object.total_sales
But I am not sure if this is standard way,
Whats the better way achieve this.
Use mixin for this, create a module like below.
module CommonMethods
def total_sales
// there are some dates & status conditions too
self.orders.sum(:total)
end
end
include the module in each class like User, Admin, Device etc.
class User
include CommonMethods
end

Ruby class which could take an array of rules

I'm in the process of learning Ruby/Rails. I'm currently learning to create a model/classes.
I have an existing class that has these methods
def duration
(start_on..end_on).count
end
def items
space.available_items
end
def available_items
duration >= 365 ? items.not_including_insurance : items
end
In the class above, I have a method called available_items that checks if the duration is more than 365 days, then item will not be included in the array.
Rather than coupling the duration logic to the existing class, I think it's better to encapsulate it in another class which could take an array of rules to apply to the items.
So instead, in the above class, in the available_items method I can do something like:
policy.apply(items)
which will return all of the items which satisfy the rules.
And in the future, I can append more rules and keeps it flexible.
After includeing you module you still can define available_items method with custom rules (and have a generic one upstream), this is the easy way.
As for "passing" parameters to a strategy - it can be accomplished in a Concern like:
module SomePolicy
extend ActiveSupport::Concern
module ClassMethods
def my_policiable(param)
has_many :prices, class_name: 'Pricing::SimplePrice', as: :priceable
# ...
define_method(:available_items) {
if param == :foo
# ...
end
}
end
end
end
include SomePolicy
my_policiable(:foo)
trick is that the method is run in class context, there based on params you can define methods differently (but note that in example above if param.. will run for each resulting method invocation)

Can someone assist with translating this Object Oriented Design diagram example into code?

I am going through Sandi Metz's Practical Object Oriented Design. In Chapter 4, Creating Flexible Interfaces, I am presented with some UML diagrams to show the interactions and messages of two classes. For example, below is the following diagram from the book:
The description of this image is the following:
Therefore, this sequence diagram can be read as follows: Customer Moe sends the suitable_trips message to the Trip class, which is activated to process it and then, when finished, returns a response.
Would an implementation like the following be accurate?
class Customer
attr_reader :name, :on_date, :of_difficulty, :need_bike
def initialize(name, on_date, of_difficulty, need_bike)
#name = name
#on_date = on_date
#of_difficulty = of_difficulty
#need_bike = need_bike
end
end
class Trip
attr_reader :trip
def initialize(trip)
#trip = trip
end
def suitable_trips(on_date, of_difficulty, need_bike)
#gives list of suitable trips based on factors
end
end
moe = Customer.new("moe", "now", "easy", "yes")
trip = Trip.new('Grand Canyon')
trip.suitable_trips(moe.on_date, moe.of_difficulty, moe.need_bike)
Sometimes I think I get it but then I'll run into another UML diagram and then get confused by wording especially when it comes to receiver and sender. I just want to be sure I'm getting this right so I understand completely where methods are supposed to go and in which class.
Therefore, this sequence diagram can be read as follows: Customer Moe sends the suitable_trips message to the Trip class, which is activated to process it and then, when finished, returns a response.
Let’s implement it exactly as stated.
class Trip
class << self # class methods
def suitable_trips(on_date, of_difficulty, need_bike)
# gives list of suitable trips based on factors
[Trip.new(...), Trip.new(...), ...]
end
end
end
class Customer
attr_reader :name
def initialize(name)
#name = name
end
# this is a search function, specific for Moe
def best_fit(on_date, of_difficulty, need_bike)
trips = Trip.suitable_trips(on_date, of_difficulty, need_bike)
# get the best accordingly to some rules
# that are specific for Moe
trips.find { |trip| ... }
end
end
And use it like:
moe = Customer.new("Moe")
moe.best_fit("2019-06-01", :hard, false)
#⇒ Trip instance or `nil`
Sidenote: there are tons of great books on Ruby and my personal advice would be to pick up one of them instead. Sandi Metz is probably a great populist, but it makes sense to read tutorials from people who are actually good at coding.

How should I refactor and extract these methods in fat User model in rails?

I am currently refactoring a User model with over 800 lines of code (with several hundred more that have been added via mixins). Currently there are quite a few methods in the model like the ones below that are simply used to identify a type of user based off of some specific criteria.
def is_a_manager?
# logic to determine manager
end
def is_a_teacher?
# logic to determine teacher
end
def is_a_parent?
# logic to determine parent
end
def is_a_student?
# logic to determine student
end
def is_a_coach?
# logic to determine coach
end
def is_a_employee?
# logic to determine employee
end
What is the best way to refactor this code? Should I just put it in a concern and Include it in the class? Or should I extract all of these methods into its own separate class such as a UserIdentifier.new(user).is_teacher?? Or is this kind of refactor completely unnecessary?
You can use dry concept to check type:
def method_name(user_type)
//logic example
type == user_type
end
call method like:
User.method_name("Teacher")
I hope this help you.

Should I should subclass this Rails model?

I have a model called Coupon, which can either be set to have a money_off or percent_off attributes (it can only have one set at a time).
Also depending on whether the Coupon is money_off or percent_off changes which methods are used.
Im wondering if i should be using Single table inheritance to eseentially sub class Coupon and have a sub class that deals with percent off coupons and another dealing with money off coupons?
I would then like to know how a user would be able to select this from the view.
Here's an example that illustrates the usage of strategies (about which Yam posted a more detailed answer):
class Coupon < Struct.new(:original_price, :amount_off, :type)
def price_after_discount
discount_strategy.call(self)
end
private
def discount_strategy
# note: no hardcoding here
klass = type.to_s.camelize # :money_off to 'MoneyOff'
"Coupon::#{klass}".constantize.new
end
class MoneyOff
def call(coupon)
coupon.original_price - coupon.amount_off
end
end
class PercentOff
def call(coupon)
coupon.original_price * (1.0 - coupon.amount_off / 100.0)
end
end
end
Coupon.new(150, 10, :money_off).price_after_discount # => 140
Coupon.new(150, 10, :percent_off).price_after_discount # => 135.0
Now, instead of creating a strategy internally, we can accept it in constructor, thus making the strategy "injectable".
The best way is to determine which functionality you require for each class. If you only need a small amount of changes, then stick to a single class with an enum:
#app/models/coupon.rb
class Coupon < ActiveRecord::Base
enum type: [:percent, :money]
def value
if type.percent?
# ...
elsif type.money?
# ...
end
end
end
This will allow you to use the type in your instance methods, which shouldn't cause such a problem if you didn't have a lot of changes to make within the class.
This would allow you to call:
#coupon = Coupon.find x
#coupon.value #-> returns value based on the type
--
The alternative (STI) would be more of a structured change, and would only work if you were referencing each class explicitly:
#app/models/coupon.rb
class Coupon < ActiveRecord::Base
end
#app/models/percent.rb
class Percent < Coupon
def amount
# ...
end
end
#app/models/money.rb
class Money < Coupon
def takeout
# ...
end
end
An important factor here is how you call these.
For the above classes, you have to reference the subclassed classes on their own:
#percentage_coupon = Percent.find x
#money_coupon = Money.find y
This will obviously be more cumbersome, and may even cause problems with your routes & controllers etc.
.... so it may be best going with the single class :)
What you can do is maintain the strategy internally, and provide methods such as price, discounted?, discounted_price. In addition, whether or not the admin chose to enter percentages or fixed units, you can still supply both methods: discount_pct, discount_units which would internally realize how to compute their return values.
This way the original class still supports the concept (same as the data model), yet is also flexible enough to allow various ways of providing it the necessary input. Whether you wish to show customers the pct off, or the fixed price units, you can do so independently of the admin's preferred method of input.
Even internal methods can use these abstractions. And if it turns out you're if/elsing all over the place internally, you can create nested classes for strategies and instantiate the right one once you get the record from the DB.

Resources