Date Based Automated Actions in Rails4 - ruby-on-rails

I'm developing a Rails4 app and I have lots of date attributes in a model. For example;
first_payment_date
last_payment_date
first_application_date
last_application_date
first_result_validation_date
last_result_validation_date
etc.
I want to automate things a little bit and want my application act with these dates. For example, user's won't be able to do payment, after the last_payment_date, or they will not be able to make an application before first_application_date.
What is the best approach to plan this kind of thing? I heard about "state machines" and state_machine GEM but I'm not sure if it's the right thing for me.
Thanks.

If these dates are in the User model, you can create several helper methods inside it to return true of false based on your conditions:
def can_deliver_payment?
self.last_payment_date > Date.today
end
def can_make_application?
self.first_application_date > Date.today
end
# etc
So now when you have an instance of User, you can check these conditions on a more readable way.

Related

Select Model based on year variable

I am building a new app in Rails for an internal project that changes slightly each year based on the requirements of our clients. Any changes between years will occur within the models (add/remove columns, formatting, reports, etc). My plan is to build it to the requirements for this year and going forward each year I will create a new model and migration (e.g. Sample2019Record, Sample2020Record) that will encapsulate the requirements for that year. The app also needs to render previous year data and all the data is scoped based on the year meaning there is no need to render or query multiple years data. I would prefer not to create a new app each year since that is more apps that need to be maintained.
So my idea is to include the year into the URL (/2018/sample/new or /sample/new?year=2018) and parse the model based on the year ("Sample#{year}Record"). Can rails handle this safely and is there a Gem that can help assist with this approach?
Here is what I came up with, thanks for the advice.
routes.rb
get '/:year/samples', to: 'samples#index', as: :samples, defaults: { year: Time.current.year }
Routes will always default to the current year
application_controller.rb
before_action :check_year
def check_year
if params.has_key?(:year)
if "Sample#{params[:year]}Record".safe_constantize.nil?
redirect_to root_path(year: Time.current.year), notice: "Invalid Year"
end
else
redirect_to root_path(year: Time.current.year), notice: "Invalid Year"
end
end
def get_sample_record(year=Time.current.year)
"Sample#{year}Record".safe_constantize
end
Added a before_action to check the year parameter and added the get_sample_record method to safely constantize the record that can be called from any controller with an optional year like so:
sample_controller.rb
sample_2018_record = get_sample_record
sample_2018_record.count
#> 304
sample_2017_record = get_sample_record 2017
sample_2017_record.count
#> 575683
The result will be nil if an invalid year is passed so I will handle the check in the controller.
As #DaveNewton said, this seems like it should work fine so long as you keep data corresponding to different years' requirements in different tables. A few other observations:
Rails has a helper method constantize for parsing a model from a string:
klass_name = "Sample#{year}Record"
record = klass_name.constantize.new()
will make the variable record an instance of your class corresponding to the year variable. You may find it helpful to use a Factory pattern to encapsulate the process.
Also. be careful how you name and organise your files. You may find this thread helpful when working with the Rails infectors for classes with numbers in their names. A big part of working with Rails is allowing its magic to work for you rather than unwittingly trying to work against it.
As a general rather than Rails-specific piece of advice, I'd also give a considerable amount of thought to how you could define a common public interface for records that will persist across years. A codebase featuring things like
if record.instance_of? Sample2018Record
record.my_2018_method
elsif record.instance_of? Sample2019Record
record.my_method_only_relevant_to_2019
...
will become very difficult to reason about, especially for developers who join after a couple of years. Ruby has extremely powerful tools to help you duck type very effectively.

How to check if resque job has finished

I have a case scenario where I need to run multiple record updates in the background(using resque) and I want to give user visual indicator of how the task is running(eg started/running/finished).
One way of achieving this(which I can think of) is saving the current state into a table, then showing the state to user by simple page refresh.
Can anyone suggest a better solution of doing it?I want to avoid creating the whole migration, model, controller for this.
Thanks
As I've commented, resque-status gem could be useful for you. I am not sure if that is an answer but since you said that you do not want to create migration, model and controller for this. Thus, a gem might be the way to go.
From the job id you can get the status you are looking for, for example:
status = Resque::Plugins::Status::Hash.get(job_id)
status.working? #=> true
There is also a front-end called resque-web, check that out too.
You may use ruby's global variable $var_name = 'foo'. However I am not sure about it, because they are considered bad practice in rails, but in this case I see them reasonable, as soon as their name is very unique.
It can be done like (in case of resque):
class UpdateJob
#queue = data
def self.perform
$my_job_name_is_running = true
MyJobName.new.run
$my_job_name_is_running = nil
end
end
then you can access them from anywhere in the app:
while $my_job_name_is_running
puts "job is running..." if $my_job_name_is_running
sleep 3 # important to not overload your processor
end
Ruby global vars are not very popular. Check docs for more info https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/globalvars.html

Testing Internationalised Dates in Rspec

I have some business logic in a controller that I want to test that involves setting two values to today's date and yesterday's date.
Initially I had a passing test that essentially looked like this;
controller:
def wibble
#start_time = Date.yesterday
end
test:
it 'blah blah'
get :wibble
assigns(:start_date).should eq(Date.yesterday)
end
But a new requirement has been added that means the date should be i18n'd, which means that the controller is returning back something different.
My Thoughts
I had thought about mocking the variables, because I could only care that they are set to something, but then the business logic of Today and Yesterday isn't being exercised.
I also considered forcing i18n on the test, but this seems way to brittle.
Can anyone suggest a good way to test this?

Evaluate whether date is not one of past dates in model -- Ruby on Rails

I have a Day model which has a date column. I have to implement the validation that the date column must not have a past date. If the date is a past date it must not get saved to the database and give the appropriate error message right on the. I know I can put this validation in the controller, but I think it violates the MVC rules i.e keeping the business logic away from the controllers.
Is there any way to put this validation in the model or anywhere else and if the date is a past date then it must redirect back to the new action with the message "Date cannot be a past date"
Please Help
Thanks in advance
In your model you need add a validation
validate :not_past_date
def not_past_date
if self.date < Date.today
errors.add(:date, 'not in past')
end
end
After in your controller, you just check if save return true or false. False is send when you don't validate your model. If false redirect to another controller.
Edit :
Like said by Simone Carletti in comment you can use #past?
validate :not_past_date
def not_past_date
if self.date.past?
errors.add(:date, 'not in past')
end
end
I know I can put this validation in
the controller, but I think it
violates the MVC rules i.e keeping the
business logic away from the
controllers.
Eeh, in Rails you should put validations in the model (not in the controller!). See:
http://guides.rubyonrails.org/active_record_validations_callbacks.html.
#Simone and #shingara using the #past? method will compare a date with the current DateTime in UTC. So you may face a problem in two occasions here:
If you want to get all elements with a DateTime set to a day, wether they began at the beginning of the day or not;
If you are working on another TimeZone.
There are workarounds to deal with it, but it's just more straightforward to do the first #shingara way: self.date < Date.today

Rails: Helpers and Models - where to organize code

More and more I'm putting all of my code in models and helpers concerning MVC.
However, sometimes I'm not sure where to organize code. Should it go into the model or should it go into a helper. What are the benefits of each. Is one faster or are they the same. I've heard something about all models getting cached so it seems then like that would be a better place to put most of my code.
For example here is a scenario that works in a model or in helper:
def status
if self.purchased
"Purchased"
elsif self.confirmed
"Confirmed"
elsif self.reserved
"Reserved"
else
"Pending"
end
end
I don't need to save this status as in the database because there are boolean fields for purchased, and confirmed, and reserved. So why put this in a model or why put it into a helper?
So I'm not sure of the best practice or benefits gained on putting code into a model or into helper if it can be in both.
Your specific example is including a business rule in the sense that if the instance of the model is both purchased and confirmed then the proper status is "purchased" not "confirmed"
So in your example, I'd definitely put the method in the model since it is coding one of your applications business rules.
A different example:
def status_string
case status
when 0: "Purchased"
when 1: "Confirmed"
else
"Pending"
end
end
In this case, the status_string method could be reasonably defined either in a View Helper or Model--it has nothing to do with any business rules, it is changing the representation of the value. I'd put it in the model since I tend to only put html-related sw into the View Helpers. But depending on your internationalization scheme, a similar method might be better placed in the View Helper.
A good example of a View Helper is an application-wide method to transform date time values into the standard representation for your app. Eg
# application_helper.rb
def date_long_s(d)
d.strftime("%A, %b *%d, %Y *%I:%M %p")
end
This is really subjective and I agree, sometimes it is not clear if something belongs in a model or helper.
For example:
# using model
status ? status.nice_name : "Pending"
# using helper
nice_name(status)
The clear advantage here for the helper is that it can handle nil objects gracefully keeping views clean. The disadvantage is that the code is now in a different location away from the model
Performance wise you will not see any significant difference between using helpers and models. It is more likely that DB round trips to pull status objects will be a bottleneck.
I use constant hashes in this kind of situations.
Hash is defined in model file like this
STATUS = {
1 => "Pending",
2 => "Confirmed"
}
I also declare constants for each status like this.
ST_PENDING = 1
Declaring this is useful when writing queries. For example,
MyModel.all(:status=>ST_PENDING)
status field in database table is number.So when printing, I simply use this.
MyModel::STATUS[obj.status]

Resources