How can I use a block to change the execution context in ruby? - ruby

I'm creating a factory for an account object and I'm setting the name like this:
name { "#{Faker::Hacker.ingverb} #{Faker::Hacker.adjective} #{Faker::Hacker.noun}" }
Is there a way to use a block to change the execution context to eliminate the redundant Faker::Hacker calls? I'd like to end up with something like this:
name { Faker::Hacker { "#{ingverb} #{adjective} #{noun}" } }
Thanks!

It looks like you are sending methods to a class/module, so your example may be simply rewritten with use of Module#class_eval method:
name { Faker::Hacker.class_eval { "#{ingverb} #{adjective} #{noun}" } }
would invoke methods in the block passed to class_eval on Faker::Hacker class.

Not a total solution according to your problem, but a lot less to type:
h = Faker::Hacker
name { "#{h.ingverb} #{h.adjective} #{h.noun}" }

Related

Faker gem generating 2-3 letter strings

The Faker gem generates short, nonsense strings instead of what is described. For example, Faker::Job.title generates "et". If I have a feature test that expects not to find a Faker-generated string on the page, chances are it's going to fail if the string is "et". Surely this is unexpected behaviour, as nobody in the world has the job title "et".
This is my code, the most recent time I checked it the title was as expected, but the role and category were not:
# frozen_string_literal: true
shared_context 'with signatory attributes' do
let(:first_name) { Faker::Name.first_name }
let(:last_name) { Faker::Name.last_name }
let(:email) { Faker::Internet.email }
let(:title) { Faker::Job.title }
let(:mobile) { Faker::Number.number(10) }
let(:employee_num) { Faker::Number.number(10) }
let(:role) { Faker::Job.title }
let(:category) { Faker::Job.title }
end
Looks like Faker isn’t set up to make realistic job titles. But it’s easy to make your own random job titles. I would just sample your own custom array, like this:
let(:title) { %w[Admin Manager Engineer].sample }
You can use regex matcher with word boundaries instead of the short string only, but it is still not bullet-proof.
let(:first_name) { /\b#{Faker::Name.first_name}\b/ }
But maybe it is better to stub the attribute on model itself and raise an Error if it is called.
It seems like it pulls strings from its Lorem Ipsum String Set for some reason. Do you mind sharing your code?

Gradle extension as NamedDomainObjectContainer of Closure

I'm trying to build a Gradle plugin that would allow the following:
myPluginConfig {
something1 {
// this is a closure
}
somethingElse {
// this is another closure
}
// more closures here
}
To achieve this I'm fairly certain I need to use a NamedDomainObjectContainer to wrap a Closure collection, so I've set up the following plugin:
class SwitchDependenciesPlugin implements Plugin<Project> {
void apply(Project project) {
// add the extension
project.getExtensions().myPluginConfig = project.container(Closure)
// read the current configuration
NamedDomainObjectContainer<Closure> config = project.myPluginConfig
// test it out, always returns []
System.out.println(config)
}
}
What am I doing wrong, do I need to use project.extensions.create instead? If so, how?
EDIT: my use case consists in adding dependencies according to some variables defined in the project hierarchy. For example, the following configuration would add the red project if the variable red is defined on project.ext, or gson otherwise:
myPluginConfig {
redTrue {
compile project(':red')
}
redFalse {
compile 'com.google.code.gson:gson:2.4'
}
greenTrue {
compile project(':green')
}
}
For this use case I need to have dynamic names for myPluginConfig, and therefore either a Map or a NamedDomainObjectContainer.
Can you elaborate what you try to model here? I think you have two options. One is to use NamedDomainObjectContainer. Here you need a class that represents "something". Have a look at the Gradle userguide chapter about maintaining multiple domain objects (see https://docs.gradle.org/current/userguide/custom_plugins.html#N175CF) in the sample of the userguide, the "thing" is 'Book'. The build-in configuration syntax like you described above comes for free.
If you want to have a syntax like above without the need for maintaining multiple domain objects, you can simply add a method that takes a Closure as a parameter to your Extension class:
void somethingToConfigure(Closure) {
}
You cannot have Closure as a type for NamedDomainObjectContainer simply because the type you use must have a property called name and a public constructor with a single String parameter.
To overcome this, you may create a wrapper around Closure with a name field added.

How do I name the function which only does something if condition is true

According to clean code laws, we want to have functions which do only one thing and are on the same "level of abstraction". But how to name function, whose work is just to check some condition and do the work if condition is true. For example, how could this function be named?
public void HowToNameThis(){
if(!ComponentIsInstalled()){
DisableCheckbox();
}
}
I thought about naming it like DisableCheckboxIfComponentIsNotInstalled, but then the name just repeats the code, which effectively means I have created a function but did not create any abstraction.
CleanCode also suggest that you stay as positive as you can in your code. If you reverse the logic within your method, then, naming becomes easier.
public void TryEnableComponent() {
if(ComponentIsInstalled()) {
EnableCheckbox();
}
}
I generally think really hard about if the IF is really deserving it's own function.
And then often end up inlining it:
Like this (pseudo):
void SetupInstallerWindow()
{
LoadLicenseAgreement();
if(!ComponentIsInstalled()){
DisableCheckbox();
}
BringWindowTop();
}
If that really gets to messy, here's an idea for a name might provide more context for the reader:
AllowReinstallationOfComponent()

Dynamically setting content in Geb

I want to define a method in a groovy class that I can pass an xpath to on the fly(in order for the same method to be reusable depending on the application). The code snippet below is just a proof of concept, however I would eventually like to build a library of re-usable commands/components, which is why I would like to learn how to dynamically define page content.
If I try this:
import geb.Page;
class oneStepDefMethodClass extends Page {
static url = 'http://www.google.com'
static content = {
queryInput { $("input", id: "gbqfq") }
queryButton { $("button",name: "btnG") }
//songLink { $("span._BZ")}
}
....
void assertSongInResults2(String xpathOfSongLink){
println "Waiting on video link "+ xpathOfSongLink
songLink { $(xpathOfSongLink)}
waitFor {
songLink.displayed
}
}
}
I get this error :groovy.lang.MissingMethodException: No signature of method: geb.navigator.NonEmptyNavigator.songLink() is applicable for argument types: (oneStepDefMethodClass$_assertSongInResults2_closure3) values: [oneStepDefMethodClass$_assertSongInResults2_closure3#7c455e96]
If I throw a
content={songLink {$(xpathOfSongLink)}
}
block in the assertSongInResults2 method, I get this error:
geb.error.UnresolvablePropertyException: Unable to resolve songLink as content for oneStepDefMethodClass, or as a property on its Navigator context. Is songLink a class you forgot to import?
So, yeah is there a way to dynamically define page content like that? The program executes fine if I define it statically up top with the rest of the content , but that is not the point, I want to create re-usable resources instead of redefining the wheel every time I want to use geb.
Solved as I was writing the question, but thought I would post in case anyone else has a similar problem
static String someXpath
static content = {
queryInput { $("input", id: "gbqfq") } //
queryButton { $("button",name: "btnG") } //
songLink { $(someXpath) } //syntax element.className
}
....
void assertSongInResults2(String xpathOfSongLink){
println "Waiting on video link "+ xpathOfSongLink
someXpath=xpathOfSongLink
waitFor {
songLink.displayed
}
}

Best way to create an array will multiple objects generated the same way?

What more elegant way to do this in ruby way? I suppose this is good:
([0]*5).collect { Factory :customer_pj }
or
(1..5).to_a.collect { Factory :customer_pj }
My goal is to initialize 5 customers and put into array. I happen to be doing this in a let in an rspec test.
let(:customers) do
Array.new(5){ Factory :customer_pj }
end
Since Andrew Marshall edited the question, the relevant part became this:
Array.new(5){ Factory :customer_pj }
5.times.map { Factory :customer_pj }

Resources