How do I change this case statement to an if statement? - ruby

I would like to check for the value of a node attribute. This case statement is what I have so far, and it works:
case node[:languages][:ruby][:host_cpu]
when "x86_64"
...
when "i686"
...
end
What I would like to do is use an if statement instead. This is what I tried:
if node[:languages][:ruby][:host_cpu]?("X86_64")
...
end
This is based on the following, Which worked.
if platform?("ubuntu")
...
end
However, my try didn't work. it gave a syntax error on the if line saying that there was an unexpected \n and $end was expected.
I found that there are two kinds of ways of performing an if. The first being the one I demonstrated above, which (apparently) only works with resources, and if_only, which works with nodes. like so
if_only {node[:languages]}
which seems to work only for checking the presence of nodes, and within a do context.
How do I check the value of a node using an if statement? One method does check values, but only of resources, the other checks nodes, but only for their presence, and not their values.

You are mixing up way to many different variants for conditionals, most of which are part of Chef, not Ruby. Let me try to describe the different options one by one.
Generally, a case is roughly comparable to a series of if and elsif statements. Your case above
case node[:languages][:ruby][:host_cpu]
when "x86_64"
...
when "i686"
...
end
is thus roughly equivalent to
if node[:languages][:ruby][:host_cpu] == "x86_64"
...
elsif node[:languages][:ruby][:host_cpu] == "i686"
...
end
As a side remark, case actually uses the === operator which is often not commutative but more powerful. For simple comparisons it works the same as == though. Both these variants are part of the Ruby language, in which you write your cookbooks.
The other options you mentioned are actually part of the API which Chef defined on top of Ruby. This is often called the Chef DSL (which stands for Domain Specific Language, i.e. an extension or adaption of a language, in this case Ruby for a specific usage domain, in this case configuration management.)
The platform? method is a method defined by Chef that checks whether the curent platform is one of the passed values. You can read more about that (and similar methods, e.g. the now recommended platform_family? method at the Chef docs for recipes in general and some often used ruby idioms.
As a side-remark: you might be surprised by the fact that Ruby allows the ? and ! characters to appear at the end of method names, which makes Ruby rather unique among similar languages in this regard. These characters are simply part of the method name and have no special meaning to the language. They are only used by convention to programmers to better identify the purpose of a method. If a method has a ? at the end, it is to be used to check some condition and is expected to return either a truthy or falsy value. Methods with a ! at the end often perform some potentially dangerous operation, e.g. change object in place, delete stuff, ... Again, this is only a convention and is not interpreted by the language.
The last option you mentioned, the only_if and by extension not_if are used to define conditionals on Chef resources to make sure they are only executed when a certain condition is true (or when using not_if, if it is false). As these attributes are used on Chef resources only, they are naturally also defined by Chef.
To understand why they are useful it is necessary to understand how a Chef run works. The details can be found at the description of the Anatomy of a Chef Run. What is important there is, that you basically have two execution phases: Resource Compilation and Convergence. In the first step, the actual code to define the resources is executed. Here, also the code in your case statement would be run. After all the recipes have been loaded and all the resources have been defined, Chef enters the second phase, the Convergence phase. There, the actual implementation of the resources which performs the changes (create files and directories, in stall packages, ...) is run. Only in this phase, the only_if and not_if conditions are checked.
In fact, you can observe the difference between
file "/tmp/helloworld"
action :create
content "hello world"
end
if File.exist?("/tmp/helloworld")
file "/tmp/foobar"
action :create
content "foobar"
end
end
and
file "/tmp/helloworld"
action :create
content "hello world"
end
file "/tmp/foobar"
action :create
content "foobar"
only_if{ File.exist?("/tmp/helloworld") }
end
In the first variant, the condition whether /tmp/foobar exists is checked during resource compilation. At this time, the code to actually create the /tmp/helloworld file has not been run, as it does that only in the Conversion step. Thus, during your first run, the /tmp/foobar file would not be created.
In the second variant however, the check is done with only_if which is evaluated during conversion. Here you will notice that both files get created in the first run.
If you want to read a bit more on how the definition of the conditionals works in terms of Ruby (and you definitely should), you can read about Ruby Blocks which are more or less pieces of code that can be passed around for later execution.

Related

What Is the correct syntax for writing test definitions in Cucumber?

What is the actual syntax for writing step definitions in Cucumber? I have seen it being written in different ways. Is there no definite syntax? I know the anchors are not compulsory, but is there a basic rule?
I am new to Cucumber and will appreciate baby step information to help me understand the basics. Thanks guys!
I was planning to point you to online documentation, but the online documentation I know about (at cucumber.io and at relishapp.com) doesn't actually answer your question well. (It does contain many examples, though, and is well worth reading.)
In the Ruby implementation of Cucumber, step definition files are .rb files in the features/step_definition directory. They contain a series of calls to methods that each define an implementation of a Gherkin step. Here's an example:
Given /^there is a user named "(.*)"$/ do |username|
# code that creates a user with the given username
end
There are several methods that define steps: Given, When, Then, And and But. They all do exactly the same thing, and you can use any one to define any step. The best practice is to use the one that reads best with the step you're defining (never And or But).
The argument passed to the step-defining method is a regular expression intended to match one or more steps in Gherkin .feature files. The above example matches the following step:
Given there is a user named "Ade Tester"
("Ade Tester" could be anything).
The block passed to the step-defining method is run when Cucumber executes a step which the regular expression matches. It can contain any Ruby code you like.
Matching groups (enclosed in parentheses) in the regular expression are passed to the block as block parameters. The number of matching groups must match the number of block parameters, or you'll get an error. A common convention is to enclose matching groups that match strings in quotes to visually separate them from the fixed part of the step, as I did above, but this is purely convention and you can choose not to do it.
The regexp need not match the entire step by default. If you want a definition to match only the entire step you must enforce that in the regular expression, as I did in the example above with ^ and $. Do that unless you have a good reason not to. This step definition (without $)
Given /^there is a user named "(.*)"/ do |username|
create :user, username: username
end
would match
Given there is a user named "Ade Tester" on weekdays but "Dave Schweisguth" on weekends
which would probably be a bad idea. Worse, if you had definitions for both steps, Cucumber would not be able to tell which definition to use and you'd get an error.
In features/step_definitions/documentation.rb:
When /^I go to the "([^"]+)" documentation$/ do |section|
path_part =
case section
when "Documentation"
"documentation"
else
raise "Unknown documentation section: #{section}"
end
visit "/documentation/#{path_part}/topics"
end
Then /^I should see the "([^"]+) documentation"$/ do |section|
expect(page).to have_css('h2.doctag_title a', text: section)
end
These steps exercise a web application. They are about as simple as they can be while still being practical.
A good step definition should have in its block a single method call e.g.
When "Frank logs in" do
login user: #frank
end
Bad step definitions have lots of code in their block e.g
When "I login as Frank" do
visit root_path
fill_in login_email, with:
# lots of other stuff about HOW to login
...
end
Other bad step definitions use really complicated regex's with lots of arguments.
Terrible step definitions call other step definitions and do the things bad step definitions do.

