Defining Custom RSpec Matchers - testing

I'm trying to create a matched for a hash defined in user_record. I tried creating a matcher.matches() as I did in be_a_boolean, but it only ever returns an object. be_a_boolean isn't a defined object, so it currently does not work.
def user_record
{
email_address: email_address_value,
date_of_birth: date_value,
active: boolean_value
}
end
def users(count)
Array.new(count) { user_record }
end
end
RSpec.describe DataGenerator do
def match_the_shape_of_a_user_record
# Use `be_a_boolean`, `be_a_date_before_2000` and `be_an_email_address`
# in the hash passed to `match` below to define this matcher.
match(a_hash_including(
an_object_having_attributes(:active => be_a_boolean_value),
# :date_of_birth => be_a_date_before_2000,
#:email_address => be_an_email_address
))
end
it "generates user records" do
user = DataGenerator.new.user_record
expect(user).to match_the_shape_of_a_user_record
end
def all_match_the_shape_of_a_user_record
# Combine the `all` matcher and `match_the_shape_of_a_user_record` here.
end
it "generates a list of user records" do
users = DataGenerator.new.users(4)
expect(users.to all_match_the_shape_of_a_user_record)
end
end

Related

Rails: Search by custom instance method's value using tire gem & elasticsearch

For example, I have Article model like
class Article < ActiveRecord::Base
#Columns: id, title, status_number...etc
STATUSES = {1 => "SUCCESS", 2 => "REJECTED"}
include Tire::Model::Search
include Tire::Model::Callbacks
def display_status
STATUSES[status_number]
end
def self.search(params)
tire.search(load: true, page: params[:page], per_page: 2) do
query do
boolean do
must { string params[:query], default_operator: "AND" } if params[:query].present?
end
end
end
end
how to include display_status as "SUCCESS" by default in search method?
I tried
query do
boolean do
must { string params[:query], default_operator: "AND" } if params[:query].present?
must { term :display_status , "SUCCESS" }
end
end
But couldn't get result.
Please help to solve this problem. Thanks

rails 3 validations uniqueness on attributes of an association

i have a model called Fund and a model called Company .. where fund belongs_to a company.
i have this validation in my Fund table:
validates :name, presence: true, uniqueness: true
This works both on server side and client side using client_side_validations. But i want my fund names to be unique across both fund.name values and fund.company.name values. And i want to do it in a way it would work with client_side_validations too.
Suggestions?
Ended up creating a very specific validator and adding it to client-side-validation. Here'z the breakdown
In models/fund.rb
validates_fund_name_not_company_name :name
new file in config/initializers/validators .. called fund_name_not_company_name_validator.rb
class FundNameNotCompanyNameValidator < ActiveModel::EachValidator
def validate_each(record, attr_name, value)
if ::Company.exists?(name: value)
record.errors.add(attr_name, :fund_name_not_company_name, options.merge(:value => value))
end
end
end
# This allows us to assign the validator in the model
module ActiveModel::Validations::HelperMethods
def validates_fund_name_not_company_name(*attr_names)
validates_with FundNameNotCompanyNameValidator, _merge_attributes(attr_names)
end
end
module ClientSideValidations::Middleware
class FundNameNotCompanyName < ClientSideValidations::Middleware::Base
def response
if ::Company.exists?(name: request.params[:name])
self.status = 404
else
self.status = 200
end
super
end
end
end
then in app/assets/javascripts/rails.validations.custom.js
clientSideValidations.validators.remote['fund_name_not_company_name'] = function(element, options) {
if ($.ajax({
url: '/validators/fund_name_not_company_name',
data: { name: element.val() },
// async must be false
async: false
}).status == 404) { return options.message; }
}
This helped a great deal

Why am I not able to call a static function in my Rspec class

