GithubHelp home page GithubHelp logo

coercible's Introduction

Coercible

Gem Version Build Status Dependency Status Code Climate Test Coverage Inline docs

Installation

Add this line to your application's Gemfile:

gem 'coercible'

And then execute:

$ bundle

Or install it yourself as:

$ gem install coercible

Usage

Coercible gives you access to coercer objects where each object is responsible for coercing only one type into other types. For example a string coercer knows only how to coerce string objects, integer coercer knows only how to coerce integers etc.

Here's the most basic example:

coercer = Coercible::Coercer.new

# coerce a string to a date
coercer[String].to_date('2012/12/25') # => #<Date: 4912573/2,0,2299161>

# coerce a string to a boolean value
coercer[String].to_boolean('yes') # => true

# you got the idea :)

For more control you can configure your coercer like that:

# build coercer instance
coercer = Coercible::Coercer.new do |config|
  config.string.boolean_map = { 'yup' => true, 'nope' => false }
end

# coerce a string to boolean
coercer[String].to_boolean('yup') # => true
coercer[String].to_boolean('nope') # => false

Note that at the moment only Integer and String are configurable. More configurable coercers will be added later whenever we find good usecases.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

coercible's People

Contributors

dawidjanczak avatar dkubb avatar ktdreyer avatar mbj avatar snusnu avatar solnic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

coercible's Issues

please release 1.0.1

Hi,

Could you please release the 1.0.1 version with RSpec3 support as a gem?
Thanks!

Cédric

Array coercion

Hi,
I wanted to give a try to this gem but I can't figure out how I could coerce an entire array, I suppose it is possible since Virtus supports it but I have no idea how and where to start looking :(

Here what I wish to achieve:

input  = ["1", 2, 5]
output = [1, 2, 5]

is it possible without iterating myself through the array ?

#coerced? backwards?

The implementation of #coerced? is not in line with the documentation (and seems counterintuitive).

The docs state:

# @example when coercion was successful
#     coercer[String].coerced?(1) # => true

The actual behavior is:

coercer = Coercible::Coercer.new
coercer[String].coerced?(1) # => false
coercer[String].coerced?("1") # => true

Maybe I get the idea of a coercer backwards, but the docs seem to describe what I would expect:

string_coercer = Coercible::Coercer.new[String]
string_coercer.coerced?(string_coercer.to_boolean('1')) # => true
# in general:
# x_coercer.coerced?(<value not of type X>) # => true
# x_coercer.coerced?(<value of type X>) # => false

Let me know - I can submit a PR either way.

coerce of Date to integer

Hello,

I use Virtus for building models and declare an attribute with type Integer. Virtus has dependency to coercible.
The problem is that sometimes the model attribute receives a Date object and fails in your gem.

I believe it's something like this:

coercer = Coercible::Coercer.new
coercer[Date].to_integer('1')

This raises: ArgumentError: wrong number of arguments (given 3, expected 2)

The fix is to implement #to_integer in TimeCoercions like this monkey patch:

module Coercible
  class Coercer

    module TimeCoercions
      def to_integer(_); end
    end

  end
end

I guess it's not expected situation for the gem, but would be wonderful to see that it supports more use cases and I wouldn't need to monkey patch this.

Please let me know if you are still maintaining the gem so that we could agree about the final solution and make the gem even better.

Error with ruby-2.7.0-preview1

I was experiencing troubles with 'telegram/bot' file from 'telegram-bot-ruby' gem due to the 'coercible' library in ruby-2.7.0-preview1 interpreter.

Error boils down to Undefined = Class.new.freeze line inside options.rb file.

 git:(master) ✗ irb
2.7.0-preview1 :001 > require 'coercible'
Traceback (most recent call last):
       13: from /Users/a.shestakov/.rvm/rubies/ruby-2.7.0-preview1/bin/irb:23:in `<main>'
       12: from /Users/a.shestakov/.rvm/rubies/ruby-2.7.0-preview1/bin/irb:23:in `load'
       11: from /Users/a.shestakov/.rvm/rubies/ruby-2.7.0-preview1/lib/ruby/gems/2.7.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
       10: from (irb):1
        9: from /Users/a.shestakov/.rvm/rubies/ruby-2.7.0-preview1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:34:in `require'
        8: from /Users/a.shestakov/.rvm/rubies/ruby-2.7.0-preview1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:130:in `rescue in require'
        7: from /Users/a.shestakov/.rvm/rubies/ruby-2.7.0-preview1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:130:in `require'
        6: from /Users/a.shestakov/.rvm/gems/ruby-2.7.0-preview1/gems/coercible-1.0.0/lib/coercible.rb:27:in `<top (required)>'
        5: from /Users/a.shestakov/.rvm/rubies/ruby-2.7.0-preview1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        4: from /Users/a.shestakov/.rvm/rubies/ruby-2.7.0-preview1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:54:in `require'
        3: from /Users/a.shestakov/.rvm/gems/ruby-2.7.0-preview1/gems/coercible-1.0.0/lib/support/options.rb:1:in `<top (required)>'
        2: from /Users/a.shestakov/.rvm/gems/ruby-2.7.0-preview1/gems/coercible-1.0.0/lib/support/options.rb:4:in `<module:Coercible>'
        1: from /Users/a.shestakov/.rvm/gems/ruby-2.7.0-preview1/gems/coercible-1.0.0/lib/support/options.rb:5:in `<module:Options>'
