ChanServ changed the topic of #crystal-lang to: The Crystal programming language | https://crystal-lang.org | Fund Crystal's development: https://crystal-lang.org/sponsors | GH: https://github.com/crystal-lang/crystal | Docs: https://crystal-lang.org/docs | Gitter: https://gitter.im/crystal-lang/crystal
<FromGitter> <jrei:matrix.org> previous macs were notoriously not good. They even thermal throttle
waleee has quit [Ping timeout: 240 seconds]
waleee has joined #crystal-lang
fifr[m] has quit [Ping timeout: 240 seconds]
fifr[m] has joined #crystal-lang
waleee has quit [Ping timeout: 240 seconds]
ur5us has quit [Ping timeout: 260 seconds]
<FromGitter> <moe:busyloop.net> "super complex" unfortunately starts at... having a JOIN in your query. ⏎ i think more realistically one should say "if all you want to do is 2-3 trivial queries".
<FromGitter> <moe:busyloop.net> for me clear ORM has been the best experience so far. the docs are still a bit rough in some places, but the code is solid.
ur5us has joined #crystal-lang
ur5us has quit [Ping timeout: 256 seconds]
notzmv has quit [Ping timeout: 250 seconds]
[RMS] has joined #crystal-lang
[R] has quit [*.net *.split]
Stephie has quit [*.net *.split]
Stephie has joined #crystal-lang
ejjfunky has joined #crystal-lang
notzmv has joined #crystal-lang
<FromGitter> <alex-lairan> @moe:busyloop.net no clear couldn't be solid because of the fact it uses the active record pattern, just this fact break the s ^^ ⏎ ⏎ I do complex queries with a lot of joins and CTE. ⏎ ⏎ I need an ORM to hide this behind repositories for that my app code doesn't care about the complex queries. ... [https://gitter.im/crystal-lang/crystal?at=62051ea0e4c979478d6e98df]
<FromGitter> <alex-lairan> I am porting a ruby project that uses ROM-RB with a heavy db usage that returns aggregates for the app domain.
<FromGitter> <alex-lairan> If the queries are complex, what could be the problems?
<FromGitter> <Blacksmoke16> that `.from_rs` doesn't work if you have a join
<FromGitter> <Blacksmoke16> as its only really able to instantiate a single obj, not one that has a nested obj
<FromGitter> <alex-lairan> @Blacksmoke16 I will instantiate a DB::Database object to hold the connection, I use ADI from Athena to manage my dependencies. ⏎ ⏎ To store it I just have to do ⏎ ⏎ ```ADI.bind db_connection : DB::Database,``` [https://gitter.im/crystal-lang/crystal?at=620523c3708e9c3dd77092e1]
<FromGitter> <Blacksmoke16> i'd just use a constant for that tbh
<FromGitter> <Blacksmoke16> it manages an internal pool, so no need to have multiple of them
<FromGitter> <Blacksmoke16> then yea, could prob bind the const such that it's provided when that arg name/type is encountered
<FromGitter> <alex-lairan> Ok, I will try this :) ⏎ ⏎ A constant doesn't really fit my use case because I will have dynamic connection to multiples DB with the same schema.
<FromGitter> <Blacksmoke16> could also just manually wire up that param if that would be easier. something like `@[ADI::Register(_db_connection: DATABASE)]`
<FromGitter> <Blacksmoke16> ah gotcha, thats fair. in that case yea, could prob get away with binding multiple `DB::Database` instances with diff constructor names
<FromGitter> <alex-lairan> The app is multi-tenant + multi-db. Legal obligation for multi-db. :(
<FromGitter> <Blacksmoke16> hows the DI stuff working out? first time im hearing about it being used on its own outside of the frameowkr
<FromGitter> <alex-lairan> We will see that :D ⏎ ⏎ 1st time I use your DI lib too
<FromGitter> <Blacksmoke16> haha fair enough. Any questions/issue feel free to message me
<FromGitter> <alex-lairan> At the company I am working on, we like your shards philosophy, but not the controller one, so we might use all of them except this one :)
<FromGitter> <Blacksmoke16> but thats like the core one that ties everything together :P
<FromGitter> <Blacksmoke16> what dont you like about it? the annotations or?
<FromGitter> <alex-lairan> One class for multiple actions, this don't allow us to inject repositories inside the controller action. ⏎ ⏎ Also, we prefer separate router/action. ⏎ ⏎ I have extracted this part of our PoC ... [https://gitter.im/crystal-lang/crystal?at=62052736708e9c3dd7709a49]
<FromGitter> <alex-lairan> Here repositories objects just change the state of a global array ⏎ ⏎ I am at the part of the PoC where I connect to a database, then I will have to inject a database depending on a variable from the HTTP query.
<FromGitter> <alex-lairan> In ruby we used https://dry-rb.org/gems/dry-effects/0.1/effects/resolve/ for the dynamic part, here I think the container will keep track of all databases, and a resolver will find the correct one
<FromGitter> <alex-lairan> The production running code example : ⏎ ⏎ ```code paste, see link``` ⏎ ⏎ It's a rack middleware that I will have to convert into a Crystal HTTP Middleware [https://gitter.im/crystal-lang/crystal?at=6205293a0779373db8da130b]
<FromGitter> <Blacksmoke16> > One class for multiple actions, this don't allow us to inject repositories inside the controller action. ⏎ ⏎ you deff could, esp when using `ADI.bind`. I.e. if you have multiple instantiations of `DB::Database` you can have your constructor accept them by name: ⏎ ⏎ ```def initialize( ⏎ @db_one : DB::Database, ⏎ @db_two : DB::Database ⏎ ); end```
<FromGitter> <Blacksmoke16> > have to inject a database depending on a variable from the HTTP query. ⏎ ⏎ Ah, so for this you could inject another service and call like `.get_repository param` that returns the correct `DB::Database`
<FromGitter> <Blacksmoke16> `.get_database`*
<FromGitter> <Blacksmoke16> ofc could just have 1 action per controller, idea was to allow multiple just for organizational as the same controller usually containers all the endpoints related to some resource/feature. In the end the majority of your business logic would be in the service layer. I.e. thin controllers so doesnt really matter *too* much in the long run
<FromGitter> <Blacksmoke16> technically could also use a request listener to extract data from the request and set the related param, so that your controller could just be like: ⏎ ⏎ ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=62052ba06e4c1e1c8459b92e]
<FromGitter> <Blacksmoke16> param converter would prob also do a similar thing, but a bit more robustly. I.e. if you wanted to inject a specific `Repository` type for a given entity versus the raw database instance
waleee has joined #crystal-lang
ur5us has joined #crystal-lang
ejjfunky has quit [Quit: Leaving]
dostoyevsky2 has quit [Ping timeout: 256 seconds]
ur5us has quit [Ping timeout: 250 seconds]
dostoyevsky2 has joined #crystal-lang
ejjfunky has joined #crystal-lang
ur5us has joined #crystal-lang
<FromGitter> <moe:busyloop.net> quiz time: https://carc.in/#/r/cqjo ⏎ ⏎ anyone good with type inference? 🤔
<FromGitter> <Blacksmoke16> https://carc.in/#/r/cqjv
<FromGitter> <moe:busyloop.net> hmm that looks at least more transparent. ⏎ i feel like it's probably the exact same that += does under the hood, tho?
<FromGitter> <moe:busyloop.net> (my concern is about the inefficiency of touching/casting all array elements individually, but perhaps the compiler optimizes all that away anyway.. hm)
<FromGitter> <Blacksmoke16> pretty sure its the best you're going to get
<FromGitter> <Blacksmoke16> without getting into overloads for single items or series of them w/o an existing array
<FromGitter> <Blacksmoke16> depends on if in your real usecase you'll have an actual array already or not
<FromGitter> <moe:busyloop.net> yea, i could muck up something ugly with macros. but i bump into this kinda regularly. feels like in principle the compiler *should* be able to infer that `Array(B)` can be cast to `Array(A)` when B is a subclass of A 🤔
<FromGitter> <moe:busyloop.net> anyway, will go with one of these two options for now, thx for confirming that i'm at least not missing something too obvious. ⏎ ⏎ yea, i guess this one falls in the "not fully supported" bucket
<FromGitter> <Blacksmoke16> easiest solution is just to add the `of ...`
<FromGitter> <moe:busyloop.net> yup but the call is a user-facing api. i don't want the poor devs to have to add that boilerplate to every call.
<FromGitter> <Blacksmoke16> :shrug: not really a big deal
<FromGitter> <Blacksmoke16> only really needed if there's only 1 item in the array as well
<riza> why not just make it an array of Jerry
<FromGitter> <Blacksmoke16> so having an overload that accepts just 1 item might be just as good
<riza> @mice : Array(Jerry)
<FromGitter> <moe:busyloop.net> cause i want to take any types of mice there, as long as they `include JSON::Serializable` ;)
<riza> yeah I get that
<riza> the situation where you want to `Jerry.new([])` is weird -- just make an initializer that takes nothing so you can do `Jerry.new()` instead
<FromGitter> <Blacksmoke16> https://carc.in/#/r/cqkc
<riza> Aside from that, nobody will have to have the verbose-typesneeze that an empty array creates
<FromGitter> <Blacksmoke16> riza its an issue with 1 element arrays as it gets typed as that type instead of the parent type
<FromGitter> <Blacksmoke16> e.g. if you had `[Jerry.new]` with a parent `Mouse` type the type of that array would be `Array(Jerry)` which would fail when assigning it to `@mice : Array(Mouse)`
<FromGitter> <moe:busyloop.net> doesn't count, you changed the line. ;) but yea i should've made clearer that i want to pass arrays of 1..n elements. in reality Jerry would be e.g. an ORM collection-object.
<FromGitter> <moe:busyloop.net> or literally just anything that serializes to json
<FromGitter> <Blacksmoke16> sure, but this is a better API when you just want to create a collection with 1 item
<FromGitter> <moe:busyloop.net> grrr. i just tried to keep the sample code simple. if i wanted to limit it to 1 element i wouldn't use an array :P
<FromGitter> <Blacksmoke16> still think `of Mouse` wouldn't be the end of the world, would have to compare the generated code of that versus the map to know if theres actually a diff tho
<FromGitter> <jrei:matrix.org> A forall + generic trick could do it
<FromGitter> <moe:busyloop.net> hmm yap good call j8r. 🤔
<FromGitter> <jrei:matrix.org> Having the JSON::Serializable restriction seems not ideal. Can any type be really accepted? ⏎ Note that Crystalizer can serialize any type out-of-the-box
<FromGitter> <moe:busyloop.net> yea, the use-case is for serializing a json-api response; `JsonApi::Response.new(data: User.where("xyz"))` (JsonApi::Response is the long name for "Tom")
<FromGitter> <moe:busyloop.net> so it should take an array of anything that is JSON::Serializable
<FromGitter> <Blacksmoke16> Do you need to store the array at all
<FromGitter> <moe:busyloop.net> i'll look into generics, i think i may have actually used them for sth similar in the past
<FromGitter> <jrei:matrix.org> https://carc.in/#/r/cqkh
<FromGitter> <Blacksmoke16> Versus like super data.to_json
<FromGitter> <moe:busyloop.net> j8r (https://matrix.to/#/@jrei:matrix.org): 🥇
<FromGitter> <Blacksmoke16> If you go that route you'll need to manually ensure it's serializable, as atm that would accept an array of anything
<FromGitter> <moe:busyloop.net> https://carc.in/#/r/cqkj
<FromGitter> <moe:busyloop.net> looks like the compiler does that for me 💪
<FromGitter> <Blacksmoke16> Kinda, but it's not as clear imo
<FromGitter> <Blacksmoke16> As like "Error: Data is not serializable"
<FromGitter> <moe:busyloop.net> yeh, i can probably whip up a clearer error with a maro
<FromGitter> <Blacksmoke16> But backing up, is there a reason you need to store the data at all?
<FromGitter> <moe:busyloop.net> but thx j8r! that was the missing link (i always forget about generics)
<FromGitter> <moe:busyloop.net> no, strictly speaking i could *probably* do without storing it. but not sure about the gymnastics that would take. since there will be multiple of such attributes (`data`, `meta`, `errors`) and i don't want to serialize immediately on obj creation
<FromGitter> <jrei:matrix.org> a macro could be added to implement Blacksmoke16 (George Dietrich)'s suggestion
<FromGitter> <Blacksmoke16> Fair enough
<FromGitter> <moe:busyloop.net> yap, that part i'll figure out. friendly error is indeed desirable
<FromGitter> <Blacksmoke16> ^ might be a bit tricky since primitive types do not include that module. IMO im still in the boat of keep it simple and throw an of ... on it and be done versus introducing generics just to avoid that but :shrug:
<FromGitter> <jrei:matrix.org> there is this hack
<FromGitter> <jrei:matrix.org> https://carc.in/#/r/cqkl
<FromGitter> <jrei:matrix.org> it will return an error, but maybe better to have a proper error
<FromGitter> <moe:busyloop.net> nice, saving that to my crystal snippets. will def come in handy at some point.
<FromGitter> <Blacksmoke16> https://carc.in/#/r/cqkn is another option
<FromGitter> <Blacksmoke16> just an alternate syntax to `of ...`
<FromGitter> <jrei:matrix.org> you meant https://carc.in/#/r/cqko
<FromGitter> <jrei:matrix.org> a wait
<FromGitter> <Blacksmoke16> mainly was doing it for the array like syntax part of it
<FromGitter> <jrei:matrix.org> not working, I misstyped a : to =
<FromGitter> <jrei:matrix.org> It doesn't solve his issue actually
<FromGitter> <moe:busyloop.net> interesting, that looks a bit less obnoxious indeed. but i don't really want the user to have to think about it at all. just pass what you got, i'll complain if it's not serializable.
<FromGitter> <Blacksmoke16> it does, just not the way he wants ;)
<FromGitter> <Blacksmoke16> which is valid
<FromGitter> <moe:busyloop.net> if i want boilerplate i use golang. since i never want that, i use crystal. :p
waleee has quit [Ping timeout: 260 seconds]