Skip to content

temporarily enabling or disabling boolean attributes on classes and instances

License

Notifications You must be signed in to change notification settings

javierav/with_attributes

Repository files navigation

WithAttributes

Testing

Temporarily enabling or disabling boolean attributes on classes and instances using with and without dynamic methods.

Installation

Add this line to your application's Gemfile:

gem "with_attributes"

And then execute:

bundle install

Usage

class User
  extend WithAttributes

  with_attribute :notifications, default: false
end

This add the following methods:

  • User.with_notifications for enable notifications in class-level during block execution.

  • User.without_notifications for disable notifications in class-level during block execution.

  • User.enable_notificationsfor enable notifications in class-level permanently.

  • User.disable_notificationsfor disable notifications in class-level permanently.

  • User.notifications?for check current status of notifications in class-level, using default value if not inside with_notifications or without_notifications modifiers.

  • user.with_notifications for enable notifications in instance-level during block execution.

  • user.without_notifications for disable notifications in instance-level during block execution.

  • user.enable_notificationsfor enable notifications in instance-level permanently.

  • user.disable_notificationsfor disable notifications in instance-level permanently.

  • user.notifications?for check current status of notifications in instance-level, using class-level value if not inside with_notifications or without_notifications modifiers.

Example using class-level methods:

# using default value
User.notifications? # => false

User.with_notifications do
  User.notifications? # => true

  User.without_notifications do
    User.notifications? # => false
  end

  User.notifications? # => true
end

User.notifications? # => false

Example using instance level methods:

user = User.new

# if notifications is not changed in this instance, using value of class or default if class value is also not changed
user.notifications? # => false

# changing value in class-level
User.with_notifications do
  user.notifications? # => true
end

# changing value in instance-level
user.with_notifications do
  user.notifications? # => true
end

# using nested
User.with_notifications do
  user.without_notifications do
    user.notifications? # => false
  end
end

Real world example

module Trackable
  extend ActiveSupport::Concern

  class_methods do
    include WithAttributes
  end

  included do
    with_attribute :tracking, default: true

    with_options if: :tracking? do
      after_create  { ... }
      after_update  { ... }
      after_destroy { ... }
    end
  end
end

class User
  include Trackable
end

User.without_tracking do
  User.create(...)
end

User.find(...).tap do |user|
  user.without_tracking do
    user.update(...)
  end
end

Thread safety

Thread safety is guaranteed using class-level methods. Instance-level methods only are thread safe if each thread not share the same instance with others.

# thread safe example

t1 = Thread.new do
  User.without_notifications do
    User.create(...) # user created without notifications
  end
end

t2 = Thread.new do
  User.with_notifications do
    User.create(...) # user created with notifications
  end
end

[t1, t2].map(&:join)
# thread safe example

t1 = Thread.new do
  user = User.find(...)

  user.without_notifications do
    user.update(...) # user updated without notifications
  end
end

t2 = Thread.new do
  user = User.find(...)

  user.with_notifications do
    user.update(...) # user updated with notifications
  end
end

[t1, t2].map(&:join)
# thread unsafe example

user = User.new

t1 = Thread.new do
  user.without_notifications do
    user.update(...) # unexpected behaviour, can be created with or without notifications
  end
end

t2 = Thread.new do
  user.with_notifications do
    user.update(...) # unexpected behaviour, can be created with or without notifications
  end
end

[t1, t2].map(&:join)

Licence

Copyright © 2024 Javier Aranda. Released under the terms of the MIT license.

About

temporarily enabling or disabling boolean attributes on classes and instances

Topics

Resources

License

Stars

Watchers

Forks