Accessing Ruby Threads - ruby

Probably not even a valid question but how can I see what this block contains:
spec = Thread.current[:spec]
print spec # gives => #<RSpec::Core::ExampleGroup::Nested_1:0x7f61991d90c8>
Can I see any of the methods assigned to this or whatever is in it?
If more context is needed, I'm trying to understand what spec is doing here in function here but not being used anywhere (at least directly)
https://github.com/amfranz/rspec-hiera-puppet/blob/master/lib/rspec-hiera-puppet/puppet.rb#L7

To view properties do:
spec.inspect
If you want to access those properties:
spec.propertyyouwant

Related

How can I mock a Ruby "require" statement in RSpec?

I have a Ruby cli program that can optionally load a user-specified file via require. I would like to unit test this functionality via RSpec. The obvious thing to do is to mock the require and verify that it happened. Something like this:
context 'with the --require option' do
let(:file) { "test_require.rb" }
let(:args) { ["--require", "#{file}"] }
it "loads the specified file"
expect(...something...).to receive(:require).with(file).and_return(true)
command.start(args)
end
end
(That's just typed, not copy/pasted - the actual code would obscure the question.)
No matter what I try, I can't capture the require, even though it's occurring (it raises a LoadError, so I can see that). I've tried a variety of things, including the most obvious:
expect(Kernel).to receive(:require).with(file).and_return(true)
or even:
let(:kernel_class) { class_double('Kernel') }
kernel_class.as_stubbed_const
allow(Kernel).to receive(:require).and_call_original
allow(Kernel).to receive(:require).with(file).and_return(true)
but nothing seems to hook onto the require
Suggestions?
So require is defined by Kernel but Kernel is included in Object so when you call require inside this context it is not necessarily the Kernel module that is processing the statement.
Update
I am not sure if this exactly solves your issue but it does not suffer from the strange behavior exhibited below:
file = 'non-existent-file'
allow(self).to receive(:require).with(file).and_return(true)
expect(self).to receive(:require).with(file)
expect(require file).to eq(true)
Working Example
OLD Answer:
This is incorrect and exists only for posterity due to the up-votes received. Some how works without the allow. Would love it if someone could explain why as I assumed it should raise instead. I believe the issue to be related to and_return where this is not part of the expectation. My guess is we are only testing that self received require, with_file, and that the and_return portion is just a message transmission (thus my updated answer)
You can still stub this like so:
file = 'non-existent-file.rb'
allow_any_instance_of(Kernel).to receive(:require).with(file).and_return(true)
expect(self).to receive(:require).with(file).and_return(true)
require file
Since I am unclear on your exact implementation since you have obfuscated it for the question I cannot solve your exact issue.

How can I call a Chef resource from an HWRP?

Maybe this is really simple, and I'm just not understanding something. I want to invoke a Chef resource from within an HWRP that I wrote. In my scenario, I'd like to invoke the reboot resource. How should I go about doing so?
I have tried something like the following:
def reboot_system
wu_reboot = Chef::Resource::Reboot.new('wu_reboot', :reboot_now)
wu_reboot.run_action(:reboot_now)
end
A few things. I am not sure if I should be creating an instance of Chef::Resource::Reboot or Chef::Provider::Reboot. I also don't really understand the second argument listed above..this is supposed to be the "run_context", but I don't know what that is. Finally, I do not know how to set attributes or invoke an action.
I tried using this as a format to go by, but I haven't been able to get it to work so far. Any help understanding would be much appreciated.
EDIT:
I looked at the source code and I could just execute this:
node.run_context.request_reboot(
:delay_mins => #new_resource.delay_mins,
:reason => #new_resource.reason,
:timestamp => Time.now,
:requested_by => #new_resource.name
)
However, I don't think this is the best solution. I would like to know how to accomplish invoking the resource instead of bypassing it this way.
You can find an example of using Chef-Resources inside a HWRP in an older revision of the official Jenkins cookbook (was converted to LWRP in the meantime):
https://github.com/opscode-cookbooks/jenkins/blob/v2.0.2/libraries/plugin.rb#L138-L141
Keep in mind, that the Reboot resource is rather new (Chef 12+)
You can do it the same way you would in a recipe. If you need it to run immediately, then you would do:
reboot 'now' do
action :nothing
end.run_action(:reboot_now)
Within Ruby classes, you don't have access to the Chef DSL, so you have to access the underlying implementation of the resource as a class. The name of the class will be the camelcase-conversion of the resource name. You invoke the action with the run_action method.
Your original version actually was pretty close. You only use the resource, not the provider (because the provider may not even always be the same, depending on your platform).
The run_context is an object that chef uses to pass information to the resource - for instance, you can access node attributes through run_context.node['attributename']. It is already a member variable in your provider (and I think also in the resource object); you can simply pass it in to the constructor for your new resource.
You set attributes through member variables by the same name, and you trigger the actual action with the run_action method.
r = Chef::Resource::Reboot.new("wu_reboot", run_context)
r.reason("Because we need a reboot")
r.run_action(:reboot_now)

Is there any way to delay a resource's attribute resolution until the "execute" phase?

I have two LWRPs. The first deals with creating a disk volume, formatting it, and mounting it on a virtual machine, we'll call this resource cloud_volume. The second resource (not really important what it does) needs a UUID for the newly formatted volume which is a required attribute, we'll call this resource foobar.
The resources cloud_volume and foobar are used in a recipe something like the following.
volumes.each do |mount_point, volume|
cloud_volume "#{mount_point}" do
size volume['size']
label volume['label']
action [:create, :initialize]
end
foobar "#{mount_point}" do
disk_uuid node[:volumes][mount_point][:uuid] # This is set by cloud_volume
action [:do_stuff]
end
end
So, when I do a chef run I get a Required argument disk_identifier is missing! exception.
After doing some digging I discovered that recipes are processed in two phases, a compile phase and an execute phase. It looks like the issue is at compile time as that is the point in time that node[:volumes][mount_point][:uuid] is not set.
Unfortunately I can't use the trick that OpsCode has here as notifications are being used in the cloud_volume LWRP (so it would fall into the anti-pattern shown in the documentation)
So, after all this, my question is, is there any way to get around the requirement that the value of disk_uuid be known at compile time?
A cleaner way would be to use Lazy Attribute Evaluation. This will evaluate node[:volumes][mount_point][:uuid] during execution time instead of compile
foobar "#{mount_point}" do
disk_uuid lazy { node[:volumes][mount_point][:uuid] }
action [:do_stuff]
end
Disclaimer: this is the way to go with older Chef (<11.6.0), before they added lazy attribute evaluation.
Wrap your foobar resource into ruby_block and define foobar dynamically. This way after the compile stage you will have a ruby code in resource collection and it will be evaluated in run stage.
ruby_block "mount #{mount_point} using foobar" do
block do
res = Chef::Resource::Foobar.new( mount_point, run_context )
res.disk_uuid node[:volumes][mount_point][:uuid]
res.run_action :do_stuff
end
end
This way node[:volumes][mount_point][:uuid] will not be known at compile time, but it also will not be accessed at compile time. It will only be accessed in running stage, when it should already be set.

PageObject with Ruby - set text in a text field only works in the main file

I'm automating a site that has a page with a list of options selected by a radio button. When selecting one of the radios, a text field and a select list are presented.
I created a file (test_contracting.rb) that is the one through which I execute the test (ruby test_contracting.rb) and some other classes to represent my page.
On my class ContractPage, I have the following element declaration:
checkbox(:option_sub_domain, :id => "option_sub_domain")
text_field(:domain, :id => "domain_text")
select_list(:tld, :id => "domain_tld")
I've created in the ContractPage a method that sets the configuration of the domain like this:
def configure_domain(config={})
check_option_sub_domain
domain = config[:domain]
tld = config[:tld]
end
When I call the method configure_domain from the test_contracting.rb, it selects the radio button, but it doesn't fill the field with the values. The params are getting into the method correctly. I've checked it using "puts". Even if I change the params to a general string like "bla" it doesnt work. The annoying point is that if on test_contracting.rb I call the exact same components, it works.
my_page_instance = ContractPage.new(browser)
my_page_instance.domain = "bla"
my_page_instance.tld = ".com"
What I found to work was to in the configure_domain method, implement the following:
domain_element.value = config[:domain]
tld_element.send_keys config[:locaweb_domain]
Then it worked.
The documentation for the PageObjects module that I'm using as reference can be found here: http://rubydoc.info/github/cheezy/page-object/master/PageObject/Accessors#select_list-instance_method
Do you guys have any explation on why the method auto generated by the pageobject to set the value of the object didnt work in this scope/context ?
By the way, a friend tried the same thing with Java and it failed as well.
In ruby all equals methods (methods that end with the = sign) need to have a receiver. Let me show you some code that will demonstrate why. Here is the code that sets a local variable to a value:
domain = "blah"
and here is the code that calls the domain= method:
domain = "blah"
In order for ruby to know that you are calling a method instead of setting a local variable you need to add a receiver. Simply change your method above to this and it will work:
def configure_domain(config={})
check_option_sub_domain
self.domain = config[:domain]
self.tld = config[:tld]
end
I'm pretty new to this world of Selenium and page objects but maybe one of my very recent discoveries might help you.
I found that that assignment methods for the select_list fields only worked for me once I started using "self" in front. This is what I have used to access it within my page object code. e.g., self.my_select_list="my select list value"
Another note - The send_keys workaround you mention is clever and might do the trick for a number of uses, but in my case the select list values are variable and may have several options starting with the same letter.
I hope something in here is useful to you.
UPDATE (Jan 3/12)
On diving further into the actual Ruby code for the page object I discovered that the select_list set is also using send_keys, so in actuality I still have the same limitation here as the one I noted using the send_keys workaround directly. sigh So much to learn, so little time!

Ruby - Difference between :variable and #variable

As a Ruby on Rails newbie, I understand that the "#" and ":" references have different meanings. I saw this post in SO, which described some of the differences.
# indicates a instance variable (e.g., #my_selection)
: indicates an alias (e.g., :my_selection)
I ran into a situation where I had a standard MVC page, similar to all of the other forms/pages in my webapp.
html.erb snippet
<%= form_for #my_selection do |f| %>
route.rb snippet
resources :my_selections
When I attempt to access this page, I get this error:
NoMethodError in selections#create
Showing C:/somedir/myapp/app/views/my_selections/index.html.erb where line #16 raised:
undefined method `my_selection_index_path' for #<#<Class:0x1197e5676>:0x25439c3b>
Line 16 is the form snippet shown above.
All of my other forms/pages in the same web app are set up in exactly the same way and are working fine. However, once I changed the erb form reference to :my_selection, this error went away and my page behaved normally.
Questions:
Is my understanding of the difference between :my_selections and #my_selections correct?
Why would switching to :my_selection resolve my original error?
Is my understanding of the difference between :my_selections and
#my_selections correct?
Nope :(
: indicates a symbol, its not an alias for anything intrinsically. It's like an immutable string, which is often used as a name to represent something.
In places where the Rails api accepts a symbol in place of an instance variable, internally it's actually doing this:
self.instance_variable_get "##{my_symbol}"
Which actually returns the value of the requested instance variable.
So the only reason that you think symbol correspond to instance variable at all, is because the code that drives the API you are using works that way. Without a framework to do that for you, there is no correlation at all.
Why would switching to :my_selection resolve my original error?
for_form(model_instance) will generate a form that submits to the create action if the model instance is unsaved, or to the update action if the model is already exiting in your DB.
No I don't know what's in #my_selection, but whatever class it is doesn't seem to be generating the routes properly.
resources :my_selections
Will generate a route you would invoke like this:
my_selections_path
How your form is generating a route for my_selection_index_path I'm not sure and it really depends on what your models are.
And when you pass a symbol instead, and there is no corresponding ivar, it uses that as the model name for route generation. Which would do the right thing by trying to invoke my_selections_path, which is directly based on the symbol you pass in.

Resources