lib/lazier.rb
#
# This file is part of the lazier gem. Copyright (C) 2013 and above Shogun <shogun@cowtech.it>.
# Licensed under the MIT license, which can be found at https://choosealicense.com/licenses/mit.
#
# PI: Ignore flog on this file.
require(RUBY_ENGINE != "jruby" ? "oj" : "json")
require "English"
require "tzinfo"
require "active_support"
require "active_support/core_ext"
require "i18n"
require "i18n/backend/fallbacks"
require "hashie"
require "pathname"
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
require "lazier/version" unless defined?(Lazier::Version)
require "lazier/exceptions"
require "lazier/i18n"
require "lazier/configuration"
require "lazier/settings"
require "lazier/object"
require "lazier/boolean"
require "lazier/string"
require "lazier/hash"
require "lazier/datetime"
require "lazier/timezone"
require "lazier/math"
require "lazier/pathname"
# Several Ruby object enhancements.
module Lazier
# The root directory of the library
ROOT = File.absolute_path(__dir__ + "/../")
# Returns the settings for the extensions.
#
# @return [Settings] The settings for the extensions.
def self.settings
::Lazier::Settings.instance
end
# Loads the extensions.
#
# @param what [Array] The modules to load. Valid values are:
# @option object Extensions for all objects.
# @option boolean Extensions for boolean values.
# @option string Extensions for strings.
# @option hash Extensions for hashs.
# @option hash_method_access Extensions for hash to allow method access. Not included by default.
# @option datetime Extensions date and time objects.
# @option math Extensions for Math module.
# @option pathname Extensions for path objects.
# @return [Settings] The settings for the extensions.
def self.load!(*what)
valid_modules = [:object, :boolean, :string, :hash, :datetime, :math, :pathname]
modules = what.present? ? what.flatten.uniq.compact.map(&:to_sym) : valid_modules
(modules & valid_modules).each { |w| ::Lazier.send("load_#{w}") }
yield if block_given?
::Lazier::Settings.instance
end
# Loads `Object` extensions.
def self.load_object
Lazier.load_boolean
perform_load(:object, ::Object, ::Lazier::Object)
end
# Loads `Boolean` extensions.
def self.load_boolean
perform_load(:boolean) do
[::TrueClass, ::FalseClass].each do |klass|
klass.class_eval do
include ::Lazier::Object
include ::Lazier::Boolean
end
end
end
end
# Loads `String` extensions.
def self.load_string
perform_load(:string, ::String, ::Lazier::String)
end
# Loads `Hash` extensions.
def self.load_hash
Lazier.load_object
perform_load(:hash) do
clean_hash_compact
::Hash.class_eval { include ::Lazier::Hash }
end
end
# Loads `DateTime` extensions.
def self.load_datetime
Lazier.load_object
perform_load(:datetime) do
[::Time, ::Date, ::DateTime].each do |c|
c.class_eval { include ::Lazier::DateTime }
end
::ActiveSupport::TimeZone.class_eval { include ::Lazier::TimeZone }
end
end
# Loads `Math` extensions.
def self.load_math
Lazier.load_object
perform_load(:math, ::Math, ::Lazier::Math)
end
# Loads `Pathname` extensions.
def self.load_pathname
perform_load(:pathname, ::Pathname, ::Lazier::Pathname)
end
# Returns the list of loaded Lazier modules.
#
# @return [Array] The list of loaded Lazier modules.
def self.loaded_modules
@loaded || []
end
# Checks if *all* of the specified modules have been loaded.
#
# @return [Boolean] `true` if the *all* of the specified modules has already been loaded, `false` otherwise.
def self.modules_loaded?(*modules)
(modules.flatten.uniq.compact.map(&:to_sym) - @loaded).blank?
end
# Finds a class to instantiate.
#
# @param cls [Symbol|String|Object] If a `String` or a `Symbol` or a `Class`, then it will be the class to instantiate.
# Otherwise the class of the object will returned.
# @param scope [String] The scope where to find the class. `%CLASS%`, `%`, `$`, `?` and `@` will be substituted with the class name.
# @param only_in_scope [Boolean] If only search inside the scope.
# @return [Class] The found class.
def self.find_class(cls, scope = "::@", only_in_scope = false)
if [::String, ::Symbol].include?(cls.class)
cls = cls.to_s.camelize
cls.gsub!(/^::/, "") if scope && only_in_scope
search_class(cls, scope) || raise(NameError, ["", cls])
else
cls.is_a?(::Class) ? cls : cls.class
end
end
# Measure the time in milliseconds required to execute the given block.
#
# @param message [String|NilClass] An optional message (see return value).
# @param precision [Fixnum] The precision for the message (see return value).
# @param block [Proc] The block to evaluate.
# @return [Float|String] If a `message` is provided, then the message itself plus the duration under parenthesis will be returned,
# otherwise the duration alone as a number.
def self.benchmark(message: nil, precision: 0, &block)
rv = Benchmark.ms(&block)
message ? format("%s (%0.#{precision}f ms)", message, rv) : rv
end
# Returns which platform are we running on. Can be `:java`, `:osx`, `:posix` or `:win32`
#
# @return [Boolean] If force detection again.
# @return [Symbol] The current platform.
def self.platform(force = false)
@platform = nil if force
@platform ||=
case RUBY_PLATFORM
when /cygwin|mingw|win32/ then :win32
when /java/ then :java
when /darwin/ then :osx
else :posix
end
end
# :nodoc:
def self.clean_hash_compact
::Hash.class_eval do
remove_method(:compact) if {}.respond_to?(:compact)
remove_method(:compact!) if {}.respond_to?(:compact!)
end
end
# :nodoc:
def self.search_class(cls, scope = nil)
cls = scope.gsub(/%CLASS%|[@%$?]/, cls)
cls.constantize
rescue
nil
end
# :nodoc:
def self.perform_load(mod, target = nil, extension = nil, &block)
@loaded ||= []
unless @loaded.include?(mod)
block_given? ? block.call : target.class_eval { include extension }
@loaded << mod
end
end
end