Class: NoBrainer::Matchers::HaveField

Inherits:
Object
  • Object
show all
Defined in:
lib/matchers/have_field.rb

Overview

The ‘have_field` matcher tests that the table that backs your model has a specific field.

class User
  include NoBrainer::Document

  field :name
end

# RSpec
RSpec.describe User, type: :model do
  it { is_expected.to have_field(:name) }
end

#### Qualifiers

##### of_type

Use ‘of_type` to assert that a field is defined as a certain type.

class User
  include NoBrainer::Document

  field :name, type: String
end

# RSpec
RSpec.describe User, type: :model do
  it do
    it { is_expected.to have_field(:name).of_type(String) }
  end
end

##### with_alias

Use ‘with_alias` to assert that a field is aliased with a certain name.

class User
  include NoBrainer::Document

  field :name, store_as: :n
end

# RSpec
RSpec.describe User, type: :model do
  it do
    it { is_expected.to have_field(:name).with_alias(:s) }
  end
end

##### with_default_value_of

Use ‘with_default_value_of` to assert that a field have a certain default value.

class User
  include NoBrainer::Document

  field :name, default: ''
end

# RSpec
RSpec.describe User, type: :model do
  it do
    it { is_expected.to have_field(:name).with_default_value_of('') }
  end
end

##### required

Use ‘required` to assert that a field is mandatory.

class User
  include NoBrainer::Document

  field :name, require: true
end

# RSpec
RSpec.describe User, type: :model do
  it do
    it { is_expected.to have_field(:name).required }
  end
end

##### to_allow

Use ‘to_allow` to assert that a field has a certain list of available values.

class User
  include NoBrainer::Document

  field :status, type: Enum, in: %i[pending ongoing done]
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:status).to_allow(%i[pending ongoing
                                                   done])
  end
end

##### readonly

Use ‘readonly` to assert that a field cannot be updated.

class User
  include NoBrainer::Document

  field :status, readonly: true
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:status).readonly
  end
end

##### lazy_fetched

Use ‘lazy_fetched` to assert that a field should be fetched on demand.

class User
  include NoBrainer::Document

  field :avatar, type: Binary, lazy_fetch: true
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:avatar).lazy_fetched
  end
end

##### unique

Use ‘unique` to assert that a field should be fetched on demand.

class User
  include NoBrainer::Document

  field :uuid, type: String, unique: true
  # or
  field :uuid, type: String, uniq: true
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:uuid).unique
  end
end

##### with_primary_key

Use ‘with_primary_key` to assert that a field is used as the primary key.

class User
  include NoBrainer::Document

  field :uuid, type: String, primary_key: true
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:uuid).with_primary_key
  end
end

##### of_length

Use ‘of_length` to assert that a field has a length included in the given range.

class User
  include NoBrainer::Document

  field :password, type: String, length: (8..26)
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:password).of_length((8..26))
  end
end

##### with_min_length

Use ‘with_min_length` to assert that a field has a length of at least the given one.

class User
  include NoBrainer::Document

  field :username, type: String, min_length: 3
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:username).with_min_length(3)
  end
end

##### with_max_length

Use ‘with_max_length` to assert that a field has a length of at most the given one.

class User
  include NoBrainer::Document

  field :bio, type: Text, max_length: 250
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:bio).with_max_length(250)
  end
end

##### with_format

Use ‘with_format` to assert that a field is validating the given format.

class User
  include NoBrainer::Document

  field :email, format: /@/
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:email).with_format(/@/)
  end
end

##### indexed

Use ‘indexed` to assert that a field is indexed.

class User
  include NoBrainer::Document

  field :email, index: true
end

# RSpec
RSpec.describe User, type: :model do
  it do
    is_expected.to have_field(:email).indexed
  end
end

Instance Method Summary collapse

Constructor Details

#initialize(*attrs) ⇒ HaveField

:nodoc:



271
272
273
# File 'lib/matchers/have_field.rb', line 271

def initialize(*attrs)
  @attributes = attrs.collect(&:to_sym)
end

Instance Method Details

#descriptionObject



469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# File 'lib/matchers/have_field.rb', line 469

def description
  desc = "have #{@attributes.size > 1 ? 'fields' : 'field'} named #{@attributes.collect(&:inspect).to_sentence}"
  desc += " of type #{@type.inspect}" if @type
  desc += " with alias #{@field_alias}" if @field_alias
  desc += " with default value of #{@default.inspect}" unless @default.nil?
  desc += ' to be required' if @required
  desc += ' allowing all values mentioned' if @allowed_values
  desc += ' to be readonly' if @readonly
  desc += ' to be lazy fetched' if @lazy_fetched
  if @unique == true
    desc += ' to be unique'
  elsif @unique&.key?(:scope)
    desc += " to be unique in the scope of #{@unique[:scope]}"
  end
  desc += ' as primary key' if @primary_key
  desc += " with a length of #{@length}" if @length
  desc += " with a minimal length of #{@min_length}" if @min_length
  desc += " with a maximal length of #{@max_length}" if @max_length
  desc += " in the format #{@format}" if @format
  desc += ' to be indexed' if @index
  desc
end

#failure_message_for_shouldObject Also known as: failure_message



458
459
460
# File 'lib/matchers/have_field.rb', line 458

def failure_message_for_should
  "Expected #{@klass.inspect} to #{description}, got #{@errors.to_sentence}"
end

#failure_message_for_should_notObject Also known as: failure_message_when_negated



462
463
464
# File 'lib/matchers/have_field.rb', line 462

def failure_message_for_should_not
  "Expected #{@klass.inspect} to not #{description}, got #{@klass.inspect} to #{description}"
end

#indexed(index = true) ⇒ Object



345
346
347
348
# File 'lib/matchers/have_field.rb', line 345

def indexed(index = true)
  @index = index
  self
end

#lazy_fetched(lazy_fetched = true) ⇒ Object



310
311
312
313
# File 'lib/matchers/have_field.rb', line 310

def lazy_fetched(lazy_fetched = true)
  @lazy_fetched = lazy_fetched
  self
end

#localizedObject



275
276
277
278
# File 'lib/matchers/have_field.rb', line 275

def localized
  @localized = true
  self
end

#matches?(klass) ⇒ Boolean

Returns:

  • (Boolean)


350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/matchers/have_field.rb', line 350

def matches?(klass)
  @klass = klass.is_a?(Class) ? klass : klass.class
  @errors = []
  @attributes.each do |attr|
    if @klass.fields.include?(attr)
      error = ''

      # Checking field type
      if @type && (@klass.fields[attr][:type] != @type)
        error += " of type #{@klass.fields[attr][:type]}"
      end

      # Checking field default value
      unless @default.nil?
        if @klass.fields[attr][:default].nil?
          error += ' with default not set'
        elsif @klass.fields[attr][:default] != @default
          error += " with default value of #{@klass.fields[attr][:default]}"
        end
      end

      # Checking field store_as
      if @field_alias && (@klass.fields[attr][:store_as] != @field_alias)
        error += " with alias #{@klass.fields[attr][:store_as]}"
      end

      # Checking field required
      if @required && (@klass.fields[attr][:required] != true)
        error += ' being required'
      end

      # Checking allowed values
      if @allowed_values && (@klass.fields[attr][:in] != @allowed_values)
        error += ' allowing all values mentioned'
      end

      # Checking readonly mode
      if @readonly && (@klass.fields[attr][:readonly] != @readonly)
        error += ' in readonly'
      end

      # Checking lazy fetche
      if @lazy_fetched && (@klass.fields[attr][:lazy_fetch] != @lazy_fetched)
        error += ' lazy fetched'
      end

      # Checking unique
      if @unique
        uniq_key = @klass.fields[attr][:unique] ||
                   @klass.fields[attr][:uniq]

        if @unique == true && uniq_key.nil?
          error += ' unique'
        end

        if @unique.is_a?(Hash) && @unique.key?(:scope) &&
           @unique[:scope] != uniq_key[:scope]
          error += " unique scoped to #{uniq_key[:scope]}"
        end
      end

      # Checking primary key
      if @primary_key && (@klass.fields[attr][:primary_key] != @primary_key)
        error += ' as primary key'
      end

      # Checking length range
      if @length && (@klass.fields[attr][:length] != @length)
        error += " with a length of #{@klass.fields[attr][:length]}"
      end

      # Checking min length
      if @min_length && (@klass.fields[attr][:min_length] != @min_length)
        error += ' with a minimal length of ' \
                 "#{@klass.fields[attr][:min_length]}"
      end

      # Checking max length
      if @max_length && (@klass.fields[attr][:max_length] != @max_length)
        error += ' with a maximal length of ' \
                 "#{@klass.fields[attr][:max_length]}"
      end

      # Checking format
      if @format && (@klass.fields[attr][:format] != @format)
        error += " in the format #{@klass.fields[attr][:format]}"
      end

      # Checking index
      if @index && (@klass.fields[attr][:index] != @index)
        error += " index #{@klass.fields[attr][:index].inspect}"
      end

      @errors.push("field #{attr.inspect}" + error) unless error.blank?

      if @localized
        unless @klass.fields[attr].localized?
          @errors.push "is not localized #{attr.inspect}"
        end
      end

    else
      @errors.push "no field named #{attr.inspect}"
    end
  end
  @errors.empty?
end

#of_length(length) ⇒ Object



325
326
327
328
# File 'lib/matchers/have_field.rb', line 325

def of_length(length)
  @length = length
  self
end

#of_type(type) ⇒ Object



280
281
282
283
# File 'lib/matchers/have_field.rb', line 280

def of_type(type)
  @type = type
  self
end

#readonly(readonly = true) ⇒ Object



305
306
307
308
# File 'lib/matchers/have_field.rb', line 305

def readonly(readonly = true)
  @readonly = readonly
  self
end

#required(required = true) ⇒ Object



295
296
297
298
# File 'lib/matchers/have_field.rb', line 295

def required(required = true)
  @required = required
  self
end

#to_allow(allowed_values) ⇒ Object



300
301
302
303
# File 'lib/matchers/have_field.rb', line 300

def to_allow(allowed_values)
  @allowed_values = allowed_values
  self
end

#unique(unique = true) ⇒ Object



315
316
317
318
# File 'lib/matchers/have_field.rb', line 315

def unique(unique = true)
  @unique = unique
  self
end

#with_alias(field_alias) ⇒ Object



285
286
287
288
# File 'lib/matchers/have_field.rb', line 285

def with_alias(field_alias)
  @field_alias = field_alias
  self
end

#with_default_value_of(default) ⇒ Object



290
291
292
293
# File 'lib/matchers/have_field.rb', line 290

def with_default_value_of(default)
  @default = default
  self
end

#with_format(format) ⇒ Object



340
341
342
343
# File 'lib/matchers/have_field.rb', line 340

def with_format(format)
  @format = format
  self
end

#with_max_length(max_length) ⇒ Object



335
336
337
338
# File 'lib/matchers/have_field.rb', line 335

def with_max_length(max_length)
  @max_length = max_length
  self
end

#with_min_length(min_length) ⇒ Object



330
331
332
333
# File 'lib/matchers/have_field.rb', line 330

def with_min_length(min_length)
  @min_length = min_length
  self
end

#with_primary_key(primary_key = true) ⇒ Object



320
321
322
323
# File 'lib/matchers/have_field.rb', line 320

def with_primary_key(primary_key = true)
  @primary_key = primary_key
  self
end