How to write factory_girl create command using fabrication in rails - ruby-on-rails

I am learning how to use fabrication in Rails and we have decided to replace all our factory_girl code with fabrication.
Suppose we have this code in factory_girl, how will I rewrite the whole thing using fabrication?
FactoryGirl.create(
:payment,
user: user_ny,
amount: -4000,
booking: booking,
payable: payable]
)
Is this the correct code using Fabrication ? I am new to rails framework and will appreciate your help.
Fabricator(:payment) do
name { user_ny }
amount -4000
booking { booking }
payable { payable }
end

Fabrication's equivalent to FactoryGirl.create(:payment) is Fabricate(:payment).
It looks like booking and payable are other fabricators so you could write it like this:
Fabricate(:payment, name: user_ny) do
amount -4000
booking
payable
end
If you declare a relationship without a "value" it performs a default expansion and generates whatever the fabricator of the same name defines and sets it on the object.
In the case of user_ny above, the easiest way to use a local variable when fabricating is to pass it in as a parameter. You can mix and match however you want between the parameters and block syntax, although parameters will take precedence.

Related

How do I create a rails association for cypress testing?

I am trying to test a form on one particular page on my web app. The problem is that this web page depends on at least three model objects to be in the database for various reasons that I'll elaborate and since I'm new to Cypress for the testing, I'm not exactly sure how to go about this. So here are the problem areas:
describe('Basic SSL Certificate', () => {
context('csr submission', () => {
beforeEach(() => {
cy.request('POST', 'user_session/user_login', { login: 'testuser', password: 'Testing_ssl+1'})
.as('currentUser')
cy.appFactories([
['create', 'certificate_order']
]).as('certificateOrder')
})
it('rejects a numerical ip address for its csr', () => {
cy.visit(`/team/${this.certificateOrder.ssl_account.ssl_slug}/certificateOrders/${this.certificate_order.ref}/edit`);
First of all, the problem I am facing is this simple line of test code here:
cy.visit(`/team/${this.certificateOrder.ssl_account.ssl_slug}/certificateOrders/${this.certificate_order.ref}/edit`);
I need to hit the following url which looks like this /teams/abcd-xyz/certificate_orders/co-ref-1234/edit
Questions: How do I create rails associations with cypress? In my before block, I think I created a certificate order, does that have the associations with it on creation? Do I have to create each model seperately with appFactories and if I do, how do I "link" them together?
I don't see the way to combine ruby and javascript in this code and could use a pointer on setting up the factories. Usually in rspec I would create the models that I need and use them but in cypress I'm not sure how to do this because it doesn't seem to be the right way of doing it with JS. Helpful advice would be appreciated, thank you.
There are a few ways you can setup associations:
Setting the foreign keys
Using transient attributes
Using Nested Attributes
I have examples of all 3 in this post: https://medium.com/nexl-engineering/setting-up-associations-with-cypress-on-rails-and-factorybot-3d681315946f
My preferred way is using transient attributes as it makes is very readable from within Cypress
Example:
FactoryBot.define do
factory :author do
name { 'Taylor' }
end
factory :post do
transient do
author_name { 'Taylor' }
end
title { 'Cypress on Rails is Awesome' }
author { create(:author, name: author_name ) }
end
end
Then in Cypress you can do the following:
// example with overriding the defaults
cy.appFactories([['create', 'post', { title: 'Cypress is cool', author_name: 'James' }]]
// example without overriding
cy.appFactories([['create', 'post']]

Active Record find from factory girl

Im trying to implement a database-based sequence generator in rails so i wrote a code that goes something like
#semaphore.synchronize {
seq = Sequence.find_by_name(type)
seq.value += 1
seq.save
val = seq.value
unless prefix.nil?
"#{prefix}-#{val}"
else
"#{val}"
end
}
My question is, is it possible to setup the initial sequence data using factory girl and be able to access it using Sequence.find_by_name or fixture loading is my only option? i.e. rake db:fixtures:load RAILS_ENV=test FIXTURES_PATH=spec/fixtures?
Thanks
Ok, i was able to find the answer.
I created my Sequence definition in factory girl as below:
FactoryGirl.define do
factory :membership_sequence, class: Sequence do
name 'membership'
value 1
end
factory :payment_sequence, class: Sequence do
name 'payment'
value 1
end
end
Then, in the before(:all) I called the create() method
before(:all) do
# create the sequences
create(:membership_sequence)
create(:payment_sequence)
end
and voila! the Sequence.find_by_name shall work now.
To answer your question why i choose to implement sequence generation manually than using rails active record autogenerated ids, it's because i want to be able to generate sequences such as PREFIX-2015-0001, PREFIX-2016-0001

Store functions in mongodb using Mongoid 3

Just as the title suggests. I am not able to find anything related to Mongoid 3. The things I found only apply to old versions of mongoid that didn't use Moped.
I found this and it doesn't work:
def self.install_javascript
getWeekJs = Rails.root.join("lib/javascript/getWeek.js")
if collection.master['system.js'].find_one({'_id' => "getWeek"}).nil?
collection.master.db.add_stored_function("getWeek", File.new(getWeekJs).read)
end
end
This method would add a getWeek function to the system.js collection.
How can this be done in mongoid 3?
Nailed it!
Codes:
class StoredProcedure
include Mongoid::Document
store_in collection: "system.js"
field :_id, type: String, default: ""
def self.test
equalsJS = Rails.root.join("lib/assets/javascripts/equals.js")
code = Moped::BSON::Code.new(File.new(equalsJS).read)
unless where(id: "equals").first
proc = new(value: code)
proc._id = "equals"
proc.save
end
end
end
Explanation:
I'm using the system.js in mongoid as if it were a normal collection. I'm then simply adding new documents.
IMPORTANT:
The value needs to be a Moped::BSON::Code instance otherwise it will be saved as string, thus useless. The id needs to be the function's name. I wasn't able to specify the id in a create statement, therefore I added multiple steps.
Just add this to a rake task to make sure you add all required functions to mongo after deployment.

Rails 3 translations within models in production

I'm trying to translate an app into Japanese and everything was going smoothly until I put it into production.
As cache_classes is now true any translation within a model reverts to the default locale.
I know I'm probably supposed to target the translations directly in the yml file but I'm not sure how I would do that for the following simplified code:
class TimeseriesForecast < ActiveRecord::Base
##field_names = {
:location_name => I18n.t('forecast_timeseries.location_name'),
:local_date_time => I18n.t('forecast_timeseries.local_date_time'),
:zulu_date_time => I18n.t('forecast_timeseries.zulu_date_time'),
:temp_mean => I18n.t('forecast_timeseries.temp_mean')
}
end
Many thanks
Your I18n.t() call is evaluated at compile time since you are defining class variables, not instance variables. You need to put your call to I18n.t where they will be evaluated at runtime.
But if you want to translate ActiveRecord field names, use human_attribute_name and provide your translations via YML. You do not need to manually provide translations, Rails handles it all for you automatically.
The respective documentation is at http://guides.rubyonrails.org/i18n.html Chapter 5.1.
Don't use I18n.t or translate method in your models. You can do this instead:
In your model
Use something like this to add internationalized errors to the name attribute of your model (Check documentation: ActiveModel/Errors/method-i-add):
self.errors.add(:name, :your_error_key)
# The error key could be something like :wrong_name
NOTE: Sometimes you won't even need to add errors with errors.add method. For example if you add validations in your model with somethind like this:
validates :name, presence: true
Rails will add an error with the key :blank (the key depens on the validation type). In other words rails internally will issue self.errors.add(:name, :blank)
In your locale
Then in your locale.jp.yml can use any of this (just one):
activerecord.errors.models.[model_name].attributes.[attribute_name]
activerecord.errors.models.[model_name]
activerecord.errors.messages
errors.attributes.[attribute_name]
errors.messages
In your case replace [model_name] with timeseries_forecast and [attribute_name] with your_error_key
For example:
en:
errors:
messages:
your_error_key: "Your error message in english"
Don't think you're improving performance by caching the names in the class. Make it a method instead.
class TimeseriesForecast < ActiveRecord::Base
def self.field_names
{ :location_name => I18n.t('forecast_timeseries.location_name'),
:local_date_time => I18n.t('forecast_timeseries.local_date_time'),
:zulu_date_time => I18n.t('forecast_timeseries.zulu_date_time'),
:temp_mean => I18n.t('forecast_timeseries.temp_mean') }
end
end
# usage
TimeseriesForecast.field_names
Better yet, return just the actual fields and do the translation in the view, if you're gonna be strict MVC about it (some Rails methods - like collection_select - make it harder to do that though, hence the suggestion above).

What is Rails ActiveRecord find(x) method that is equivalent to a lazy-evaluated-scope? How to refactor ActiveRecord finder?

Is Rails' find(x) method on a model lazy? If not, what is the equivalent?
I am new to Rails, so I found myself writing scopes like this:
class Course < ActiveRecord::Base
scope :by_instructor_id, lambda { |instructor_id| where(:instructor_id => instructor_id) }
scope :by_course_template_id, lambda { |course_template_id| where(:course_template_id => course_template_id ) }
scope :by_company_id, lambda { |company_id| joins(:instructor).merge(CompanyUser.by_company_id(company_id)) }
end
It's not a lot of work, but now I'm asking myself... if Rails provided these with a scope, I wouldn't have to write them.
So, does Rails offer them? Can I do something like the below code and only make it do 1 database call?
Company.find(params[:id]).instructors.courses
instead of
Course.by_company_id(params[:id])
Which is correct? I know Course.by_company_id(params[:id]) is only 1 database call. It is very familiar to writing SQL or queries in Hibernate. But if you can write it the other way, maybe one should?
However, I don't want to write Company.find(params[:id]).instructors.courses if it results in more than 1 database call. I can see the advantage though because it means never having to write the 3 scopes I showed you above, but I am worried that Company.find(x) is not lazy. Is it?
Try using #scoped method on a model before calling #find:
user = User.scoped.instructors.courses.find(params[:id])
To make find by id query lazy you can add new method to your controller and then add this method as helper.
# company
def company
#company ||= Company.find(params[:id])
end
helper :company
#view
<%= company.name %>
To get more information you can check great RailsCast - Decent Exposure

Resources