Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

zx-monads

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

zx-monads

  • 0.0.7
  • Rubygems
  • Socket score

Version published
Maintainers
1
Created
Source

🔃 Zx::Monads

FP Monads for Ruby

Gem Build Status

Motivation

Because in sometimes, we need to handling a safe value for our objects. This gem simplify this work.

Documentation

VersionDocumentation
unreleasedhttps://github.com/thadeu/zx-monads/blob/main/README.md

Table of Contents

Compatibility

kindbranchruby
unreleasedmain>= 2.7.6, <= 3.2.x

Installation

Use bundle

bundle add zx-monads

or add this line to your application's Gemfile.

gem 'zx-monads'

and then, require module

require 'zx'

Configuration

Without configuration, because we use only Ruby. ❤️

Usage

How to use in my codebase?

class Order
  include Zx # include all Zx library.
end

class Order
  include Zx::Maybeable
end

class ProcessOrder < Zx::Steps
  # include Zx::Maybeable included now!
end

Available public methods

#type -> Returns maybe type
#some? -> Returns boolean 
#none? -> Returns boolean 
#unwrap -> Returns value unwrapped
#or(value) -> Returns unwrap or value
#>>(other) -> Forward to another Maybe
#fmap -> Create an step and wrap new value
#map(:key) -> Same the fmap, but receive another parameters
#map(&:method) -> Same the map, but respond to method
#map {} -> Same the the map, but receive an block
#map!{} -> Same the map, but change to new value
#apply!{} -> Same the map! but more legible
#dig(keys) -> Get values using keys like Hash#dig
#dig!(keys) -> Same them dig, but return unwrap
#math(some:, none:) -> Receive callables and associate them
#on_success{} -> Only when Some
#on_failure{} -> Only then None
#on(:success|:failure){}

ZX::Maybe

result = Zx::Maybe[1] # or Maybe.of(1)
result = Zx::Maybe[nil] # or Maybe.of(nil)
result = Zx::Maybe[1].map{ _1 + 2}
# -> Some(3)
result = Zx::Maybe[nil].map{ _1 + 2}
# -> None
result = Zx::Maybe.of(1).or(2)
result.or(2) # 1
result = Zx::Maybe.of(' ').or(2)
result.or(2) # 2
result = Zx::Maybe.of(' ').or(2)
result.or(2) # 2
order = {
  shopping: {
    banana: {
      price: 10.0
    }
  }
}

price =  Zx::Maybe[order]
  .map { _1[:shopping] }
  .map { _1[:banana] }
  .map { _1[:price] }

# -> Some(10.0)

# or using #dig

price =  Zx::Maybe[order].dig(:shopping, :banana, :price)
# -> Some(10.0)

price_none =  Zx::Maybe[order].dig(:shopping, :banana, :price_non_exists)
# -> None

price_or =  Zx::Maybe[order].dig(:shopping, :banana, :price_non_exists).or(10.0)
# -> Some(10.0)
class Response
  attr_reader :body

  def initialize(new_body)
    @body = Zx::Maybe[new_body]
  end

  def change(new_body)
    @body = Zx::Maybe[new_body]
  end
end

response = Response.new(nil)
expect(response.body).to be_none

response.change({ status: 200 })
expect(response.body).to be_some

response_status = response.body.match(
  some: ->(body) { Zx::Maybe[body].map { _1.fetch(:status) }.unwrap },
  none: -> {}
)

Use case, when use to parse response stringify json

dump = JSON.dump({ status: { code: '300' } })

response = Response.new(dump) # It's receive an JSON stringified

module StatusCodeUnwrapModule
  def self.call(body)
    Zx::Maybe[body]
      .map{ JSON(_1, symbolize_names: true) }
      .dig(:status, :code)
      .apply(&:to_i)
      .unwrap
  end
end

response_status = response.body.match(
  some: StatusCodeUnwrapModule,
  none: -> { 400 }
)
  
expect(response_status).to eq(300)

You can use >> to compose many callables, like this.

sum = ->(x) { Zx::Maybe::Some[x + 1] }

subtract = ->(x) { Zx::Maybe::Some[x - 1] }

result = Zx::Maybe[1] >> \
  sum >> \
  subtract

expect(result.unwrap).to eq(1)

If handle None, no worries.

sum = ->(x) { Zx::Maybe::Some[x + 1] }

subtract = ->(_) { Zx::Maybe::None.new }

result = Zx::Maybe[1] \
  >> sum \
  >> subtract

expect(result.unwrap).to be_nil
class Order
  def self.sum(x)
    Zx::Maybe[{ number: x + 1 }]
  end
end

result = Order.sum(1)
  .dig(:number)
  .apply(&:to_i)

expect(result.unwrap).to be(2)

Zx::Maybe::Some

class Order
  include Zx::Maybeable

  def self.sum(x)
    new.sum(x)
  end

  def sum(x)
    Some[{ number: x + 1 }]
  end
end

result = Order.sum(1)
  .dig(:number)
  .apply(&:to_i)

expect(result.unwrap).to be(2)

Zx::Maybe::None

class Order
  include Zx::Maybeable

  def self.sum(x)
    new.sum(x)
  end

  def sum(x)
    Try {{ number: x + ' ' }}
  end
end

result = Order.sum(1)
number = result.dig(:number).apply(&:to_i)

expect(result).to be_none
expect(result).to be_a(Maybe::None)
expect(number.unwrap).to be(0) # nil.to_i == 0

Zx::Maybe::Try

Only included or inherited!

class Order
  include Zx::Maybeable

  def self.sum(x)
    new.sum(x)
  end

  def sum(x)
    Try {{ number: x + 1 }}
  end
end

result = Order.sum(1)
  .dig(:number)
  .apply(&:to_i)

expect(result.unwrap).to be(2)

With default value, in None case.

class Order
  include Zx::Maybeable

  def self.sum(x)
    new.sum(x)
  end

  def sum(x)
    Try(2) {{ number: x + ' ' }}
  end
end

result = Order.sum(1)
  .dig(:number)
  .apply(&:to_i)

expect(result.unwrap).to be(2)
class Order
  include Zx::Maybeable

  def self.sum(x)
    new.sum(x)
  end

  def sum(x)
    Try(or: 1000) {{ number: x + ' ' }}
  end
end

result = Order.sum(1).dig(:number).apply(&:to_i)

expect(result.unwrap).to be(1000)

Zx::Steps

class OrderStep < Zx::Steps
  def initialize(x = nil)
    @x = x
  end

  step :positive?
  step :apply_tax
  step :divide

  def positive?
    return None unless total.is_a?(Integer) || total.is_a?(Float)
    return None if total <= 0

    Some total
  end

  def apply_tax
    Try { @x -= (@x * 0.1) }
  end

  def divide
    Try { @x /= 2 }
  end

  def total
    @x
  end
end
order = OrderStep.new(20)

order.call
  .map { |n| n + 1 }
  .on_success { |some| expect(some.unwrap).to eq(10) }
  .on_failure { |none| expect(none.or(0)).to eq(0) }
order = OrderStep.new(20)

order.call
  .map { |n| n + 1 }
  .on(:success) { |some| expect(some.unwrap).to eq(10) }
  .on(:failure) { |none| expect(none.or(0)).to eq(0) }
order = OrderStep.new(-1)

order.call
  .on_success { raise }
  .on_failure { |none| expect(none.or(0)).to eq(0) }

⬆️  Back to Top

Development

After checking out the repo, run bin/setup to install dependencies. Then, run bundle exec rspec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/thadeu/zx-monads. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

FAQs

Package last updated on 05 Oct 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc