Skip to content

πŸ”°πŸ±β“ Finally attr_accessor & attr_reader with question marks for booleans!?

License

Notifications You must be signed in to change notification settings

esotericpig/attr_bool

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

60 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

AttrBool

Gem Version

Source Code Changelog License

Easily create attr (attribute) methods that end with question marks (?).

require 'attr_bool'

module Wearable
  extend AttrBool::Ext

  attr_accessor? :in_fashion
  attr_reader?   :can_wash
end

class BananaHammock
  extend AttrBool::Ext
  include Wearable

  # Enforce boolean (true or false) values.
  attr_bool  :princess
  attr_bool? :crap_bag
end

banham = BananaHammock.new()

banham.in_fashion = true
banham.princess   = true

p banham.in_fashion?  #=> true
p banham.can_wash?    #=> nil
p banham.princess?    #=> true
p banham.crap_bag?    #=> false

Require attr_bool/core_ext to extend the core (monkey-patch) Module & Class (not recommended for libraries):

require 'attr_bool/core_ext'

class BananaHammock
  attr_bool  :princess
  attr_bool? :crap_bag
end

Contents

// Similar Projects

Create an issue to add your project.

Gem Name Code Example
attr_asker [GitHub] attr_asker :running
attr_boolean [GitHub] attr_boolean :running, default: true
attr_setting [GitHub] attr_setting :running, true
attribool [GitHub] bool_reader :name
attribute_boolean [GitHub] attr_boolean :running
boolean_accessor [GitHub] battr_accessor :running
named_accessors [GitHub] named_reader :running, as: :running?
property-accessor [GitHub] property(:running) { get(:running?); default { true } }
question_mark_methods [GitHub] add_question_mark_methods running?: :running
wannabe_bool [GitHub] attr_wannabe_bool :running
wardrobe [GitHub] attribute :running, Wardrobe::Boolean, default: true

Searches:

// Setup

Add attr_bool to your Gemspec or Gemfile.

Or, use the RubyGems package manager:

$ gem install attr_bool

Or, manually:

$ git clone 'https://github.com/esotericpig/attr_bool.git'
$ cd attr_bool
$ bundle install
$ bundle exec rake install:local

// Usage

Either require attr_bool or attr_bool/core_ext.

The first one requires extending AttrBool::Ext manually.

require 'attr_bool'

class Game
  extend AttrBool::Ext

  attr_accessor? :running
  attr_reader?   :winning
end

The second one automatically extends Module & Class, which is not recommended for sharing libraries.

require 'attr_bool/core_ext'

class Game
  attr_accessor? :running
  attr_reader?   :winning
end

Now, simply use attr_accessor? and/or attr_reader? with one or more Symbols and/or Strings.

These do not force the values to be booleans (true or false).

For most purposes, this is adequate.

require 'attr_bool'

class Game
  extend AttrBool::Ext

  attr_accessor? :running,'looper'
  attr_reader?   :fps,'music'

  def initialize()
    @running = false
    @looper  = nil
    @fps     = 60
    @music   = 'Beatles'
  end
end

game = Game.new()

puts game.running?  #=> false
puts game.looper?   #=> nil
puts game.fps?      #=> 60
puts game.music?    #=> 'Beatles'

game.running = true
game.looper  = :main

puts game.running?  #=> true
puts game.looper?   #=> :main

There is also attr_writer?, but it simply calls the standard attr_writer unless you pass in a block.

To enforce boolean (true or false) values, use...

Name Access
attr_bool or attr_boolor accessor
attr_bool? reader
attr_booler writer

These are slightly slower due to always checking the values.

require 'attr_bool'

class Game
  extend AttrBool::Ext

  attr_bool   :running,'looper'
  attr_bool?  :fps,'music'
  attr_booler :sound

  def initialize()
    @fps   = 60
    @music = 'Beatles'
    @sound = false
  end

  def loud?()
    music? && @sound == true
  end
end

game = Game.new()

puts game.running?  #=> false
puts game.looper?   #=> false
puts game.fps?      #=> true
puts game.music?    #=> true
puts game.loud?     #=> false

game.running = true
game.looper  = :main
game.sound   = 'loud!'

puts game.running?  #=> true
puts game.looper?   #=> true
puts game.loud?     #=> true

/// Default Values

A default value can be passed in, but I don't recommend using it because it's slightly slower due to always checking the value and not setting the instance variable directly.

It's best to just set the default values the standard way in initialize(). However, many Gems do this, so I also added this functionality anyway.

If the last argument is not a Symbol or a String, then it will be used as the default value.

Note: attr_writer? & attr_booler can not take in a default value.

require 'attr_bool'

class Game
  extend AttrBool::Ext

  attr_accessor? :running,:looper,false
  attr_reader?   :min_fps,:max_fps,60

  attr_bool  :gravity,:wind,true
  attr_bool? :min_force,:max_force,110
end

game = Game.new()

puts game.running?    #=> false
puts game.looper?     #=> false
puts game.min_fps?    #=> 60
puts game.max_fps?    #=> 60
puts game.gravity?    #=> true
puts game.wind?       #=> true
puts game.min_force?  #=> true (not 110)
puts game.max_force?  #=> true (not 110)

Instead of the last argument, you can use the default: keyword argument. In addition to being more clear, this allows you to pass in a String or a Symbol.

require 'attr_bool'

class Game
  extend AttrBool::Ext

  attr_accessor? :running,:looper,default: :main
  attr_reader?   :music,:sound,default: 'quiet!'