FrozenError (can't modify frozen #<Class:#<Class:0x00007fab142f84f8>>)

Establish sane default for nil conversions

When I was in the process of porting Virtus over to RubyMotion, I noticed a peculiarity that I hadn't noticed in previous versions where one could no longer instantiate an object without passing in attributes as well.

E.G.

class User
  include Virtus
  attribute :name, String
end

User.new #=> UnsupportedCoercion: Object#to_string doesn't know how to coerce nil.

The issue arose with the addition of this line of code. The line previously looked like this.

I don't mind handling this one if you give me a strategy. Implement a NilClass coercer that returns value for every coercion method?

skip raise_unsupported_coercion for nil / performance issue

When a value is nil Coercible::UnsupportedCoercion is raised. This raise is a real expensive operation. When I simply create a virtus model 1000.times I get the following profile result

# with virtus
%self      total      self      wait     child     calls  name
 31.30      0.952     0.493     0.000     0.460     4000   Kernel#loop 
 16.98      0.312     0.267     0.000     0.045   132000  *RubyVM::DebugInspector#frame_binding 
  9.92      1.362     0.156     0.000     1.205     8000  *Coercible::Coercer::Object#raise_unsupported_coercion 

# without virtus
%self      total      self      wait     child     calls  name
 41.31      0.002     0.001     0.000     0.001     1000   Class#new 
 38.43      0.003     0.001     0.000     0.002        1   Integer#times 
 19.14      0.001     0.001     0.000     0.000     1000   Model2#initialize 

for this test program:

require 'ruby-prof'

class Model
  include ::Virtus.model

  attribute :code, String
  attribute :reference, String
  attribute :state, String
  attribute :created_at, DateTime
  attribute :id, Integer

  include Equalizer.new(:code, :reference)
end

RubyProf.start

1000.times { Model.new({}) }

result = RubyProf.stop
printer = RubyProf::FlatPrinter.new(result)
printer.print(STDOUT)

class Model2
  attr_accessor :code
  attr_accessor :reference
  attr_accessor :state
  attr_accessor :created_at
  attr_accessor :id

  include Equalizer.new(:code, :reference)

  def initialize(*args)
  end

end

puts "\n"*5

RubyProf.start

1000.times { Model2.new({}) }

result = RubyProf.stop
printer = RubyProf::FlatPrinter.new(result)
printer.print(STDOUT)

It would be great if there is an shortcut for nil or performance increase.

Partly duplicate of #15

Make strict coercion rule configurable

After upgrading to Virtus 1.0 which depends on Coercible 0.2.0, we noticed that a bunch of our attributes are now failing due to the stricter coercion rule. For example, a Date field can no longer accept "" (an empty string) because it cannot be coerced. This is especially troublesome in a rails project where attributes can be optional (so they get submitted as empty strings).

Could we make raising UnsupportedCoercion configurable?

For now I have to monkey patch raise_unsupported_coercion to return value directly to get back 0.1.0's behaviour.

Thanks!

Question: why not use Kernel.Integer and Kernel.Float in String coercer?

Motivation: when Virtus is mixed into a class that accepts POST/PUT/PATCH params and the virtus attribute is of type Integer, a numericality validator fails if the params contains a numeric string with leading/trailing spaces. I expect that the coercer should return an integer in this case. The obvious work around would be to 'groom' the params but this requires defining attribute handling code in two places - the attribute definition and the 'groomer' method(s) and the double handling of the value.

How about in the to_integer and to_float methods, this:

      def to_integer(value)
        Kernel.Integer(value)
      rescue ArgumentError
        Kernel.Integer Kernel.Float(value)
      rescue TypeError
        raise_unsupported_coercion(value, __method__)
      end

      def to_float(value)
        Kernel.Float(value)
      rescue TypeError
        raise_unsupported_coercion(value, __method__)
      end

please move lib/support/ files inside lib/coercible

Hi!

Could you please move files inside lib/support/ inside the lib/coercible subdirectory? This would ensure that all installed files are under the same namespace, and that there are no conflict with other packages when this gem turns into e.g a package for a Linux distribution.

Thanks!

Cédric

Question: Thread safety

Is coercible thread-safe at an instance level, or should we use a new coercible instance per thread?

Specifically when running JRuby which has real threads without an interpreter lock.

Using the example below:

coercer = Coercible::Coercer.new

# coerce a string to a date
coercer[String].to_date('2012/12/25')

# Fetch the string coercer once
string_coercer = coercer[String]
string_coercer.to_date('2012/12/25')

Can one store the 'coercer' instance above into a class or global variable and then use it concurrently from multiple threads?

Also, can multiple threads access the same 'string_coercer' instance across threads?

Memory bloat in method_missing

Hi, we're using v1.0.0 (via Virtus v1.0.5), and running memory_profiler gem's report on a loop-intensive part of our code reports that coercible is allocating 90% of strings:

Allocated String Report
-----------------------------------
   2134952  "to_object"
   2134952  /bundle/ruby/2.5.0/gems/coercible-1.0.0/lib/coercible/coercer/object.rb:164

    100361  "amd"
    100361  /bundle/ruby/2.5.0/gems/money-6.11.3/lib/money/currency.rb:39

     83296  ""
     38591  /bundle/ruby/2.5.0/gems/activerecord-5.0.7.2/lib/active_record/connection_adapters/mysql/database_statements.rb:37

Could freezing strings avoid this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.