Skip to content

Instantly share code, notes, and snippets.

@nycdotnet
Last active July 14, 2023 13:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nycdotnet/72859a799053164b66e92a3f65768d16 to your computer and use it in GitHub Desktop.
Save nycdotnet/72859a799053164b66e92a3f65768d16 to your computer and use it in GitHub Desktop.
Useful Ruby Snippets

This repo contains some code snippets that I've found useful related to Ruby and Rails

Quoting aka sanitization:

  table = 'my_table'
  column = 'my_column'
  value = 'some_value'

  query = <<-SQL
      select * from #{ActiveRecord::Base.connection.quote_table_name(table)}
      where #{ActiveRecord::Base.connection.quote_column_name(column)} = #{ActiveRecord::Base.connection.quote(value)}
  SQL
  
  # query equals:            select * from "my_table"\n            where "my_column" = 'some_value'

Asserting that a constructor was called with particular arguments:

  # Adds a spy to the constructor call but does not break object initialization
  # Note: Do not add a spy to :initialize - Ruby will raise warnings.
  expect(MyClass).to receive(:new).with('first arg', 'second arg').and_call_original

Asserting that a thing should throw an error:

  it 'throws' do
    expect { perform }.to raise_error(StandardError, /some regex or string that matches the error message/)
  end
  def perform
    # do thing that will cause an error
  end

  # or
  it 'also throws' do
    expect { 1 / 0 }.to raise_error(StandardError, /divide by zero probably/)
  end

Faking a method call to make it throw an error:

  expect(whatever).to receive(:method_name).and_raise(StandardError, 'Some horrible error!')

Total fake possibly calling original:

  allow(whatever).to receive(:method_name).and_wrap_original do |m, *args|
    # m is the method
    # *args are the args passed to it.
    # you can do anything in here - return a fake result, raise an error, dump a back
    # trace with `Thread.current.backtrace` etc.
    # To return what the original would have been (aka `and_call_original`) do this:
    
    m.call(*args)
  end

Fake an implementation of a method but allow a block it uses to run unchanged:

  allow(whatever).to receive(:method_name).and_wrap_original do |m, *args, &block|
    # m is the method
    # *args are the args passed to it.
    
    block.call
  end

Fake using expression evaluated at time of call:

  allow(whatever).to receive(:method_name) { return_this_expression_which_is_evaluated_at_time_of_call }

Fakes without an object reference:

  allow_any_instance_of(MyClass).to recieve(:method_name).and_return(whatever)

Asserting that a thing should throw an error with specific properties:

  expect { perform }.to raise_error do |error|
    expect(error).to be_a(GRPC::BadStatus)
    expect(error.code).to eq(GRPC::Core::StatusCodes::NOT_FOUND)
  end

Partial assertions on method calls:

# this asserts on the first and second arguments, but ignores the rest.
expect(whatever).to receive(:method_name)
        .with('first argument', '2nd argument', any_args)

Partial match of hash set (ignoring other keys):

expect(whatever).to match(hash_including({ some_symbolized_key: 'some value', 'some_string_key' => 'other_value' }))

Assert details on an object in a hash (effectively nesting matchers):

expect(whatever).to match(hash_including({
  foo: 'bar',
  baz: be_an_instance_of(SomeClass) do |x|
    expect(x.some_property).to eq 42
  end
})

String RegEx match:

expect(whatever).to match(%r{[a-f0-9]{16}})  # sixteen characters of hexadecimal (a-f or 0-9)

Asserting on logging (with Rails)

    # before your test code runs, create spies on the appropriate method of the logger (for example one or more of these)
    allow(Rails.logger).to receive(:debug)
    allow(Rails.logger).to receive(:info)
    allow(Rails.logger).to receive(:warn)
    allow(Rails.logger).to receive(:error)
    
    #later in your assertions (this is an example - can use any have_received variant)
    expect(Rails.logger).to \
        have_received(:info).with(hash_including({
                                                   message: 'Did some thing',
                                                   entity_id: 1234567
                                                 }))

Asserting that a method was called on an object where an argument is an instance of a specific type and has specific nested properties:

    # the below applies both `.with` calls to the first argument.
    expect(my_object).to have_received(:expected_method_name) \
        .with(instance_of(ExpectedArgumentType)) \
        .with(having_attributes(
            some_property: 'abc123',
            other_property: 987654,
            deep_property: having_attributes(foo: 'bar')
        ))

Asserting that contents of an array is equal to contents of another array, but don't care about sorting, use the contain_exactly matcher.

    expect([1, 2, 3, 4]).to contain_exactly([4, 2, 3, 1])

Mocking the environment:

  # this sets up ENV to be a spy
  allow(ENV).to receive(:[]).and_call_original
  # this lets you fake only the specified variable and return the value you want (other variables
  # will be the "real" one within the test context.
  allow(ENV).to receive(:[]).with('MY_VARIABLE_NAME').and_return('my special value')

Mocking RequestStore:

    # this sets up the "read" indexer as a spy (same as ENV above)
    allow(RequestStore.store).to receive(:[]).and_call_original
    # this sets up the "write" indexer as a spy
    allow(RequestStore.store).to receive(:[]=).and_call_original
    allow(RequestStore).to receive(:clear!).and_call_original

    # later code:
    expect(RequestStore.store).to have_received(:[]=).with(:whatever_key_name, whatever_the_expected_value_was)

See if something is there:

.blank? and .present? are opposites - but need ActiveSupport for .blank?. .blank? is true for nil, empty string, and whitespace only strings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment