Rails How to define a macro to use only within factories.rb - factory-bot

I'm using FactoryGirl in a Rails application for RSpec functional testing. While writing factories I came across a need for defining private method or macro to remove the duplicate code in factories. The macros should be such that it can only be called from factories definition. Kindly help
factory :subscriber, :class => Subscriber do
sequence(:name) { |n| "test_user_#{n}" }
sequence(:email) { |n| "test_user_#{n}#example.com"}
association :authentication, factory: :authentication
factory :subscriber_with_subscription do
ignore do
payment_pending_subscriptions 0
purchased_subscriptions 0
active_subscriptions 0
end
after(:create) do |subscriber, evaluator|
count = evaluator.payment_pending_subscriptions
FactoryGirl.create_list(:subscription, count, status: 'payment_pending', :subscriber => subscriber) if count >0
count = evaluator.purchased_subscriptions
FactoryGirl.create_list(:subscription, count, status: 'purchased', :subscriber => subscriber) if count > 0
count = evaluator.active_subscriptions
FactoryGirl.create_list(:subscription, count, status: 'active', :subscriber => subscriber) if count > 0
end
end
end
if I can define a method inside the factory such that
def create_subs count, status
FactoryGirl.create_list(:subscription, count, status: status, :subscriber => subscriber) if count > 0
end
Then the calls can be reduced to one line / state
create_subs evaluate.purchased_subscription, "purchased_subscription"
But even after defining create_subs, it gives error that it is not found.

Related

Check value against multiple enum values in IQueryable

I am trying to write an elegant solution to filter a value against multiple Enum values in an IQueryable. Here's what I have got so far:
Extension
public static class EnumExtensions
{
public static bool IsAny<T>(this T value, params T[] choices)
where T : Enum
{
return choices.Contains(value);
}
}
Usage
query = query.Where(x => x.Status.IsAny(OrderStatus.Accepted, OrderStatus.Received, OrderStatus.Approved));
But when I execute this, I get following error:
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: The LINQ expression 'DbSet<SalesOrder>
.Where(s => s.DeletedAt == null)
.Where(s => False || s.SellerId == __request_OwnerId_0)
.Where(s => s.Status
.IsAny(OrderStatus[] { Accepted, Received, Approved, }))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Any pointers on how I can get this working?
You cannot use custom methods inside Linq queries and in Expression<> params for EF. You will need to do it the long-form way:
query = query
.Where( x =>
x.Status == OrderStatus.Accepted ||
x.Status == OrderStatus.Received ||
x.Status == OrderStatus.Approved
);
Depending on your RDBMS, you might be able to do it this way though:
static readonly OrderStatus[] _these = new[] { OrderStatus.Accepted, OrderStatus.Received, OrderStatus.Approved };
// ...
query = query.Where( x => _these.Contains( x.Status ) );
...which should be translated to this SQL:
WHERE
x.Status IN ( 0, 1, 2 )
(Assuming OrderStatus.Accepted == 0, Received == 1, Approved == 2).

How to create basic pagination logic in Gatling?

So I am trying to create basic pagination in Gatling but failing miserably.
My current scenario is as follows:
1) First call is always a POST request, the body includes page index 1 and page size 50
{"pageIndex":1,"pageSize":50}
I am then receiving 50 object + the total count of objects on the environment:
"totalCount":232
Since I need to iterate through all objects on the environment, I will need to POST this call 5 time, each time with an updated pageIndex.
My current (failing) code looks like:
def getAndPaginate(jsonBody: String) = {
val pageSize = 50;
var totalCount: Int = 0
var currentPage: Int = 1
var totalPages: Int =0
exec(session => session.set("pageIndex", currentPage))
exec(http("Get page")
.post("/api")
.body(ElFileBody("json/" + jsonBody)).asJson
.check(jsonPath("$.result.objects[?(#.type == 'type')].id").findAll.saveAs("list")))
.check(jsonPath("$.result.totalCount").saveAs("totalCount"))
.exec(session => {
totalCount = session("totalCount").as[Int]
totalPages = Math.ceil(totalCount/pageSize).toInt
session})
.asLongAs(currentPage <= totalPages)
{
exec(http("Get assets action list")
.post("/api")
.body(ElFileBody("json/" + jsonBody)).asJson
.check(jsonPath("$.result.objects[?(#.type == 'type')].id").findAll.saveAs("list")))
currentPage = currentPage+1
exec(session => session.set("pageIndex", currentPage))
pause(Config.minDelayValue seconds, Config.maxDelayValue seconds)
}
}
Currently the pagination values are not assign to the variables that I have created at the beginning of the function, if I create the variables at the Object level then they are assigned but in a manner which I dont understand. For example the result of Math.ceil(totalCount/pageSize).toInt is 4 while it should be 5. (It is 5 if I execute it in the immediate window.... I dont get it ). I would than expect asLongAs(currentPage <= totalPages) to repeat 5 times but it only repeats twice.
I tried to create the function in a class rather than an Object because as far as I understand there is only one Object. (To prevent multiple users accessing the same variable I also ran only one user with the same result)
I am obviously missing something basic here (new to Gatling and Scala) so any help would be highly appreciated :)
using regular scala variables to hold the values isn't going to work - the gatling DSL defines builders that are only executed once at startup, so lines like
.asLongAs(currentPage <= totalPages)
will only ever execute with the initial values.
So you just need to handle everything using session variables
def getAndPaginate(jsonBody: String) = {
val pageSize = 50;
exec(session => session.set("notDone", true))
.asLongAs("${notDone}", "index") {
exec(http("Get assets action list")
.post("/api")
.body(ElFileBody("json/" + jsonBody)).asJson
.check(
jsonPath("$.result.totalCount")
//transform lets us take the result of a check (and an optional session) and modify it before storing - so we can use it to get store a boolean that reflects whether we're on the last page
.transform( (totalCount, session) => ((session("index").as[Int] + 1) * pageSize) < totalCount.toInt)
.saveAs("notDone")
)
)
.pause(Config.minDelayValue seconds, Config.maxDelayValue seconds)
}
}

Can anyone explain interesting spin-lock behavior?

Given the following code
case class Score(value: BigInt, random: Long = randomLong) extends Comparable[Score] {
override def compareTo(that: Score): Int = {
if (this.value < that.value) -1
else if (this.value > that.value) 1
else if (this.random < that.random) -1
else if (this.random > that.random) 1
else 0
}
override def equals(obj: _root_.scala.Any): Boolean = {
val that = obj.asInstanceOf[Score]
this.value == that.value && this.random == that.random
}
}
#tailrec
private def update(mode: UpdateMode, member: String, newScore: Score, spinCount: Int, spinStart: Long): Unit = {
// Caution: there is some subtle logic below, so don't modify it unless you grok it
try {
Metrics.checkSpinCount(member, spinCount)
} catch {
case cause: ConcurrentModificationException =>
throw new ConcurrentModificationException(Leaderboard.maximumSpinCountExceeded.format("update", member), cause)
}
// Set the spin-lock
put(member, None) match {
case None =>
// BEGIN CRITICAL SECTION
// Member's first time on the board
if (scoreToMember.put(newScore, member) != null) {
val message = s"$member: added new member in memberToScore, but found old member in scoreToMember"
logger.error(message)
throw new ConcurrentModificationException(message)
}
memberToScore.put(member, Some(newScore)) // remove the spin-lock
// END CRITICAL SECTION
case Some(option) => option match {
case None => // Update in progress, so spin until complete
//logger.debug(s"update: $member locked, spinCount = $spinCount")
for (i <- -1 to spinCount * 2) {Thread.`yield`()} // dampen contention
update(mode, member, newScore, spinCount + 1, spinStart)
case Some(oldScore) =>
// BEGIN CRITICAL SECTION
// Member already on the leaderboard
if (scoreToMember.remove(oldScore) == null) {
val message = s"$member: oldScore not found in scoreToMember, concurrency defect"
logger.error(message)
throw new ConcurrentModificationException(message)
} else {
val score =
mode match {
case Replace =>
//logger.debug(s"$member: newScore = $newScore")
newScore
case Increment =>
//logger.debug(s"$member: newScore = $newScore, oldScore = $oldScore")
Score(newScore.value + oldScore.value)
}
//logger.debug(s"$member: updated score = $score")
scoreToMember.put(score, member)
memberToScore.put(member, Some(score)) // remove the spin-lock
//logger.debug(s"update: $member unlocked")
}
// END CRITICAL SECTION
// Do this outside the critical section to reduce time under lock
if (spinCount > 0) Metrics.checkSpinTime(System.nanoTime() - spinStart)
}
}
}
There are two important data structures: memberToScore and scoreToMember. I have experimented using both TrieMap[String,Option[Score]] and ConcurrentHashMap[String,Option[Score]] for memberToScore and both have the same behavior.
So far my testing indicates the code is correct and thread safe, but the mystery is the performance of the spin-lock. On a system with 12 hardware threads, and 1000 iterations on 12 Futures: hitting the same member all the time results in spin cycles of 50 or more, but hitting a random distribution of members can result in spin cycles of 100 or more. The behavior gets worse if I don't dampen the spin without iterating over yield() calls.
So, this seems counter intuitive, I was expecting the random distribution of keys to result in less spin than the same key, but testing proves otherwise.
Can anyone offer some insight into this counter-intuitive behavior?
Granted there may be better solutions to my design, and I am open to them, but for now I cannot seem to find a satisfactory explanation for what my tests are showing, and my curiosity leaves me hungry.
As an aside, while the single member test has a lower ceiling for the spin count, the random member test has a lower ceiling for time spinning, which is what I would expect. I just cannot explain why the random member test generally produces a higher ceiling for spin count.

SCALA multiple nested case statement in map RDD operations

I have an RDD with 7 string elements
linesData: org.apache.spark.rdd.RDD[(String, String, String, String, String, String, String)]
I need to figure out for each record the following 2 items: Status and CSrange.
the logic is something like:
case when a(6)=0
if Date(a(5)) > Date(a(4)) then
if Date(a(5)) - Date(a(4)) > 60 days then
staus = '60+'
else
status = 'Curr'
endif
else
status = 'Curr'
end
when (a(6) >=1 and a(6) <=3 ) then
Status = 'In-Forcl'
when (a(6) >=4 and a(6) <=8)
Status = 'Forclosed'
else
Status = 'Unknown'
end case
case when (a(1) <640 and a(1) >0 ) then CSrange = '<640'
when (a(1) <650 and a(1)> 579 then CSrange = '640-649'
when (a(1) <660 and a(1)> 619 then CSrange = '650-659'
when (a(1) <680 and a(1)> 639 then CSrange = '640-649'
when (a(1) >789 then CSrange = '680+'
else
CSRange ='Unknown'
end case
At this point, I would like to write the data out to disk, with probably 9 elements insteatd of 7. (later I will need to do calculate the rate of each of the statuses above by various elements).
My first problems are:
1. how to do date arithmetics? as I need to stay at the RDD level (no data frames).
2. I don't know how to do the CASE statements in SCALA.
SAMPLE DATA:
(2017_7_0555,794,Scott,CORNERSTONE,8/1/2017,8/1/2017,0)
(2017_7_0557,682,Hennepin,LAKE AREA MT,9/1/2017,8/1/2017,0)
(2017_7_0565,754,Ramsey,GUARANTEED R,6/1/2017,8/1/2017,0)
(2017_7_0570,645,Hennepin,FAIRWAY INDE,2/1/2015,8/1/2017,5)
(2017_7_0574,732,Wright,GUARANTEED R,7/1/2017,8/1/2017,0)
(2017_7_0575,789,Hennepin,GUARANTEED R,8/1/2017,8/1/2017,0)
(2017_7_0577,662,Hennepin,MIDCOUNTRY,8/1/2017,8/1/2017,0)
(2017_7_4550,642,Mower,WELLS FARGO,5/1/2017,8/1/2017,0)
(2017_7_4574,689,Hennepin,RIVER CITY,8/1/2017,8/1/2017,0)
(2017_7_4584,662,Hennepin,WELLS FARGO,8/1/2017,8/1/2017,0)
(2017_7_4600,719,Ramsey,PHH HOME LOA,5/1/2017,8/1/2017,0)
I would create a case class of seven fields and map RDD[(,...)] to RDD[MyClass] and cast to appropriate types. I recommend you JodaTime date library for dates. This will make your code more descriptive.
Then in a map extract status and range into two functions:
myRDD.map(myInstance => (getStatus(myInstance), getRange(myInstance)))
def getStatus(myInstance: MyClass) : String = {
val (_,_,_,_,date4,date5,field6,_) = MyClass.unapply(myInstance).get
field6 match {
case 0 => {
if(date5.isAfter(date4) {
if(date4.plusDays(60).isAfter(date5)){
"60+"
} else {
"Curr"
}
}
}
case x if (x >= 1 && x <= 3) => "Forclosed"
....
}
}
Note:
I haven't tested the code.
Rename variables from the example.
I show you an example about how to manage JodaTime dates, and scala pattern matching. You have to finish the function and define the other one.

Convert IEnumerable<XElement> to XElement

The return type of my query is IEnumerable<XElement>. how can i convert the resultant data to XElement type? is it possible? could some body help me to understand this.
var resQ = from e in docElmnt.Descendants(xmlns + Constants.T_ROOT)
.Where(x => x.Attribute(Constants.T_ID).Value == "testid")
select e;
I have to pass resQ as a parameter to the below function. in order to do that I have to convert resQ to XElement type.
Database.usp_InsertTestNQuestions(tid, qId, qstn, ans, resQ );
As long as your query only returns a single result, you can either call Single() or First() on the result (also, there's no need for the extra query syntax up top):
// Use if there should be only one value.
// Will throw an Exception if there are no results or more than one.
var resQ = docElmnt.Descendents(xmlns + Constants.T_ROOT)
.Single(x => x.Attribute(Constants.T_ID).Value == "testid");
// Use if there could be more than one result and you want the first.
// Will throw an Exception if there are no results.
var resQ = docElmnt.Descendents(xmlns + Contants.T_ROOT)
.First(x => x.Attribute(Constants.T_ID).Value == "testid");
If you want to handle the case when no results are returned for the query without throwing an Exception, you can use SingleOrDefault (which will still throw an Exception if you get more than one result) or FirstOrDefault.
In addition to Justin's answer, you may want to allow for 0 elements returned or some other condition.
In that case, simply do a:
IEnumerable<XElement> resQ = docElmnt.Descendents(xmlns + Constants.T_ROOT)
.Where(x => x.Attribute(Constants.T_ID).Value == "testid");
if(resQ.Count() == 0) {
//handle no elements returned
} else if(resQ.Count() > 1) {
//handle more than 1 elements returned
} else {
XElement single = resQ.Single();
}
Most of the time I find it's best not to throw an error-- unless having exactly 1 returned is really important.
You can iterate over each element in the query and then call the method with your enumerator.
resQ.ToList().ForEach(e => ...func... );