How to schedule puppet custom facts to execute every X hours?

I have a custom puppet fact (written in ruby) which executes a simple bash script. The script checks all files in the file system for world-readable permissions and returns results accordingly. The puppet fact checks the output of the script and returns "fail" if the script output is not null.
The problem is that the script is resource intensive and I don't want it to be executed every 30 minutes (client' puppet.conf: runinternval=1800).
I've tried using the "schedule" resource but because of the way puppet works, it only affects the class I'm using to evaluate the fact's output. Puppet facts are evaluated on each puppet run, regardless of anything else. (thus making them available for resources)
I've also tried to move the code that executes the bash script out from the fact and into the puppet class but it appears you cannot evaluate the output of a bash script (using "exec" type) and store it in a variable (again, because of the way puppet works).
I am now reduced to try and apply some kind of scheduling mechanism using Ruby language, and implement it in the fact. I've read a bit on PStore and right now this seems like a good direction.
Does anyone know how I can make a ruby script execute only during night? I cannot use crontab because the code will run by puppet.
Code snippet from the puppet class:
if $::myfact == 'fail' {
notify { "warning blah blah...":
loglevel => err,
schedule => 'night',
}
}
Code snippet from the puppet fact:
require 'facter'
Facter.add(:myfact) do
setcode do
if File.exists? '/usr/local/puppet/scripts/myscript.sh'
result = Facter::Util::Resolution.exec("/usr/local/puppet/scripts/myscript.sh")
if result != ''
puts "Result of myfact is: \n #{result}"
'fail'
end
end
end
end
Your custom fact can memorialize its result in a file somewhere on the system. When it is evaluated, it can compare the timestamp on that file to the current system time, and either read back the value or compute it freshly, as appropriate. That also allows you to defer updates (for instance, by touching the file) or to manually request re-evaluation (by deleting the file).
Consider, however, whether it makes sense to decouple the costly evaluation from fact gathering. Even if Facter only occasionally has to re-evaluate the fact from scratch, Facter -- and therefore Puppet -- will take a long time to perform those runs. You could consider instead using a scheduled job to perform the evaluation at whatever intervals you like, and have the custom fact always rely on cached results from the latest of those evaluations.

Conditionally defining functions in Ruby

I have some code that is run in one of a few different locations: as a command line tool with debug output, as part of a larger program that doesn't take any output, and in a rails environment.
There are occasions where I need to make slight changes to the code based on its location, and I realized the following style seems to work:
print "Testing nested functions defined\n"
CLI = true
if CLI
def test_print
print "Command Line Version\n"
end
else
def test_print
print "Release Version\n"
end
end
test_print()
This results in:
Testing nested functions defined
Command Line Version
I've never come across functions that are defined conditionally in Ruby. Is this safe to do?
This isn't how I'm structuring most of my code, but there are a few functions that require complete rewrites per-system.
I don't think that is a clean way.
My suggestion is to define the same sets of methods (with different definition bodies) in different modules, and conditionally include the relevant module into the class/module you are going to call the methods from.
module CLI
def test_print
... # definition for CLI
end
end
module SomeOtherMode
def test_print
... # definition for some other mode
end
end
class Foo
include some_condition ? CLI : SomeOtherMode
end
Foo.new.test_print
If you are only going to use only one mode per run, and think that it is a waste to define the modules that end up not being used, then you can take a further step; define respective modules (CLI, SomeOtherMode, ...) in separate files, and use autoload.
autoload :CLI, "path/to/CLI"
autoload :SomeOtherMode, "path/to/SomeOtherMode"
It's a form of meta-programming and is generally safe. The real risk is not if it will work as expected, but in testing all the variations you create.
The example you've given here makes it impossible to execute the alternate version. To properly exercise both methods you need a way to force the injection of one or the other.

Ruby code blocks and Chef

I am an extremely new person to Ruby and Chef. I have been trying to wrap my head around the syntax and do some research, but I am sure as you all know unless one knows the terminology, it is hard to find what you are looking for.
I have read up on Ruby code blocks, but the Chef code blocks still confuse me. I see something like this for example:
log "a debug string" do
level :debug
end
Which adds "a debug string" to the log. From what I have seen though, it seems to me like it should be represented as:
log do |message|
#some logic
end
Chef refers to these as resources. Can someone please help explain the syntax difference and give me some terminology from which I can start to educate myself with?
If you come from another language (not Ruby), this syntax might seem very strange. Let's break down things.
When calling a method with parameters, in most cases the parentheses are optional:
foo(bar) is equivalent to foo bar
foo(bar, baz) is equivalent to foo bar, baz
A Ruby block of code can be wrapped in curly braces ({}) or inside a do..end block and can be passed to a method as its last parameters (but note that there's no comma and if you're using parentheses it goes after them. Some examples:
foo(bar) { # code here }
foo(bar) do
# code here
end
foo bar do
# code here
end
foo do
# code here
end
In some cases, code blocks can receive parameters, but in Chef the resources' blocks never do. Just for reference, the syntax for that is:
foo(bar) do |baz, qux|
baz + qux
end
Specifically about Chef resources, their syntax is usually:
resource_type(name) do
attribute1 value1
attribute2 value2
end
This means that, when you say:
log "a debug string" do
level :debug
end
you're actually creating a log resource whose name attribute is set to "a debug string". It can later be referred to (in other resources, for example) using log[a debug string].
AFAIK, the name attribute is mandatory for every Chef resource type as it's what makes it unique, and allows you to, among other things, call actions on it after it has been declared.
Side note: The ruby block is usually optional for a Chef resource. If you do something like:
directory "/some/path"
Chef will compile that resource using its default attributes (among which is action :create), and try to create the named directory using those.
The do ... end here is not a usual ruby block statement.
It's a implementation of DSL (Domain Specific Language).
Here's a nice explanation [1]:
there is the concept of an internal DSL, which uses the syntax of an
exŃ–sting language, a host language, such as Ruby. The means of the
language are used to build constructs resembling a distinct language.
The, already mentioned, Rake uses this to make code like this
possible:
task :codeGen do
# do the code generation
end
Hope that answer your question.
[1] : http://www.infoq.com/news/2007/06/dsl-or-not

RSpec -- test if block called with block defined in before

I recently asked how to test in RSpec if a block was called and the answers to that question seem to work in a simple case. The problem is when the initialization with the block is more complex. Then it is done in before and reused by a number of different tests in the context, among them the one testing if the block was evaluated. See the example:
context "the node definition using block of code" do
before do
#n=node do
# this block should be called
end
# some more complex setup concerning #n
end
it "should call the block" do
# how to test it?
end
# here a bunch of other tests using #n
end
In this case the solution with side effect changing value of a local variable does not work. Raising an exception from the block is useless since the whole statement must be properly evaluated to be used by the other tests.
Obviously I could do the tests separately, but it seems to stink, since I must copy-paste the initialization part and since the was-the-block-called test inherently belongs to this very context.
How to test if the block was evaluated in such a case?
Explanation for question asked by #zetetic below.
The context is that I'm implementing a kind of DSL, with nodes defined by their parameters and blocks of code (that can define something else in the scope of node). Since the things defined by the node's block can be pretty generic, at least for the first attempt I just need to be sure the block is evaluated and that what a user provides there will be considered. For now does not matter what it is.
Probably I should refactor my tests now and using mocks make them test behaviors rather then implementation. However it will be a little bit tricky, for the sake of some mixins and dynamic handling of messages sent to objects. For now the cincept of such tests is a little bit fuzzy in my head ;-)
Anyway your answers and comments helped me to better understand how RSpec works and explained why what I'm trying to do looks as if it did not fit to the RSpec.
Try something like this (untested by me):
context "the node definition using block of code" do
let(:node){
node = Node.new "arg1", "arg2", node_block
# more complex stuff here
node
}
context "checking the block is called" do
let(:node_block) {
double = double("node_block")
double.should_receive("some kind of arg").and_return("something")
# this will now cause a fail if it isn't called
double
}
it "should call the block" do
node.blah()
end
end
let(:node_block) {
# some real code
}
subject { node.blah() }
it { should == 2 }
# ...
end
So that's a very shaky piece of code (you'll have to fill in the gaps as you didn't give very much to go on, and let is obviously a lambda too, which could mean you've got to play around with it a bit) that uses let and a double to check it's called, and avoids using before, which is really for side effects not setting up variables for use in the specs.
#zetetic makes a very insightful comment that you're not testing behaviour here. I'm not against using rspec for doing more unit test style stuff (guidelines are made to be broken), but you might ask how later tests will pass when using a real block of code if that block isn't being called? In a way, I'm not even sure you need to check the block is called, but only you know.

Resources