Complex Scope, Rails 3 - ruby-on-rails

So I am building an application that matches users. User models have 3 attributes (that are relevant to my question anyways: gender:string, looking_for_men:boolean, looking_for_women:boolean.
currently I've got a method in my model like so:
def browse
if self.looking_for_men == true && self.looking_for_women == true
if self.sex == "Male"
User.where("looking_for_men = ?", true)
elsif self.sex == "Female"
User.where("looking_for_women = ?", true)
end
elsif self.sex == "Male" && self.looking_for_men == true
User.where("looking_for_men = ? AND sex = ?", true, "Male")
elsif self.sex == "Female" && self.looking_for_women == true
User.where("looking_for_women = ? AND sex = ?", true, "Female")
else
if self.sex == "Male"
User.where("looking_for_men = ? AND sex = ?", true, "Female")
elsif self.sex == "Female"
User.where("looking_for_women = ? AND sex = ?", true, "Male")
end
end
end
This is pretty messy, as you can tell. Is there anyway to clean this up and make it into a scope, so that say for example I am a male user, and I am looking for women that it returns only women who are looking for men when I do a query like so:
#users = User.all.browse

I would just do the code below, to make it more readable. But somehow,I'm not totally comfortable with this solution. Still lot of code:
class User < ActiveRecord::Base
scope :male, where(:gender => "Male")
scope :female, where(:gender => "Female")
scope :looking_for_men, where(:looking_for_men => true)
scope :looking_for_women, where(:looking_for_women => true)
def browse
#women = #men = []
#women = self.interested_females if self.looking_for_women
#men = self.interested_males if self.looking_for_men
#result = #women.concat(#men)
#result.delete(self) #removes the user itself from the result-set
return #result
end
def interested_females
return User.female.looking_for_men if self.male?
return User.female.looking_for_women if self.female?
end
def interested_males
return User.male.looking_for_men if self.male?
return User.male.looking_for_women if self.female?
end
def male?
return (self.gender == "Male")
end
def female?
return (self.gender == "Female")
end
end

Just from a scope point of view, you could move that logic into a scope fairly easily just by passing it to a proc.
class User
scope :browse_for, lambda { |user|
user.looking_for_men == true && user.looking_for_women == true
...
}
end
#users = User.browse_for(#single_male)
and you could also chain scopes together to clean up the logic: http://edgerails.info/articles/what-s-new-in-edge-rails/2010/02/23/the-skinny-on-scopes-formerly-named-scope/index.html.
I'm not sure if that quite answers your question?

Related

How to simplify big conditions

I have five dropdowns, and I need to put conditions on each of them. My code is:
def search(search, compare, year, rain_fall_type)
if search == 'All'
if rain_fall_type == 'All'
all
else
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
# all
where(Sector: rain_fall_type).order('id')
end
else
if rain_fall_type == "All"
order("#{year} ")
elsif rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
where(Sector: rain_fall_type).order("#{year} ")
end
end
# where(Year: year).order("#{rain_fall_type} ")
end
elsif compare != "None"
if year == 'All'
where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
else
where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
end
else
if rain_fall_type == 'All'
all.order('id')
else
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? ', search).order('id')
else
where('Sector = ? ', rain_fall_type).order('id')
end
else
if rain_fall_type == "None"
if search == "All"
where('Sector = ? ', search).order('id')
else
where('Sector = ? ', search).order('id')
end
else
# all
where('Sector = ? ', rain_fall_type).order('id')
end
end
end
end
end
It has many if and else. I am trying to minimise the conditions. What can be the best way to shrink this code? Someone suggested that I should use switch case instead. Should I use it? If so, how?
You can use a guard statement which is basically return something if some_condition?. This only doable in specific scenarios (where one of the condition is executing a single statement:
Bad example:
if condition?
do_something
else
do_something_else
end
This could be written as:
return do_something if condition?
do_something_else
This will give you less code branching.
Also, another recommendation is to call another method with more conditions instead of nesting conditions in one single shot.
Bad example:
if condition?
if condition_two?
do_something_two
else
do_something
end
else
do_something_else
end
This could be written as:
if condition?
call_another_method
else
do_something_else
end
def call_another_method
if condition_two?
do_something_two
else
do_something
end
end
An example from your code could be:
if rain_fall_type == 'All'
all
else
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
# all
where(Sector: rain_fall_type).order('id')
end
else
if rain_fall_type == "All"
order("#{year} ")
elsif rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
where(Sector: rain_fall_type).order("#{year} ")
end
end
end
That could be converted to:
return all if rain_fall_type == 'All'
if year == 'All'
return where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id') if rain_fall_type == "None"
where(Sector: rain_fall_type).order('id')
else
return order("#{year} ") if rain_fall_type == "All"
return where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id') if rain_fall_type == "None"
where(Sector: rain_fall_type).order("#{year} ")
end
I hope this could help :)
NOTE: This is to answer the original question of How to simplify big conditions?. But the original post is not following Rails/Ruby way of doing search and filters and not making a good use of scopes.
This is probably the best explanation of how you should set this up.
class Product < ActiveRecord::Base
# custom_scope_1
scope :status, -> (status) { where status: status }
# custom_scope_2
scope :location, -> (location_id) { where location_id: location_id }
# custom_scope_3
scope :search, -> (name) { where("name like ?", "#{name}%")}
end
def index
#products = Product.where(nil) # creates an anonymous scope
#products = #products.status(params[:status]) if params[:status].present?
#products = #products.location(params[:location]) if params[:location].present?
#products = #products.search(params[:search]) if params[:search].present?
end
This can be cleaned up further by...
def index
#products = Product.where(nil)
filtering_params(params).each do |key, value|
#products = #products.public_send(key, value) if value.present?
end
end
private
# A list of the param names that can be used for filtering the Products
def filtering_params(params)
params.slice(:status, :location, :search)
end
This method uses ruby meta-programming to loop through parameters and dynamically call predefined scopes on a model
You can move this code into a module and include it into any model that supports filtering
app/models/concerns/filterable.rb
module Filterable
extend ActiveSupport::Concern
module ClassMethods
def filter(filtering_params)
results = self.where(nil)
filtering_params.each do |key, value|
results = results.public_send(key, value) if value.present?
end
results
end
end
end
app/models/product.rb
class Product
include Filterable
...
end
app/controllers/product_controller.rb
def index
#products = Product.filter(params.slice(:status, :location, :search))
end
You now have filtering and searching of your models with one line in the controller and one line in the model
First of all, some of your logic doesn't make sense:
def search(search, compare, year, rain_fall_type)
if search == 'All'
if rain_fall_type == 'All'
all
else
# rain_fall_type != 'All'
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
where(Sector: rain_fall_type).order('id')
end
else
# in rain_fall_type != 'All' branch, so meaningless 'if'
if rain_fall_type == "All"
order("#{year} ")
elsif rain_fall_type == "None"
where('Sector = ? OR Sector = ? OR Sector = ?', "Primary", 'Secondary', 'Tertiary').order('id')
else
where(Sector: rain_fall_type).order("#{year} ")
end
end
end
elsif compare != "None"
# both are same, so meaningless 'if'
if year == 'All'
where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
else
where('Sector = ? OR Sector = ?', rain_fall_type, compare).order(:id)
end
else
# search != 'All'
if rain_fall_type == 'All'
all.order('id')
else
if year == 'All'
if rain_fall_type == "None"
where('Sector = ? ', search).order('id')
else
where('Sector = ? ', rain_fall_type).order('id')
end
else
if rain_fall_type == "None"
# in search != 'All' branch, so meaningless 'if'
# AND both are same, so again meaningless 'if'
if search == "All"
where('Sector = ? ', search).order('id')
else
where('Sector = ? ', search).order('id')
end
else
where('Sector = ? ', rain_fall_type).order('id')
end
end
end
end
end
There's more like that and I won't point it all out because we're throwing all that if stuff out, anyway.
Ultimately, we're going to defer the querying to the end of the method, like this:
def search(search, compare, year, rain_fall_type)
...
#query = all
#query = #query.where(Sector: #sectors) if #sectors
#query = #query.order(#order) if #order
#query
end
That way, you take all of your where and order statements, and do them only once at the end. That saves a lot of typing right there. See the comment from muistooshort for why (Sector: #sectors) works.
So, the trick is setting #sectors and #order. First, I'm going to assign the input variables to instance variables because I like it like that (and to avoid confusion between the variable #search and the method search):
def search(search, compare, year, rain_fall_type)
#search, #compare, #year, #rain_fall_type = search, compare, year, rain_fall_type
...
#query = all
#query = #query.where(Sector: #sectors) if #sectors
#query = #query.order(#order) if #order
#query
end
Now, this answer is going on too long already, so I won't drag you through all the gorey details. But, adding in a couple of helper methods (sectors_to_use, and order_to_use) and substituting them in for #sectors and #order, you basically end up with this:
def search(search, compare, year, rain_fall_type)
#search, #compare, #year, #rain_fall_type = search, compare, year, rain_fall_type
#query = all
#query = #query.where(Sector: sectors_to_use) if sectors_to_use
#query = #query.order(order_to_use) if order_to_use
#query
end
private
def sectors_to_use
return [#rain_fall_type, #compare] if #search != 'All' && #compare != 'None'
unless #rain_fall_type == 'All'
if #rain_fall_type == 'None'
#search == 'All' ? ['Primary', 'Secondary', 'Tertiary'] : [#search]
else
[#rain_fall_type]
end
end
end
def order_to_use
return nil if (#search == 'All') && (#rain_fall_type == 'All')
return #year if (#search == 'All') && !(#year == 'All')
return :id
end
That's less than half the lines of code, over a thousand fewer characters, and a whole lot fewer ifs.

Rails- Building dynamic query for filtering search results

I am trying to build a dynamic querying method to filter search results.
My models:
class Order < ActiveRecord::Base
scope :by_state, -> (state) { joins(:states).where("states.id = ?", state) }
scope :by_counsel, -> (counsel) { where("counsel_id = ?", counsel) }
scope :by_sales_rep, -> (sales) { where("sales_id = ?", sales) }
scope :by_year, -> (year) { where("title_number LIKE ?", "%NYN#{year}%") }
has_many :properties, :dependent => :destroy
has_many :documents, :dependent => :destroy
has_many :participants, :dependent => :destroy
has_many :states, through: :properties
belongs_to :action
belongs_to :role
belongs_to :type
belongs_to :sales, :class_name => 'Member'
belongs_to :counsel, :class_name => 'Member'
belongs_to :deal_name
end
class Property < ActiveRecord::Base
belongs_to :order
belongs_to :state
end
class State < ActiveRecord::Base
has_many :properties
has_many :orders, through: :properties
end
I have a page where I display ALL orders by default. I want to have check boxes to allow for filtering of the results. The filters are: Year, State, Sales, and Counsel. an example of a query is: All orders in 2016, 2015("order.title_number LIKE ?", "%NYN#{year}%") in states (has_many through) NJ, PA, CA, etc with sales_id unlimited ids and counsel_id unlimited counsel_ids.
In a nut shell I am trying to figure out how to create ONE query that takes into account ALL options the user checks. Here is my current query code:
def Order.query(opt = {})
results = []
orders = []
if !opt["state"].empty?
opt["state"].each do |value|
if orders.empty?
orders = Order.send("by_state", value)
else
orders << Order.send("by_state", value)
end
end
orders = orders.flatten
end
if !opt["year"].empty?
new_orders = []
opt["year"].each do |y|
new_orders = orders.by_year(y)
results << new_orders
end
end
if !opt["sales_id"].empty?
end
if !opt["counsel_id"].empty?
end
if !results.empty?
results.flatten
else
orders.flatten
end
end
Here is the solution I have come up with to allow for unlimited amount of filtering.
def self.query(opts = {})
orders = Order.all
opts.delete_if { |key, value| value.blank? }
const_query = ""
state_query = nil
counsel_query = nil
sales_query = nil
year_query = nil
queries = []
if opts["by_year"]
year_query = opts["by_year"].map do |val|
" title_number LIKE '%NYN#{val}%' "
end.join(" or ")
queries << year_query
end
if opts["by_sales_rep"]
sales_query = opts["by_sales_rep"].map do |val|
" sales_id = '#{val}' "
end.join(" or ")
queries << sales_query
end
if opts["by_counsel"]
counsel_query = opts["by_counsel"].map do |val|
" counsel_id = '#{val}' "
end.join(" or ")
queries << counsel_query
end
if opts["by_state"]
state_query = opts["by_state"].map do |val|
"states.id = '#{val}'"
end.join(" or ")
end
query_string = queries.join(" AND ")
if state_query
#orders = Order.joins(:states).where("#{state_query}")
#orders = #orders.where(query_string)
else
#orders = orders.where("#{query_string}")
end
#orders.order("title_number DESC")
end
What you're looking for a query/filter object, which is a common pattern. I wrote an answer similar to this, but I'll try to extract the important parts.
First you should move those logic to it's own object. When the search/filter object is initialized it should start with a relation query (Order.all or some base query) and then filter that as you go.
Here is a super basic example that isn't fleshed out but should get you on the right track. You would call it like so, orders = OrderQuery.call(params).
# /app/services/order_query.rb
class OrderQuery
def call(opts)
new(opts).results
end
private
attr_reader :opts, :orders
def new(opts={})
#opts = opts
#orders = Order.all # If using Rails 3 you'll need to use something like
# Order.where(1=1) to get a Relation instead of an Array.
end
def results
if !opt['state'].empty?
opt['state'].each do |state|
#orders = orders.by_state(state)
end
end
if !opt['year'].empty?
opt['year'].each do |year|
#orders = orders.by_year(year)
end
end
# ... all filtering logic
# you could also put this in private functions for each
# type of filter you support.
orders
end
end
EDIT: Using OR logic instead of AND logic
# /app/services/order_query.rb
class OrderQuery
def call(opts)
new(opts).results
end
private
attr_reader :opts, :orders
def new(opts={})
#opts = opts
#orders = Order.all # If using Rails 3 you'll need to use something like
# Order.where(1=1) to get a Relation instead of an Array.
end
def results
if !opt['state'].empty?
#orders = orders.where(state: opt['state'])
end
if !opt['year'].empty?
#orders = orders.where(year: opt['year'])
end
# ... all filtering logic
# you could also put this in private functions for each
# type of filter you support.
orders
end
end
The above syntax basically filters sayings if state is in this array of states and year is within this array of years.
In my case, the filter options came from the Controller's params, so I've done something like this:
The ActionController::Parameters structure:
{
all: <Can be true or false>,
has_planned_tasks: <Can be true or false>
... future filters params
}
The filter method:
def self.filter(filter_params)
filter_params.reduce(all) do |queries, filter_pair|
filter_key = filter_pair[0]
filter_value = filter_pair[1]
return {
all: proc { queries.where(deleted_at: nil) if filter_value == false },
has_planned_tasks: proc { queries.joins(:planned_tasks).distinct if filter_value == true },
}.fetch(filter_key).call || queries
end
end
Then I call the ModelName.filter(filter_params.to_h) in the Controller. I was able to add more conditional filters easily doing like this.
There's space for improving here, like extract the filters logic or the whole filter object, but I let you decide what is better in your context.
Here is one I built for an ecommerce order dashboard in Rails with the parameters coming from the controller.
This query will execute twice, once to count the orders and once to return the requested orders according to the parameters in the request.
This query supports:
Sort by column
Sort direction
Incremental Search - It'll search the beginning of a given field and returns those records that match enabling real-time suggestions while searching
Pagination (limited by 100 records per page)
I also have predefined values to sanitize some of the data.
This style is extremely clean and easy for others to read and modify.
Here's a sample query:
api/shipping/orders?pageNumber=1&orderStatus=unprocessedOrders&filters=standard,second_day&stores=82891&sort_column=Date&sort_direction=dsc&search_query=916
And here's the controller code:
user_id = session_user.id
order_status = params[:orderStatus]
status = {
"unprocessedOrders" => ["0", "1", "4", "5"],
"processedOrders" => ["2", "3", "6"],
"printedOrders" => ["3"],
"ratedOrders" => ["1"],
}
services = [
"standard",
"expedited",
"next_day",
"second_day"
]
countries = [
"domestic",
"international"
]
country_defs = {
domestic: ['US'],
international: ['CA', 'AE', 'EU', 'GB', 'MX', 'FR']
}
columns = {
Number: "order_number",
QTY: "order_qty",
Weight: "weight",
Status: "order_status",
Date: "order_date",
Carrier: "ship_with_carrier",
Service: "ship_with_carrier_code",
Shipping: "requestedShippingService",
Rate: "cheapest_rate",
Domestic: "country",
Batch: "print_batch_id",
Skus: "skus"
}
# sort_column=${sortColumn}&sort_direction=${sortDirection}&search_query=${searchQuery}
filters = params[:filters].split(',')
stores = params[:stores].split(',')
sort_column = params[:sort_column]
sort_direction = params[:sort_direction]
search_query = params[:search_query]
sort_by_column = columns[params[:sort_column].to_sym]
sort_direction = params[:sort_direction] == "asc" ? "asc" : "desc"
service_params = filters.select{ |p| services.include?(p) }
country_params = filters.select{ |p| countries.include?(p) }
order_status_params = filters.select{ |p| status[p] != nil }
query_countries = []
query_countries << country_defs[:"#{country_params[0]}"] if country_params[0]
query_countries << country_defs[:"#{country_params[1]}"] if country_params[1]
active_filters = [service_params, country_params].flatten
query = Order.where(user_id: user_id)
query = query.where(order_status: status[order_status]) if order_status_params.empty?
query = query.where("order_number ILIKE ? OR order_id::TEXT ILIKE ? OR order_info->'advancedOptions'->>'customField2' ILIKE ?", "%#{search_query}%", "%#{search_query}%", "%#{search_query}%") unless search_query.gsub(/\s+/, "").length == 0
query = query.where(requestedShippingService: service_params) unless service_params.empty?
query = query.where(country: "US") if country_params.include?("domestic") && !country_params.include?("international")
query = query.where.not(country: "US") if country_params.include?("international") && !country_params.include?("domestic")
query = query.where(order_status: status[order_status_params[0]]) unless order_status_params.empty?
query = query.where(store_id: stores) unless stores.empty?\
order_count = query.count
num_of_pages = (order_count.to_f / 100).ceil()
requested_page = params[:pageNumber].to_i
formatted_number = (requested_page.to_s + "00").to_i
query = query.offset(formatted_number - 100) unless requested_page == 1
query = query.limit(100)
query = query.order("#{sort_by_column}": :"#{sort_direction}") unless sort_by_column == "skus"
query = query.order("skus[1] #{sort_direction}") if sort_by_column == "skus"
query = query.order(order_number: :"#{sort_direction}")
orders = query.all
puts "After querying orders mem:" + mem.mb.to_s
requested_page = requested_page <= num_of_pages ? requested_page : 1
options = {}
options[:meta] = {
page_number: requested_page,
pages: num_of_pages,
type: order_status,
count: order_count,
active_filters: active_filters
}
render json: OrderSerializer.new(orders, options).serialized_json

Rails/MongoDb Search and Refine Search Implementation

I have a search functionality in my app which works as the following
Homepage: user selects location from a drop-down and then enters a search key-word and searches to get a set of results
Search Page (REfine Search Options): Once the user hits this page from the above he will be provided with more options to refine the search and narrow the results.
Right now we are implementing as follows but i am assuming as the paramenters increase over 5-7 the number of combinations will increase the number of if-else-elseif statement as well.
#refine search
#search = params[:search]
if params[:city].present? && params[:location_ids].present? && params[:search].blank?
#blood_banks = BloodBank.where(
{ :city_id => "#{#city}" }).where(
{ :location_id.in => params[:location_ids] })
elsif params[:search].present? && params[:location_ids].blank?
#blood_banks = BloodBank.where(
{ :bb_name => /##search/i })
elsif params[:search].present? && params[:city].present? && params[:location_ids].present?
#blood_banks = BloodBank.where(
{ :city_id => "#{#city}" }).where(
{ :location_id.in => params[:location_ids] }).where(
{ :bb_name => /##search/i })
end
Which is the best way to implement the same.
How do you achieve below code,
if params[:gender].present?
if params[:gender] == "male"
#doctors = Doctor.where( :gender => "Male")
end
if params[:gender] == "female"
#doctors = Doctor.where( :gender => "Female")
end
if params[:gender] == "any"
#doctors = Doctor.where( :gender => "Male") || Doctor.where( :gender => "Female")
end
end
Mongoid's where returns a Mongoid::Criteria and Mongoid::Criteria responds to where by returning another Mongoid::Criteria. This means that you can build your query piece by piece:
#blood_banks = BloodBank.all
if params[:city].present?
#blood_banks = #blood_banks.where(:city_id => params[:city])
end
if params[:location_ids].present?
#blood_banks = #blood_banks.where(:location_id.in => params[:location_ids])
end
...
As far as the second part goes, if you're searching for any gender then just leave it out entirely, then you can do things like:
#doctors = Doctor.all
genders = { 'male' => 'Male', 'female' => 'Female' }
if genders.has_key? params[:gender]
#doctors = #doctors.where(:gender => genders[params[:gender]]
end
Searching for any gender is the same not filtering on gender at all so the nil and 'all' cases are the same. Then you can handle the input and :gender values with a simple lookup table.

rails apply where if variable is not nil else apply all - nicer way

How to optimize/refactor this rails code,
I want to apply where condition to the Country and the City if the co is not nil, if its nil, then apply all.
def pre(co = nil,ci = nil)
cond1 = co.nil? ? "all" : "where(:id => co)"
cond2 = ci.nil? ? "all" : "where(:id => ci)"
#countries = Country.send(cond1).order(:name).map{|i| [i.name,i.id]}
#cities = City.send(cond2).order(:name).map{|i| [i.name,i.id]}
end
Is it a nice way or is there a nicer way?
#countries = Country.where(co.nil? || {:id => co}).order(:name).map{|i| [i.name,i.id]}
#cities = City.where(ci.nil? || {:id => ci}).order(:name).map{|i| [i.name,i.id]}
You can try something like this
#country = Country
#city = City
if co.blank?
#country = #country.where(:id => co)
end
if ci.blank?
#city = #city.where(:id => ci)
end
#countries = #country.order(:name).all.map{|i| [i.name,i.id]}
#cities = #city.order(:name).all.map{|i| [i.name,i.id]}
You would think about something like this.
def pre(co = nil, ci = nil)
#countries = scopify(Country, co)
#cities = scopify(City, ci)
end
def scopify(model_or_scope, attribute)
scope = model_or_scope.scoped
scope = scope.where(:id => attribute) if attribute.present?
scope.order(:name).map { |s| [s.name, s.id] }
end

Is it okay to have a method in the before block of a series of RSpec unit tests?

I have a pretty elaborate set-up to change the condition from passing to failing for four composing methods of a method called eligible?.
describe "#participant_age_eligible?" do
it "returns whether participant is age-eligible" do
#part.participant_age_eligible?(#pers).should == true
end
it "returns false if participant is not age eligible" do
q = #survey_section.questions.select { |q| q.data_export_identifier ==
"#{OperationalDataExtractor::PbsEligibilityScreener::
INTERVIEW_PREFIX}.AGE_ELIG" }.first
answer = q.answers.select { |a| a.response_class == "answer" && a.reference_identifier == "2" }.first
Factory(:response, :survey_section_id => #survey_section.id, :question_id => q.id, :answer_id => answer.id, :response_set_id => #response_set.id)
#part.participant_age_eligible?(#pers).should == false
end
end
describe "#participant_psu_county_eligible?" do
it "returns whether participant lives in eligible PSU" do
#part.participant_psu_county_eligible?(#pers).should == true
end
it "returns false if participant coes not live in an eligible PSU" do
q = #survey_section.questions.select { |q| q.data_export_identifier ==
"#{OperationalDataExtractor::PbsEligibilityScreener::
INTERVIEW_PREFIX}.PSU_ELIG_CONFIRM" }.first
answer = q.answers.select { |a| a.response_class == "answer" && a.reference_identifier == "2" }.first
Factory(:response, :survey_section_id => #survey_section.id, :question_id => q.id, :answer_id => answer.id, :response_set_id => #response_set.id)
#part.participant_psu_county_eligible?(#pers).should == false
end
end
There are two more methods just like those two. What I'd like to do is extract the
q = #survey_section.questions.select { |q| q.data_export_identifier ==
"#{OperationalDataExtractor::PbsEligibilityScreener::
INTERVIEW_PREFIX}.AGE_ELIG" }.first
answer = q.answers.select { |a| a.response_class == "answer" && a.reference_identifier == "2" }.first
Factory(:response, :survey_section_id => #survey_section.id, :question_id => q.id, :answer_id => answer.id, :response_set_id => #response_set.id)
portion into a method in the before block and then pass the relevant parameters, but I hesitiate because I have never seen someone define a method in a before block, not even sure you can do it, further, I'm not sure if you even should do it even if you can, maybe it's pointing to a problem that I'm not seeing. So I though I'd ask the unfathomably enormous expertise of the SO community. Thanks.
You can always define methods within your specific rspec file, and then call them from anywhere in that file. For example:
# your_file_spec.rb
describe MyModel do
before(:each) { setup_variables }
describe ...
end
# I usually put my helper methods at the bottom
def setup_variables
# Do some work
end
end
You can also sometimes use a 'scenario outline' approach to your work, for example:
# your_file_spec.rb
describe MyModel do
examples = [{:name => "Joe", :login => "joe18"}, {:name => "Grace", :login => "grace12"}]
examples.each do |example|
it "logs in #{example[:name]}." do
# Do some work
end
end
end
You may also find that useful.

Resources