dkubb / axiom

Simplifies querying of structured data using relational algebra
https://github.com/dkubb/axiom
MIT License
459 stars 22 forks source link

Support for Boolean values #31

Closed paneq closed 11 years ago

paneq commented 11 years ago

How do you create relation with Boolean values ?

relation = Axiom::Relation.new(
  [ [ :id, String ], [ :name, String ], [ :color, String ], [ :weight, Float ], [ :city, String ], [:bool, TrueClass] ],
  [
    [ 'P1', 'Nut',   'Red',   12.0, 'London', true ],
    [ 'P2', 'Bolt',  'Green', 17.0, 'Paris', true  ],
    [ 'P3', 'Screw', 'Blue',  17.0, 'Oslo', false   ],
    [ 'P4', 'Screw', 'Red',   14.0, 'London', false ],
    [ 'P5', 'Cam',   'Blue',  12.0, 'Paris', false  ],
    [ 'P6', 'Cog',   'Red',   19.0, 'London', true ],
  ]
)

leads to an exception:

NoMethodError: undefined method `new' for TrueClass:Class
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/axiom-0.1.0/lib/axiom/attribute.rb:41:in `coerce'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/axiom-0.1.0/lib/axiom/relation/header.rb:77:in `coerce_attribute'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/axiom-0.1.0/lib/axiom/relation/header.rb:46:in `block in coerce'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/axiom-0.1.0/lib/axiom/relation/header.rb:48:in `map'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/axiom-0.1.0/lib/axiom/relation/header.rb:48:in `coerce'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/axiom-0.1.0/lib/axiom/relation.rb:73:in `initialize'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/axiom-0.1.0/lib/axiom/relation/materialized.rb:29:in `initialize'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/adamantium-0.0.7/lib/adamantium/class_methods.rb:17:in `new'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/adamantium-0.0.7/lib/adamantium/class_methods.rb:17:in `new'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/axiom-0.1.0/lib/axiom/relation.rb:58:in `new'
    from RUBY_PATH/lib/ruby/gems/1.9.1/gems/axiom-0.1.0/lib/axiom/relation.rb:56:in `new'

Same problem with having Symbols in a tuple.

dkubb commented 11 years ago

@paneq One way is to define the boolean attribute using Axiom::Attribute::Boolean.new(:bool). That's not the ideal case though. It's unfortunate there's no Boolean type in Ruby.. I've toyed with the idea of adding one if it doesn't exist, which would allow you to say [:bool, Boolean]. The only thing stopping me is it feels wrong to add constants outside my namespace to Kernel.

Another approach that could work is if I do the following assignment somewhere:

Axiom::Attribute::TrueClass = Axiom::Attribute::Boolean
Axiom::Attribute::FalseClass = Axiom::Attribute::Boolean

This will allow you to use both [:bool, TrueClass] and [:bool, FalseClass]. Not quite as elegant as Boolean, but it's a step in the right direction.

Some of these problems will go away when I finish refactoring axiom to use axiom-types which has a more extensive primitive to type mapping system. We still wouldn't be able to use the bare constant Boolean anywhere without adding a Boolean constant.. actually, while typing this I just had a thought, you could do this in your own application:

# Define a Boolean constant (the value doesn't matter)
Boolean = nil

relation = Axiom::Relation.new(
  [ [ :id, String ], [ :name, String ], [ :color, String ], [ :weight, Float ], [ :city, String ], [:bool, Boolean] ],
  [
    [ 'P1', 'Nut',   'Red',   12.0, 'London', true ],
    [ 'P2', 'Bolt',  'Green', 17.0, 'Paris', true  ],
    [ 'P3', 'Screw', 'Blue',  17.0, 'Oslo', false   ],
    [ 'P4', 'Screw', 'Red',   14.0, 'London', false ],
    [ 'P5', 'Cam',   'Blue',  12.0, 'Paris', false  ],
    [ 'P6', 'Cog',   'Red',   19.0, 'London', true ],
  ]
)

I don't feel comfortable doing this just yet because Boolean could be defined in any dependency in an app, but you're free to do so in your own app.

dkubb commented 11 years ago

The first solution to this has been applied to axiom in https://github.com/dkubb/axiom/commit/d9957595cf0e97938f4847e985511dc4f15ceed7 This will allow your example to work.

As far as adding Kernel::Boolean when it does not exist, it's something I'm going to defer on for now. Maybe it'll be added in the future if enough people ask, or maybe it'll be something you have to explicitly require in your app (like having you do require 'axiom/core_ext/boolean' in your app). I'll wait until axiom-types is integrated though.

For now I'm going to close this issue. If you have any problems using the commit above, please comment here and I will reopen this issue.

blambeau commented 11 years ago

@dkubb You know I'm a nitpicker ;-) but the problem with the first solution is that it does not make sense from a semantics point of view. The relation header (say, [:bool, TrueClass]) clearly states that the :bool attribute may only be true, and tuples having a false value are accepted though.

The only sound way is through Axiom::Attribute::Boolean.new(:bool). Alternatively, it might be a good idea to create a unique, universally accepted gem boolean-rb that adds the Boolean constant on top-level namespace once for all. As you probably know, I need it as well.

dkubb commented 11 years ago

@blambeau actually, one thing I was thinking of doing would be:

module Boolean; end

TrueClass.class_eval  { include Boolean }
FalseClass.class_eval { include Boolean }

Then you could do things like value.kind_of?(Boolean).

dkubb commented 11 years ago

@blambeau oh and don't worry about feeling like a nitpicker, I never think that ;) I love hearing your comments, and even when we don't always agree, they always make me think of things from a new point of view. So please feel free to point out anything you think can be improved.

blambeau commented 11 years ago

@dkubb Agreed about value.kind_of?(Boolean). I like having Boolean#=== working properly as well. We should probably start with some spec tests.

Related (my way of implementing Boolean for now): https://github.com/blambeau/domain#union-domains