Ruby / RSpec - Expect not to include any of - ruby

I have the following spec:
describe 'active' do
it 'does not include inactive or deleted records' do
inactive_record= create(:record, :inactive)
deleted_record= create(:record, :deleted)
expect(described_class.active).not_to include inactive_record
expect(described_class.active).not_to include deleted_record
end
end
This is OK when there are two tests, but when I have 10 different statuses I need to check, I'd need to write out ten different expect lines. I can do something like this:
[records_not_to_be_included].each { |record| expect(described_class.active).not_to include record }
But would like to be able to do something like:
expect(described_class.active).not_to include_any_of [records_not_to_be_included]
Is this possible with RSpec?

As you can see, from the docs:
# Passes if actual includes expected. This works for
# collections and Strings. You can also pass in multiple args
# and it will only pass if all args are found in collection.
#
# #example
# expect([1,2,3]).to include(3)
# expect([1,2,3]).to include(2,3)
# expect([1,2,3]).not_to include(4)
# expect("spread").to include("read")
# expect("spread").not_to include("red")
# expect(:a => 1, :b => 2).to include(:b => 2, :a => 1)
# expect(:a => 1, :b => 2).not_to include(:a => 2)
# ...
def include(*expected)
BuiltIn::Include.new(*expected)
end
include accepts one or more elements, so you can try with:
expect(described_class.active).not_to include(inactive_record, deleted_record)

Related

Ruby Yard documentation: how to add a "verbatim" (to generate something like a <pre> tag)

I want a piece of code, like a hash, to display with fixed typeface on the resulting html. Suppose this is the contents of my file:
=begin
One example of valid hash to this function is:
{
:name => "Engelbert",
:id => 1345
}
=end
def f hash_param
# ...
end
How to instruct yard (using the default of the version 0.9.15) so a yard doc file.rb will generate, for the hash example, the equivalent of adding 4 backslashes to the markdown format, or 4 starting empty spaces to stackoverflow, or the <pre> tag in html, resulting in a verbatim/fixed typeface format in the resulting html?
Expected output:
One example of valid hash to this function is:
{
:name => "Engelbert",
:id => 1345
}
EDIT
> gem install redcarpet
> yard doc --markup-provider redcarpet --markup markdown - file.rb
Should wrap the contents of file.rb within a <pre> tag, producing this page.
Use #example
Show an example snippet of code for an object. The first line is an optional title.
# #example One example of valid hash to this function is:
# {
# :name => "Engelbert",
# :id => 1345
# }
def f hash_param
# ...
end
Maybe I don't get your question:
the equivalent of adding 4 backslashes to the markdown format, or 4 starting empty spaces to stackoverflow
If I use the 4 starting empty spaces in my code like this:
=begin
One example of valid hash to this function is:
{
:name => "Engelbert",
:id => 1345
}
=end
def f hash_param
# ...
end
then I get
But maybe you can also use #option:
#param hash_param
#option hash_param [String] :name The name of...
#option hash_param [Integer] :id The id of...
and you get:
Disclaimer: I used yard 0.9.26 for my examples.

ruby object to_s gives unexpected output

