RuboCop complains when using 'Hash.new' - ruby

RuboCop complains when I use Hash.new, and suggests that I instead use a hash literal. Is there a way to make RuboCop ignore uses of Hash.new? More specifically, can I edit my .rubocop.yml configuration to allow the use of Hash.new without raising any complaints?

You can disable the Rubocop::Cop::Style::EmptyLiteral cop within the rubocop.yml file:
# .rubocop.yml
Style:
EmptyLiteral: false
Or if you want just to ignore a certain line:
hsh = Hash.new # rubocop:disable Style/EmptyLiteral

According to Ruby Style Guide literal array and hash creation notation are preferred unless you need to pass parameters to their constructors. So, to follow the guide you should use hash = {} instead hash = Hash.new.
I would follow the conventions in the guide, but in case that you do not want, you can disable the Style/EmptyLiteral cop, as for any other cop, either locally or globally.
Globally
Add to you rubocop.yml file:
Style:
EmptyLiteral: false
Locally
# rubocop:disable Style/EmptyLiteral
hash = Hash.new
# rubocop:enable Style/EmptyLiteral
or the short version for a single line:
hash = Hash.new # rubocop:disable Style/EmptyLiteral
For more information about how to configure Rubocop check its documentation.

Related

In Ruby, using Rdoc, how can aliases of methods to ENV be documented?

Starting with the very concrete issue I face, I want Rdoc to recognize documentation I placed around my monkeypatch of ENV:
# ENV is a hash-like accessor for environment variables.
ENV.singleton_class.class_eval do
##
# :singleton-method: ENV::[name]
# Returns the value for the environment variable +name+ if it exists.
#
# Note that unlike +ENV::fetch+, this method does not raise any error if the
# requested key is valid and not set, but simply returns +nil+.
#
# This difference of behavior is taken into account in provided aliases.
##
# Alternative to +ENV::[]+, that is, returns the value for the environment
# variable name if it exists.
#
# This alias provide a trigraph which is moreover lexically close to the
# classical get/set accessor identifiers. This let +get+ as a possible
# alias for fetch, while giving a good hint on the fact that this method
# is generally a quicker accessor – with less safety net.
#
# Merriam-Webster gives the following pertaining definition for jet:
# transitive verb : to emit in a stream : spout
# See: https://www.merriam-webster.com/dictionary/jet
alias_method :jet, :[]
##
# Trigraph alternative to +ENV::[]=+ and +ENV::store+.
#
# Merriam-Webster gives the following pertaining definition for sow:
# To spread abroad; to propagate.
# See: https://www.merriam-webster.com/dictionary/sow
alias_method :sow, :[]=
end
So my goal is that when rdoc is run, it generates an entry for ENV in the Class and Module Index and document ::[] and its provided aliases. How might I do that?

With Ruby and RSpec is there a way with rubocop to disallow constants being declared in the global scope?

I am working on setting up rubocop on a test suite I've taken over. The test suite contains a lib/constants.rb config that is made available to the test suite, and all tests are all found in a spec/* folder. I am setting up rubocop so that it only runs against the test specs (spec/*), and ignores the lib folder.
I have set up RSpec/LeakyConstantDeclaration with rubocop to ensure nobody is potentially re-assigning any of the global constants. This cop rule works for any specs written like this, and rubocop will correctly flag RSpec/LeakyConstantDeclaration on this file:
# spec/some_test_spec.rb
describe 'some test' do
SOME_CONSTANT = { 'blah' => 'blah' }
# SOME_CONSTANT used somewhere in here
it 'tests something' do
...
end
end
However, I've recently found some more tests that are also doing this (EEK!):
# spec/some_other_test_spec.rb
SOME_CONSTANT = { 'blah' => 'blah' }
describe 'some other test' do
# SOME_CONSTANT used somewhere in here
it 'tests something else' do
...
end
end
Some devs have started declaring their own constants explicitly in the global scope, and this could also potentially re-assign a constant created in the lib/constants.rb.
Since I'm running rubocop only against the test folder of spec/* is there any way to write a rubocop convention rule that disallows constants, and recommends using let or a local variable instead?
I'm thinking I may have to write a custom Cop to do this, but wondering if there is a known existing way to do it?
Thanks.

# rubocop:disable Metrics/AbcSize

I am really stuck in this part:
If i disable # rubocop:disable Metrics/AbcSize, then I am getting this error:
ruby -v : ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin14]
rubocop -V
0.52.1 (using Parser 2.4.0.2, running on ruby 2.4.2 x86_64-darwin14)
$ rubocop .
Inspecting 7 files
.....W.
Offenses:
recipes/default.rb:13:1: W: Lint/MissingCopEnableDirective: Re-enable Metrics/AbcSize cop with # rubocop:enable after disabling it.
# rubocop:disable Metrics/AbcSize
^
7 files inspected, 1 offense detected
If I enable rubocop in the script then getting this:
rubocop .
Inspecting 7 files
.....C.
Offenses:
recipes/default.rb:31:1: C: Metrics/AbcSize: Assignment Branch Condition size for check_tropo_versions is too high. [33.02/20]
def check_tropo_versions ...
^^^^^^^^^^^^^^^^^^^^^^^^
7 files inspected, 1 offense detected
A few lines of my script:
# rubocop:enable Metrics/AbcSize
require 'nokogiri'
Chef.event_handler do
on :resource_updated do |resource, _action|
if resource.declared_type.to_s == 'remote_file' && resource.cookbook_name == 'tropo-patch' && resource.recipe_name == 'default'
puts "#{Time.now.strftime('%Y%m%d%H%M%S')},#{resource.path},#{resource.source[0]}"
File.open('/var/log/tropo-patch.log', 'a') { |f| f.write("#{Time.now.strftime('%Y%m%d%H%M%S')},#{resource.path},#{resource.source[0]}\n") }
end
end
end
I cannot disable rubocop from global config file, but if it can resolve I will try this as well:
Metrics/AbcSize:
Description: >-
A calculated magnitude based on number of assignments,
branches, and conditions.
Reference: 'http://c2.com/cgi/wiki?AbcMetric'
Enabled: true
What Rubocop is basically complaining about is that it asks you to enable the cop after the method is closed, so the cop can be in effect for other methods of the file. If you disable it on the first line of the file, it will be disable for all methods in that file. Disabling cops for whole files is not a good idea, since you should be explicit and conscious when disabling the cops.
The fix in your case is just simply disabling the cop before your method, and then enable it after.
For example:
# rubocop:disable Metrics/AbcSize
require 'nokogiri'
Chef.event_handler do
on :resource_updated do |resource, _action|
if resource.declared_type.to_s == 'remote_file' && resource.cookbook_name == 'tropo-patch' && resource.recipe_name == 'default'
puts "#{Time.now.strftime('%Y%m%d%H%M%S')},#{resource.path},#{resource.source[0]}"
File.open('/var/log/tropo-patch.log', 'a') { |f| f.write("#{Time.now.strftime('%Y%m%d%H%M%S')},#{resource.path},#{resource.source[0]}\n") }
end
end
end
# rubocop:enable Metrics/AbcSize
Also, if you need to disable multiple cops, you can enable all of them by using:
# rubocop:enable all
Hope that helps!
Edit: After looking at Tom Lord's comment on your question, I noticed that you haven't posted the actual method that is breaking the rule of the cop. Still, the solution I provided should work if your put the disable and enable statements at the correct places. In addition, I second what Tom stated, that if you show us the code we might improve the method and you will not need to disable the cop for the method.
You can disable a cop for this method only, instead of adding # rubocop:disable at the top of the file, just mark your offender like this
def check_tropo_versions # rubocop:disable Metrics/AbcSize
# ...
Use cookstyle in place of rubocop there. It’s got better defaults for linting cookbook code.

How do I turn a hash to a string in Puppet DSL?

I have a hash of hashes that I need to embed in an exec resource command. My thought was to serialize the hash to a string and interpolate it into the exec call. The exec call will be executing ruby code via ruby -e 'ruby code here'.
Using irb, I know that hash.to_s creates a single line parse-able version of the hash. Or I could use json. I doubt you can call to_s in puppet, but am not sure.
The stdlib for Puppet has parseyaml and parsejson to deserialize, but is there a way to serialize to a parse-able string? I can write a custom puppet function to do it, but prefer an already built in solution if there is one.
Update
I am considering defining a puppet function. I have never written one before, so am not sure of the syntax. Here is my first attempt:
Puppet::Parser::Functions.newfunction(
:serialize_hash,
:arity => 2,
:doc => "Serialize a hash to any depth and optionally escape the double quotes.",
:type => :rvalue) do |args|
hash = args[0]
escape_quotes = args[1]
serialized = hash.to_s
if (escape_quotes)
serialized.sub!(/"/, "\\\"")
end
serialized
end
You can always execute ruby code inline with your puppet module:
$my_string = inline_template('<%= #my_hash.to_s %>')
Obviously it is important to not overuse this, but it is particularly useful when a very simple ruby function can achieve what you need.

How to define a simple global variable in an rspec test that can be accesed by helper functions

I cant figure out how to use a simple global variable in an rspec test. It seems like such a trivial feature but after much goggleing I havent been able to find a solution.
I want a variable that can be accessed/changed throughout the main spec file and from functions in helper spec files.
Here is what I have so far:
require_relative 'spec_helper.rb'
require_relative 'helpers.rb'
let(:concept0) { '' }
describe 'ICE Testing' do
describe 'step1' do
it "Populates suggestions correctly" do
concept0 = "tg"
selectConcept() #in helper file. Sets concept0 to "First Concept"
puts concept0 #echos tg?? Should echo "First Concept"
end
end
.
#helpers.rb
def selectConcept
concept0 = "First Concept"
end
Can someone point out what I am missing or if using "let" is totally the wrong method?
Consider using a global before hook with an instance variable: http://www.rubydoc.info/github/rspec/rspec-core/RSpec/Core/Configuration
In your spec_helper.rb file:
RSpec.configure do |config|
config.before(:example) { #concept0 = 'value' }
end
Then #concept0 will be set in your examples (my_example_spec.rb):
RSpec.describe MyExample do
it { expect(#concept0).to eql('value') } # This code will pass
end
It turns out the easiest way is to use a $ sign to indicate a global variable.
See Preserve variable in cucumber?
This is an old thread, but i had this question today. I just needed to define a long string to stub out a command that is in multiple files as:
# in each spec file that needed it
let(:date_check) do
<<~PWSH.strip
# lots of powershell code
PWSH
end
# in any context in that file (or a shared context)
before(:each) do
stub_command(date_check).and_return(false)
end
Searched, Stack Overflow, etc, landed on this: Note the usage of the variable doesn't change at all! (Assumes all specs require 'spec_helper')
# in spec_helper.rb
def date_check
<<~PWSH.strip
# lots of powershell code
PWSH
end
# in any context in any spec file
before(:each) do
stub_command(date_check).and_return(false)
end
I suggest you define the variable in the helper file, where it can be used by other helper code, and can be accessed from your tests.
For my project, I wanted to keep all the setup stuff in spec_helper.rb, and use those settings, plus any custom variables and methods in the tests. The following, modified from the RSpec-core 3.10 docs, is not Rails-specific.
Create a new setting for RSpec.configure called my_variable, and give it a value, like this:
# spec/spec_helper.rb
RSpec.configure do |config|
config.add_setting :my_variable
config.my_variable = "Value of my_variable"
end
Access settings as a new read-only property in RSpec.configuration from your test:
# spec/my_spec.rb
RSpec.describe(MyModule) do
it "creates an instance of something" do
my_instance = MyModule::MyClass.new(RSpec.configuration.my_variable)
end
end

Resources