Skip to content

smart-rb/smart_engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

87 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SmartCore::Engine · Supported by Cado Labs · Gem Version

Generic SmartCore functionality.


Supported by Cado Labs


Installation

gem 'smart_engine'
bundle install
# --- or ---
gem install smart_engine
require 'smart_core'

Technologies


Global set of error types

  • SmartCore::Error (inherited from ::StandardError);
  • SmartCore::ArgumentError (inherited from ::ArgumentError);
  • SmartCore::FrozenError (inherited from ::FrozenError);
  • SmartCore::NameError (inherited from ::NameError);
  • SmartCore::TypeError (inherited from ::TypeError);

Simple reentrant lock

lock = SmartCore::Engine::Lock.new
lock.synchronize { your_code }

Read/Write Lock

  • non-controlable reader count;
  • readers does not lock each other;
  • readers waits for writer;
  • writer waits for readers;
lock = SmartCore::Engine::ReadWriteLock.new

lock.read_sync { ...some-read-op... } # waits for writer
lock.read_sync { ...some-read-op... } # waits for writer
lock.write_sync { ... some-write-op... } # waits for all readers and current writer

# is write_sync lock is owned by current thread?
lock.write_owned? # true or false

Cache Storage

  • you can use any object as a cache key;

  • you can store any object as a cache value;

  • you can cache nil object too;

  • cache read has fetch semantics:

    • signature: #read(key, &fallback);
    • in the event of cache miss the &fallback black will be invoked;
    • the return value of the fallback block will be written to the cache, and that return value will be returned;
  • cache write:

    • signature: #write(key, value);
    • you can use any object as a cache key;
    • you can store any object as a value;
    • you can write nil object too;
  • cache clear:

    • signature: #clear;
cache = SmartCore::Engine::Cache.new

# write and read
cache.write(:amount, 123.456) # => 123.456
cache.read(:amount) # => 123.456

# read non-existing with a fallback
cache.read('name') # => nil
cache.read('name') { 'D@iVeR' } # => 'D@iVeR'
cache.read('name') # => 'D@iVeR'

# store nil object
cache.write(:nil_value, nil) # => nil
cache.read(:nil_value) # => nil
cache.read(:nil_value) { 'rewritten' } # => nil
cache.read(:nil_value) # => nil

# clear cache
cache.clear # => nil
# aliases:

# write:
cache[:key1] = 'test'

# read:
cache[:key1] # => 'test'

# read with fallback:
cache[:key2] { 'test2' } # => 'test2'
cache[:key2] # => 'test2'

Atomic thread-safe value container

atom = SmartCore::Engine::Atom.new # initial value - nil
atom.value # => nil
# --- or ---
atom = SmartCore::Engine::Atom.new(7) # initial value - 7
atom.value # => 7

# set new value (thread-safely)
atom.swap { |original_value| original_value * 2 }
atom.value # => 14

Any Object Frozener

  • works with any type of ruby objects (event with BasicObject);
  • uses classic Ruby C-level frozen?/freeze functionality;
# as a singleton

object = BasicObject.new
SmartCore::Engine::Frozener.frozen?(object) # => false

SmartCore::Engine::Frozener.freeze(object)
SmartCore::Engine::Frozener.frozen?(object) # => true
# as a mixin

class EmptyObject < BasicObject
  include SmartCore::Engine::Frozener::Mixin
end

object = EmptyObject.new

object.frozen? # => false
object.freeze
object.frozen? # => true

Basic Object Refinements

Ruby's BasicObject class does not have some fundamental (extremely important for instrumenting) methods:

  • is_a? / kind_of?
  • instance_of?
  • freeze / frozen?
  • hash
  • nil?
  • inspect

SmartCore::Ext::BasicObjectAsObject refinement solves this problem (by Ruby's internal API without any manualy-emulated behavior).

# without refinement:
basic_obj = ::BasicObject.new

basic_obj.is_a?(::BasicObject) # raises ::NoMethodError
basic_obj.kind_of?(::BasicObject) # raises ::NoMethodError
basic_obj.instance_of?(::BasicObject) # rasies ::NoMethodError
basic_obj.freeze # raises ::NoMethodError
basic_obj.frozen? # raises ::NoMethodError
basic_object.hash # raises ::NoMethodError
basic_object.nil? # raises ::NoMethodError
basic_object.inspect # raises ::NoMethodError
# with refinement:
using SmartCore::Ext::BasicObjectAsObject

basic_obj = ::BasicObject.new

basic_obj.is_a?(::BasicObject) # => true
basic_obj.kind_of?(::BasicObject) # => true
basic_obj.instance_of?(::BasicObject) # => true
basic_obj.instance_of?(::Object) # => false
basic_obj.is_a?(::Integer) # => false
basic_obj.kind_of?(::Integer) # => false

basic_obj.frozen? # => false
basic_obj.freeze # => self
basic_obj.frozen? # => true

basic_obj.nil? # => false

basic_obj.hash # => 2682859680348634421 (some Integer value)

basic_obj.inspect # => "#<BasicObject:0x00007fe428018628>"

Inline rescue pipe

  • works with an array of proc objects;
  • returns the result of the first non-failed proc;
  • provides an error interception interface (a block argument);
  • fails with the last failed proc exception (if all procs were failed and interceptor was not passed);

Return the result of the first non-failed proc

SmartCore::Engine::RescueExt.inline_rescue_pipe(
  -> { raise },
  -> { raise },
  -> { 123 },
  -> { 567 },
  -> { raise },
)
# => output: 123

Fail with the last failed proc exception

SmartCore::Engine::RescueExt.inline_rescue_pipe(
  -> { raise(::ArgumentError) },
  -> { raise(::TypeError) },
  -> { raise(::ZeroDivisionError) }
)
# => fails with ZeroDivisionError

Error interception

SmartCore::Engine::RescueExt.inline_rescue_pipe(
  -> { raise(::ArgumentError) },
  -> { raise(::TypeError) },
  -> { raise(::ZeroDivisionError, 'Intercepted exception') }
) do |error|
  error.message
end
# => output: "Intercepted exception"

Roadmap

  • migrate to Github Actions in CI;
  • thread-safety for BasicObject extensions;
  • SmartCore::Engine::Cache:
    • thread-safety;
    • support for ttl: option for #write and for fallback block attribute of #read;
    • support for key-value-pair iteration;
    • support for #keys method;
    • support for #key? method;
    • think about some layer of cache object serialization;
  • SmartCore::Engine::ReadWriteLock:
    • an ability to set a maximum count of readers;

Contributing

  • Fork it ( https://github.com/smart-rb/smart_engine )
  • Create your feature branch (git checkout -b feature/my-new-feature)
  • Commit your changes (git commit -am '[feature_context] Add some feature')
  • Push to the branch (git push origin feature/my-new-feature)
  • Create new Pull Request

License

Released under MIT License.

Supporting

Supported by Cado Labs

Authors

Rustam Ibragimov

About

Generic SmartCore functionality.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •