FCO / Red

A WiP ORM for Raku

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Weirdness with a parameterized role

jonathanstowe opened this issue · comments

I've just made JSON::Class into a parameterized role in order to support opt-in which is all good, but when applied to a Red model like:

use Red;
use JSON::Class;


my $*RED-DB = database "SQLite";
my $*RED-DEBUG = True;

model Foo does JSON::Class[:opt-in] {
    has Int $.id is serial;
	has Str $.a is column is json;
}

Foo.^create-table;

Results in :

No such method 'attributes' for invocant of type
'Perl6::Metamodel::CurriedRoleHOW'
at /home/jonathan/devel/raku/3rdparty-modules/Red/js:8

It does work fine without the parameter.

And just so we're clear it isn't just JSON::Class this gets the same result:

use Red;


my $*RED-DB = database "SQLite";
my $*RED-DEBUG = True;

role Bar[Bool :$opt-in = False] {

}

model Foo does Bar[:opt-in] {
    has Int $.id is serial;
	has Str $.a is column;
}

Foo.^create-table;

I think it's because the compose of the model may need to do something earlier if it gets a CurriedRoleHOW.

Ah, I can see what it is.

diff --git a/lib/MetamodelX/Red/Model.pm6 b/lib/MetamodelX/Red/Model.pm6
index 100ed3b..71ee21c 100644
--- a/lib/MetamodelX/Red/Model.pm6
+++ b/lib/MetamodelX/Red/Model.pm6
@@ -155,7 +155,7 @@ method compose(Mu \type) {
             self.?TWEAK-MODEL(|c)
         }
     }
-    my @roles-cols = self.roles_to_compose(type).flatmap(*.^attributes).grep: Red::Attr::Column;
+    my @roles-cols = self.roles_to_compose(type).grep({ .HOW !~~ Metamodel::CurriedRoleHOW }).flatmap(*.^attributes).grep: Red::Attr::Column;
     for @roles-cols -> Red::Attr::Column $attr {
         self.add-comparate-methods: type, $attr
     }

fixes that case but I'm not sure what other impacts that may have.

Thanks for reporting!

I'll have to leave it to take a look tomorrow after work.

Have you tried running the tests? How was it?

Thank you again!

It seems to pass the tests (only fails on generating the docs). Tomorrow I’ll write sim tests to it and if everything is working, commit it

The only problem I can see with this would if said parameterised role were to provide columns that needed to be composed. Looking in the ClassHOW compose I think there is something that is done to these prior to them being composed ( I think specialize,) but I'm not sure what the consequences of doing that (and then doing it again in the original compose,) would be.

It seems to work:

➜  Red git:(FCO-patch-3) ✗ raku -I. -e '


use Red;


my $*RED-DB = database "SQLite";
my $*RED-DEBUG = True;

role Bar[Bool :$opt-in = False] {
   has Int $.b is column;
}

model Foo does Bar[:opt-in] {
    has Int $.id is serial;
    has Str $.a is column;
}

Foo.^create-table;


'
SQL : CREATE TABLE "foo" (
   id integer NOT NULL primary key AUTOINCREMENT,
   a varchar(255) NOT NULL ,
   b integer NOT NULL 
)
BIND: []

Yep and does what I expect with the real code too:

use Red;
use JSON::Class;



my $*RED-DB = database "SQLite";
my $*RED-DEBUG = True;

model Foo does JSON::Class[:opt-in] {
    has Int $.id is serial     is json;
	has Str $.public is column is json;
    has Str $.secret is column = "secret";
}

Foo.^create-table;

my $a = Foo.^create( public => "wee", secret => "sssh");

say $a.to-json;

Giving:

SQL : CREATE TABLE "foo" (
   id integer NOT NULL primary key AUTOINCREMENT,
   public varchar(255) NOT NULL ,
   secret varchar(255) NOT NULL 
)
BIND: []
SQL : BEGIN
BIND: []
SQL : INSERT INTO "foo"(
   public,
   secret
)
VALUES(
   ?,
   ?
)
BIND: ["wee", "sssh"]
SQL : SELECT
   "foo".id , "foo".public , "foo".secret 
FROM
   "foo"
WHERE
   _rowid_ = last_insert_rowid()
LIMIT 1
BIND: []
SQL : SELECT
   "foo".id , "foo".public , "foo".secret 
FROM
   "foo"
WHERE
   "foo".id = 1
LIMIT 1
BIND: []
SQL : COMMIT
BIND: []
{
  "public": "wee",
  "id": 1
}

Which is most excellent as that was one of my primary motivations for adding that to JSON::Class, so you could happily shove that straight out of an API without worrying that someone would add some super-sekrit column to the table without json-skip and exposing it to the public 👍

So I'd go with that for the time being and worry about the other part later.