I am new to rails and have just started writing tests using rspec version : 2.11.1 . I am looking for a way to seed different data for different tests in my class. For this I created a static function in the test itself. Depending on the requirements I am in instantiating different number of objects. I have a function seed_data which instantiates different number of objects based on the number passed to it. I get this exception :
NoMethodError: undefined method `seed_data' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_5::Nested_1:0x007fcedd9bea70>
Please have a look at the sample code below :
require 'spec_helper'
require 'go_live_sale'
require 'rspec/expectations'
describe GoLiveSale do
context "get_go_live_sale_for_sale_ids Function Correctness" do
describe "Testing get_go_live_sale_for_sale_ids() function correctness " do
it "should return error when sale_ids is blank" do
result = GoLiveSale.get_go_live_sale_for_sale_ids({})
result[:err].should_not be_blank
result[:err].should == "err1"
end
it "should return all columns corresponding to select field for single go_live_sale" do
go_live_sales = seed_data(1)
sale_ids = go_live_sales.map{ |go_live_sale| go_live_sale.sale_id}
result = GoLiveSale.get_go_live_sale_for_sale_ids({:sale_ids => sale_ids})
result[:err].should be_blank
result[:go_live_sales].count.should_be == 1
delete_seed_data(go_live_sales)
end
it "should return all columns corresponding to select field for multiple go_live_sale" do
go_live_sales = seed_data(2)
sale_ids = go_live_sales.map{ |go_live_sale| go_live_sale.sale_id}
GoLiveSale.get_go_live_sale_for_sale_ids({:sale_ids => sale_ids})
result[:err].should be_blank
result[:message].should be_blank
result[:go_live_sales].count.should == 2
delete_seed_data(go_live_sales)
end
it "should return selected columns corresponding to select field for single go_live_sale" do
go_live_sales = seed_data(1)
sale_ids = go_live_sales.map{ |go_live_sale| go_live_sale.sale_id}
result = GoLiveSale.get_go_live_sale_for_sale_ids({:sale_ids => sale_ids, :columns => ["id","sale_id"]})
result[:err].should be_blank
result[:go_live_sales].count.should_be == 1
delete_seed_data(go_live_sales)
end
it "should return selected columns corresponding to select field for multiple go_live_sale" do
go_live_sales = seed_data(2)
sale_ids = go_live_sales.map{ |go_live_sale| go_live_sale.sale_id}
GoLiveSale.get_go_live_sale_for_sale_ids({:sale_ids => sale_ids, :columns => ["id","sale_id"]})
result[:err].should be_blank
result[:message].should be_blank
result[:go_live_sales].count.should == 2
delete_seed_data(go_live_sales)
end
it "should return error when selecting erroneous columns" do
go_live_sales = seed_data(2)
sale_ids = go_live_sales.map{ |go_live_sale| go_live_sale.sale_id}
GoLiveSale.get_go_live_sale_for_sale_ids({:sale_ids => sale_ids, :columns => ["id","random_sale_id"]})
result[:err].should_not be_blank
delete_seed_data(go_live_sales)
end
end
end
end
def self.delete_seed_data(go_live_sales)
go_live_sales.each do |go_live_sale|
go_live_sale.delete
end
end
def self.seed_data(number_of_go_live_sale_to_create)
go_live_sales =[]
(1..number_of_go_live_sale_to_create).each do |number|
go_live_sales.push(create_go_live_sale(number))
end
return go_live_sales
end
def self.create_go_live_sale(number_to_add)
go_live_sale = GoLiveSale.new
go_live_sale.start_date = Time.now
go_live_sale.sale_id = Sale.select("IFNULL(max(id),0)+#{number_to_add} as sale_id").first.try(:sale_id)
go_live_sale.sale_name = "Test Sale" + go_live_sale.sale_id.to_s
go_live_sale.sale_type = "Flash Sale"+ go_live_sale.sale_id.to_s
User.current_user = User.first
go_live_sale.save
return go_live_sale
end
RSpec::Matchers.define :be_valid do
match_for_should do |actual|
actual[:err].blank?
actual[:validation_error].blank?
actual[:is_valid] == true
end
match_for_should_not do |actual|
actual[:err].present?
actual[:validation_error].present?
actual[:is_valid] == false
end
failure_message_for_should do |actual|
"Expected validation to pass, but it failed"
end
failure_message_for_should_not do |actual|
"Expected validation to fail, but it passed"
end
end
I understand that it is some scope issue or maybe rspec doesn't let you write the tests that way. It will be great if some one can write a small snippet of code explaining how to instantiate test data in such cases. My rails version is 3.0.5.
You can just get rid of the self. in the method definition and move it inside the describe GoLiveSale do block, then call it with seed_data as expected.
For example:
describe GoLiveSale do
def my_method
end
context "some context" do
it "should call my_method" do
expect {my_method}.not_to raise_error
end
end
end
This spec should pass.

Rails 3: Controller params default value

I am using a remote form_for for my show action to retrieve content based on the params passed by this form.
= form_tag modelname_path(#modelname), :id=>"select_content_form", :remote => true, :method => 'get' do
= text_field_tag :content_type, params[:content_type], :id=>"select_content_type"
= submit_tag "submit", :name => nil, :id=>"select_content_submit"
And I alter the content in controller as follows:
# Default params to "type1" for initial load
if params[:content_type]
#content_type = params[:content_type];
else
#content_type = "type1"
end
case #content_type
when "type1"
# get the content
#model_content = ...
when "type1"
# get the content
#model_content = ...
My question is, whether the above approach is the only we can set defaults for params or can we do it in a better manner. This works but I would like to know if this is the right approach.
UPDATE
Based on the suggestion below, I used the following and got an error on defaults.merge line:
defaults = {:content_type=>"type1"}
params = defaults.merge(params)
#content_type = params[:content_type]
A good way of setting default options is to have them in a hash, and merge your incoming options onto it. In the code below, defaults.merge(params) will overwrite any values from the params hash over the default ones.
def controller_method
defaults = {:content=>"Default Content", :content_type=>"type1"}
params = defaults.merge(params)
# now any blank params have default values
#content_type = params[:content_type]
case #content_type
when "type1"
#model_content = "Type One Content"
when "type2"
#etc etc etc
end
end
If there is a static list of types you could make it a dropdown box and just don't include a blank option so that something is always selected. But if you're stuck with a textbox you could clean up the controller action by using a before filter:
class FoosController < ActionController::Base
before_filter :set_content_type, :only => [:foo_action]
def foo_action
...
end
protected
def set_content_type
params[:content_type] ||= "type1"
end
end
I wanted to add to this discussion a working way to set default params:
defaults = { foo: 'a', bar: 'b' }
params.replace(defaults.merge(params))
This avoids assigning a local variable via "params =".

Where to set session default value?

guys!
Prior to asking i should mention, that i`m working without ActiveRecord or any self-hosted-database. So thats why i have to store some values in the session.
From the very begining i desided to set session value of the users city in the layout. - i supposed it would be loaded before anything else. So i`ve done something like this:
<% session[:city] ||= {:name => 'City-Name', :lat => '40', :lng => '40'}%>
But when i`m loading directly to inner page it occurs that session[:city is nil *(
How should i set the session properely, so that it wouldn`t be nil???
I had similar needs in one of the applications I worked on. It needed the users data to be loaded on sign-in and stored in the session. So, wrote a module called session_helpers.rb with the following:
module SessionHelpers
def get_value(key)
session[key.to_sym]
end
protected
def store_data(*objects)
objects.each do |object|
if object.is_a?(Hash)
object.each do |key, value|
session[key.to_sym] = value
end
end
end
end
def remove_data(*objects)
objects.each do |object|
if object.is_a?(String)
key = to_id(object)
else
key = to_id(object.class.name)
end
session[key] = nil
end
end
def update_data(key, value)
session[key.to_sym] = value
end
private
def to_id(name)
"#{name.parameterize('_').foreign_key}".to_sym
end
end
You can make any or all the methods available to views as well:
# application_controller.rb
helper_method :get_value
From the model I would retrieve a hash of the data that needs to be put up in the session about the user:
def common_data
#data = Hash.new
#data.merge!( { 'news' => self.news.count } )
...
#data
end
As I wanted to do this after sign-in I overrode the devise method to do this:
def after_sign_in_path_for(resource_or_scope)
store_data( '_count', current_user.common_data )
dashboard_path
end
This way I was able to load important data about the user on sign-in and store it in the session and retrieve whenever I wanted. Hope this helps.