end

game = Game.new()

puts game.running?  #=> :main
puts game.looper?   #=> :main
puts game.music?    #=> 'quiet!'
puts game.sound?    #=> 'quiet!'

/// Block/Proc/Lambda

A block can be passed in for dynamic values, but I don't recommend using it. However, many Gems do this, so I also added this functionality anyway.

With blocks, you can quickly write a dynamic attribute that depends on other variable(s) or tests variable(s) in some other special way.

Note: blocks do not update the instance variables; you must do this manually within the block. attr_accessor?/reader?/writer? & attr_bool* with blocks are exactly the same code (i.e., boolean values are not enforced).

require 'attr_bool'

class Game
  extend AttrBool::Ext

  attr_reader?(:lag)  { print @ping,','; @ping > 300 }
  attr_writer?(:ping) {|value| @ping = value.to_i() }

  # Define 1 block for both reader & writer together.
  attr_accessor?(:sound) do |value=nil|
    if value.nil? # Assume reader
      print @sound,','
      @sound > 0
    else # Assume writer
      @sound = value.to_i() % 100
    end
  end

  attr_bool?(:slow) { print @fps,','; @fps < 30 }
  attr_booler(:fps) {|value| @fps = value.to_i() }

  # Define separate blocks.
  attr_bool(:music,
    reader: -> { print @music,','; !@music.nil? },
    writer: ->(value) { @music = value.to_sym() }
  )

  # Define only 1 block.
  attr_accessor?(:frames,
    reader: -> { @frames.odd? }
  )
end

game = Game.new()

game.ping   = 310.99
game.sound  = 199.99
game.fps    = 29.99
game.music  = 'Beatles'
game.frames = 1

puts game.lag?     #=> 310,true
puts game.sound?   #=> 99,true
puts game.slow?    #=> 29,true
puts game.music?   #=> :Beatles,true
puts game.frames?  #=> true

/// Complete Example

require 'attr_bool/core_ext'

module Wearable
  # +attr_accessor?/reader?+ do not enforce boolean (true or false) values.
  attr_accessor? :in_fashion,:in_season
  attr_reader?   :can_wash,:can_wear,default: 'yes!'
end

class BananaHammock
  include Wearable

  # +attr_bool*+ enforce boolean (true or false) values.
  attr_bool   :princess,:prince,default: 'Consuela'
  attr_bool?  :can_swim,:can_wink,true
  attr_bool? (:crap_bag) { princess? && can_swim? }
  attr_booler :friends

  def for_friends()
    @friends
  end
end

banham = BananaHammock.new()

puts banham.in_fashion?  #=> nil
puts banham.in_season?   #=> nil
puts banham.can_wash?    #=> 'yes!'
puts banham.can_wear?    #=> 'yes!'
puts '---'

puts banham.princess?  #=> true (not 'Consuela')
puts banham.prince?    #=> true (not 'Consuela')
puts banham.can_swim?  #=> true
puts banham.can_wink?  #=> true
puts banham.crap_bag?  #=> true
puts '---'

banham.in_fashion = true
banham.in_season  = 'always'
banham.princess   = nil
banham.prince     = 'Charming'
banham.friends    = 'Valerie'

puts banham.in_fashion?  #=> true
puts banham.in_season?   #=> 'always'
puts banham.princess?    #=> false (not nil)
puts banham.prince?      #=> true  (not 'Charming')
puts banham.crap_bag?    #=> false (dynamic; because +princess?+ is now false)
puts banham.for_friends  #=> true  (not 'Valerie')

/// YARDoc

A custom AttributeHandler plugin is planned for the next version:

For now, please use one of YARDoc's built-in ways:

attr_accessor? :winning # @!attribute [rw] winning=(value),winning?
attr_reader?   :running # @!attribute [r]  running?

# @!attribute [r] can_swim?
#   @return [true,false] can you swim in it?
# @!attribute [r] can_wink?
#   @return [true,false] can you wink at pretty people?
attr_reader? :can_swim,:can_wink

# @!attribute [rw] princess=(value),princess?
#   @param value [true,false] this is Ms. Consuela or not!
#   @return [true,false] is this Ms. Consuela?
# @!attribute [rw] crap_bag=(value),crap_bag?
#   @param value [true,false] this is Mr. Crap Bag or not!
#   @return [true,false] is this Mr. Crap Bag?
attr_accessor? :princess,:crap_bag

# @overload in_fashion?
#   @return [true,false] whether it's fashionable right now
# @overload in_fashion=(value)
#   Make it in or out of fashion!
attr_accessor? :in_fashion

# @!method can_wear?
# @return [true,false] whether it's wearable (default: +true+)
attr_reader? :can_wear,true

# @!group My Attrs
# @!attribute [r] in_season?
attr_reader? :in_season
# @!attribute [r] can_wash?
attr_reader? :can_wash
# @!endgroup

Further reading:

// Hacking

$ git clone 'https://github.com/esotericpig/attr_bool.git'
$ cd attr_bool
$ bundle install
$ bundle exec rake -T

Test

$ bundle exec rake test

Generate Doc

$ bundle exec rake doc

Install Locally

$ bundle exec rake install:local

Release

$ bundle exec rake release

/// Benchmarks

There are some benchmarks that test define_method vs module_eval and ? true : false vs !!.

To run these on your system:

$ bundle exec rake benchmark

// License

MIT

AttrBool (https://github.com/esotericpig/attr_bool)
Copyright (c) 2020-2021 Jonathan Bradley Whited

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.