ruby: howto use multiple boolean options and keep the code readable - ruby

I need to process a couple of boolean options, and I am trying to do it like it is usually done in C:
DICT = 0x000020000
FILTER = 0x000040000
HIGH = 0x000080000
KEEP = 0x000100000
NEXT = 0x000200000
I can now assign arbitrary options to a Integer variable, and test for them:
action if (opts & HIGH|KEEP) != 0
But this looks ugly and gets hard to read. I would prefer writing it like
action if opts.have HIGH|KEEP
This would require to load have method onto Integer class.
The question now is: where would I do that, in order to keep this method contained to the module where those options are used and the classes that include this module? I don't think it's a good idea to add it globally, as somebody might define another have somewhere.
Or, are there better approaches, for the general task or for the given use-case? Adding a separate Options class looks like overkill - or should I?

You can use anybits?:
action if opts.anybits?(HIGH|KEEP)
The methods returns true if any bits from the given mask are set in the receiver, and false otherwise.

Related

Gradle - Get the default value (convention) of a Property

I want to achieve a very basic thing (in any normal language): I would like to get the default value (convention) of a Property. Gradle docs in chapter Lazy configuration, describes how to apply the convention to a Property:
def property = objects.property(String)
// Set a convention
property.convention("convention 1")
println("value = " + property.get())
// Can replace the convention
property.convention("convention 2")
println("value = " + property.get())
property.set("value")
// Once a value is set, the convention is ignored
property.convention("ignored convention")
println("value = " + property.get())
The problem is, that you cannot query the property to get information what is the convention (the default value) when you set a value. It seems that the only way it's to clear (nullify) the value:
property.value(null).get()
But this is just stupid because you are doing unnecessary actions, while the convention is somewhere there?
Does anyone know how to get it without clearing the value?
The answer (for 2022) is: No, you can't get the default value (convention) of a Property.
I'll try to answer why set overrides the convention once it's called. The convention is the default value, once the property has a value by calling set the convention is ignored because the property has a value. That makes sense because that's what a default value should mean. If I have a value use that otherwise use this default value. Convention and set seem to follow that pattern. I don't quite follow why this is surprising to how you want to use gradle, but just as an outside observer I think gradle is doing what is expected.
You can test if a property exists using
if( property.isPresent() ) {
// do whatever
}
I bet that isPresent() is going to say false when the convention would be returned by get(), but if you call set then isPresent() is going to return true.
Then there are other methods to help like:
def val = property.getOrElse( "SomeDefault" )
def maybe = property.getOrNull()
I do think Gradle doesn't always do a good job of making the API very accessible from their home page and docs (ie groovydoc) for answering more complex questions:
https://docs.gradle.org/current/javadoc/org/gradle/api/provider/Property.html
These methods are the super interface Provider and hence why it may not be obvious they are available if you don't look up the chain:
https://docs.gradle.org/current/javadoc/org/gradle/api/provider/Provider.html

How do I programmatically set a content_security_policy?

I'm configuring the Content Security Policy for our Rails 5.2 app. I need to whitelist some domains in our CSP. I'd like to put the list of domains elsewhere so I can reference them in other places in the application, then generate the CSP headers programmatically from that list.
Looking at the source code for the Content Security Policy configuration mechanisms in Rails 5, it looks like there's some magic metaprogramming going on, so it's not clear to me how to accomplish what I need to do. It looks like the functions I need to call to set headers might be picky about how exactly they want to be called. In particular, it's not clear to me if I can pass them arrays or safely call them multiple times, or if they do some metaprogramming magic that only works if the domains are passed in as individual function arguments.
Can I pass in an array to the header I want to set, like this?
whitelisted_domains = ['https://example.com', 'self']
Rails.application.configure do
config.content_security_policy do |csp|
csp.child_src whitelisted_domains
end
end
Or can I call the same function multiple times, like this?
whitelisted_domains = ['https://example.com', 'self']
Rails.application.configure do
config.content_security_policy do |csp|
whitelisted_domains.each {|domain| csp.child_src domain}
end
end
If neither of those will work, what's the best way of accomplishing what I want to do?
From what I can tell from sourcecode and documentation, it takes an array. From the edgeguides at rails, posting following
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https
...
end
and the sourcecode, using *sources as param; it believe it takes any number of arguments, meaning you could do something along the lines of;
whitelisted_domains = ['https://example.com', 'self']
Rails.application.configure do
config.content_security_policy do |csp|
csp.child_src(*whitelisted_domains)
end
end
https://blog.sqreen.io/integrating-content-security-policy-into-your-rails-applications-4f883eed8f45/
https://edgeguides.rubyonrails.org/security.html#content-security-policy
Sourcecode of define_method for each directive
https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/content_security_policy.rb#L151
(note: None of this has been tested in a Rails app, simple looking guides and source code of Rails)

Split array into comma separated list of values

I'm working on a bit of metaprogramming using send methods quite a bit. I've been successful so far because the methods I'm sending to only take one argument.
Example:
client is an API client
#command is a method on client taken as an option to a CLI utility
#verb is a method on command taken as another option in the CLI
def command_keys
case #command
when "something"
self.command_options.slice(:some, :keys)
end
end
Then I call the API client like this:
client.send(#command).send(#verb, command_keys)
This works since the methods all take a Hash as their argument. The problem I've run into is when I need to send more than 1 parameter in command_keys. What I'm wondering is the best way to handle the command_keys method returning more than 1 value. Example:
def command_keys
case #command
when "something"
return self.command_options[:some], self.command_options[:keys]
end
end
In this case, command_keys returns an Array as expected, but when I try to pass that in the send(#verb, command_options) call, it passes it as an Array (which is obviously expected). So, to make a long story short, is there some easy way to make this condition be handled easily?
I know send(#verb, argument1, argument2) would get me the result I want, but I would like to be able to not have to give my script any more implementation logic than it needs, that is to say I would like it to remain as abstracted as possible.
Use splat. You might have to rethink the code a bit, but something like:
client.send(#command).send(#verb, *all_the_args)

Accessing Ruby Threads

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

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!

Resources