What is the correct way to view the output of the puts statements below? My apologies for such a simple question.... Im a little rusty on ruby. github repo
require 'active_support'
require 'active_support/core_ext'
require 'indicators'
my_data = Indicators::Data.new(Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-08-25', :end_date => '2012-08-30').output)
puts my_data.to_s #expected to see Open,High,Low,Close for AAPL
temp=my_data.calc(:type => :sma, :params => 3)
puts temp.to_s #expected to see an RSI value for each data point from the data above
Maybe check out the awesome_print gem.
It provides the .ai method which can be called on anything.
An example:
my_obj = { a: "b" }
my_obj_as_string = my_obj.ai
puts my_obj_as_string
# ... this will print
# {
# :a => "b"
# }
# except the result is colored.
You can shorten all this into a single step with ap(my_obj).
There's also a way to return objects as HTML. It's the my_obj.ai(html: true) option.
Just use .inspect method instead of .to_s if you want to see internal properties of objects.

RSpec custom diffable matcher

I have a custom matcher in RSpec, that ignores whitespaces / newlines, and just matches content:
RSpec::Matchers.define :be_matching_content do |expected|
match do |actual|
actual.gsub(/\s/,'').should == expected.gsub(/\s/,'')
end
diffable
end
I can use it like this:
body = " some data \n more data"
body.should be_matching_content("some data\nmore wrong data")
However, when a test fails (like the one above), the diff output looks not good:
-some data
-more wrong data
+ some data
+ more data
Is it possible to configure the diffable output? The first line some data is right, but the second more wrong data is wrong. It would be very useful, to only get the second line as the root cause of the failure.
I believe you should disable default diffable behaviour in RSpec and substitute your own implementation:
RSpec::Matchers.define :be_matching_content do |expected|
match do |actual|
#stripped_actual = actual.gsub(/\s/,'')
#stripped_expected = expected.gsub(/\s/,'')
expect(#stripped_actual).to eq #stripped_expected
end
failure_message do |actual|
message = "expected that #{#stripped_actual} would match #{#stripped_expected}"
message += "\nDiff:" + differ.diff_as_string(#stripped_actual, #stripped_expected)
message
end
def differ
RSpec::Support::Differ.new(
:object_preparer => lambda { |object| RSpec::Matchers::Composable.surface_descriptions_in(object) },
:color => RSpec::Matchers.configuration.color?
)
end
end
RSpec.describe 'something'do
it 'should diff correctly' do
body = " some data \n more data"
expect(body).to be_matching_content("some data\nmore wrong data")
end
end
produces the following:
Failures:
1) something should diff correctly
Failure/Error: expect(body).to be_matching_content("some data\nmore wrong data")
expected that somedatamoredata would match somedatamorewrongdata
Diff:
## -1,2 +1,2 ##
-somedatamorewrongdata
+somedatamoredata
You can use custom differ if you want, even reimplement this whole matcher to a system call to diff command, something like this:
♥ diff -uw --label expected --label actual <(echo " some data \n more data") <(echo "some data\nmore wrong data")
--- expected
+++ actual
## -1,2 +1,2 ##
some data
- more data
+more wrong data
Cheers!
You can override the expected and actual methods that will then be used when generating the diff. In this example, we store the expected and actual values as instance variables and define methods that return the instance variables:
RSpec::Matchers.define :be_matching_content do |expected_raw|
match do |actual_raw|
#actual = actual_raw.gsub(/\s/,'')
#expected = expected_raw.gsub(/\s/,'')
expect(expected).to eq(#actual)
end
diffable
attr_reader :actual, :expected
end
Another example is to match for specific attributes in two different types of objects. (The expected object in this case is a Client model.)
RSpec::Matchers.define :have_attributes_of_v1_client do |expected_client|
match do |actual_object|
#expected = client_attributes(expected_client)
#actual = actual_object.attributes
expect(actual_object).to have_attributes(#expected)
end
diffable
attr_reader :actual, :expected
def failure_message
"expected attributes of a V1 Client view row, but they do not match"
end
def client_attributes(client)
{
"id" => client.id,
"client_type" => client.client_type.name,
"username" => client.username,
"active" => client.active?,
}
end
end
Example failure looks like this:
Failure/Error: is_expected.to have_attributes_of_v1_client(client_active_partner)
expected attributes of a V1 Client view row, but they do not match
Diff:
## -1,6 +1,6 ##
"active" => true,
-"client_type" => #<ClientType id: 2, name: "ContentPartner">,
+"client_type" => "ContentPartner",
"id" => 11,
There is a gem called diffy which can be used.
But it goes through a string line by line and compares them so instead of removing all whitespace you could replace any amount of whitespace with a newline and diff those entries.
This is an example of something you could do to improve your diffs a little bit. I am not 100% certain about where to insert this into your code.
def compare(str1, str2)
str1 = break_string(str1)
str2 = break_string(str2)
return true if str1 == str2
puts Diffy::Diff.new(str1, str2).to_s
return false
end
def break_string(str)
str.gsub(/\s+/,"\n")
end
The diffy gem can be set to produce color output suitable for the terminal.
Using this code would work like this
str1 = 'extra some content'
str2 = 'extra more content'
puts compare(str1, str2)
this would print
extra
-some # red in terminal
+more # green in terminal
content
\ No newline at end of file

meta-ruby: how to dynamically call a scoped constant?

Say I have a class like:
class Person
module Health
GOOD = 10
SICK = 4
DEAD = 0
end
end
I can reference such Health codes like: Person::Health::GOOD. I'd like to dynamically generate a hash that maps from number values back to constant names:
{ 10 => "GOOD",
4 => "SICK",
0 => "DEAD" }
To do this dynamically, I've come up with:
Person::Health.constants.inject({}) do |hsh, const|
hsh.merge!( eval("Person::Health::#{const}") => const.to_s )
end
This works, but I wonder if there's a better/safer way to go about it. It's in a Rails app, and while it's nowhere near any user input, eval still makes me nervous. Is there a better solution?
You can use constants and const_get for this purpose.
ph = Person::Health # Shorthand
Hash[ph.constants(false).map { |c| [ph.const_get(c), c.to_s ] }]
# {10=>:GOOD, 4=>:SICK, 0=>:DEAD}
I added false to .constants to prevent including any inherited constants from included Modules. For example, without false the following scenario would also include a 5 => "X" mapping:
module A
X = 5
end
class Person
module Health
include A
# ...
end
end
Hash[ph.constants.map { |c| [ph.const_get(c), c.to_s ] }]
# {10=>"GOOD", 4=>"SICK", 0=>"DEAD", 5=>"X"}

Calling save on a destroyed object returns true, but doesn't save

Getting weird behavior when trying to save a Mongoid object that has been previously destroyed. Given this class definition:
class Foo
include Mongoid::Document
end
After saving an instance, then deleting it, I am unable to save again:
Foo.count # => 0
f = Foo.create # => #<Foo _id: 522744a78d46b9b09f000001, >
Foo.count # => 1
f.destroy # => true
Foo.count # => 0
f.save # => true
# it lied - didn't actually save:
Foo.count # => 0
# these may be relevant:
f.persisted? # => false
f.destroyed? # => true
f.new_record? # => false
f.changed? # => false
Here's a failing RSpec test that I would expect to pass:
describe Foo do
it 'should allow saving a Foo instance after destroying it' do
expect(Foo.count).to eq(0)
f = Foo.create
expect(Foo.count).to eq(1)
Foo.all.destroy
expect(Foo.count).to eq(0)
f.save # => true
expect(Foo.count).to eq(1) # error - returns 0
end
end
Is this expected behavior? My use case is actually using a singleton object (didn't want to make the question more complicated by mentioning it though); Foo.instance returns the same object that was destroyed by Foo.all.destroy which is gumming up things.
Model#save
Saves the changed attributes to the database atomically, or insert the document if new. Will raise an error of validations fail.
After destruction, the document is not new and there are no attributes that have changed, so save just returns without errors. In a strict sense this seems to be the expected behavior.
You could use Model#upsert:
Performs a MongoDB upsert on the document. If the document exists in the database, it will get overwritten with the current attributes of the document in memory. If the document does not exist in the database, it will be inserted.
This will actually save the document using the same ID, but it will still be frozen? and marked as destroyed?. Therefore it might be better to just clone the document as suggested by insane-36 in the comments.

Resources