jhass changed the topic of #crystal-lang to: The Crystal programming language | https://crystal-lang.org | Crystal 0.35.1 | 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
daemonwrangler has quit [Ping timeout: 240 seconds]
daemonwrangler has joined #crystal-lang
kerframil has joined #crystal-lang
Vexatos has quit [Quit: ZNC Quit]
Vexatos has joined #crystal-lang
deavmi has quit [Quit: No Ping reply in 180 seconds.]
deavmi has joined #crystal-lang
HumanGeek has joined #crystal-lang
Human_G33k has quit [Ping timeout: 256 seconds]
deavmi has quit [Read error: Connection reset by peer]
deavmi has joined #crystal-lang
f1refly has quit [Ping timeout: 240 seconds]
f1refly has joined #crystal-lang
kerframil has quit [Ping timeout: 240 seconds]
andremedeiros has quit [Quit: ZNC 1.8.1 - https://znc.in]
andremedeiros has joined #crystal-lang
lunarkitty has quit [Ping timeout: 240 seconds]
alexherbo2 has joined #crystal-lang
MasterdonX has quit [Ping timeout: 256 seconds]
kerframil has joined #crystal-lang
MasterdonX has joined #crystal-lang
_whitelogger has joined #crystal-lang
alexherbo24 has joined #crystal-lang
alexherbo2 has quit [Ping timeout: 240 seconds]
alexherbo24 is now known as alexherbo2
alexherbo28 has joined #crystal-lang
alexherbo2 has quit [Ping timeout: 264 seconds]
alexherbo28 is now known as alexherbo2
Vexatos has quit [Quit: ZNC Quit]
Vexatos has joined #crystal-lang
<riffraff169> so, i have this array that running this `puts tokens.class` has this result `Array(Hash(Symbol, Int32 | String | Symbol))`
<riffraff169> so, that is an array of hashes, which is a symbol as a key, and a value of either int32, string, or symbol
<riffraff169> so i am going through them with `tokens.each do |tok|`, and `puts tok.class` and `puts tok.keys`
<riffraff169> tok.class: `Hash(Symbol, Int32 | String | Symbol)`
<riffraff169> if i comment out `puts tok.keys`, no problem, prints the class of the entire tokens var (Array....), and then the class of each entry
<riffraff169> but as soon as i put in `puts tok.key`, right after printing the class, i get like the following error in compile:
<riffraff169> ``` 30 | tokens.each do |tok|
<riffraff169> Error: instantiating '(Array(Array(Hash(Symbol, Int32 | String | Symbol))) | Array(Hash(Symbol, Int32 | String | Symbol)))#each()'
<riffraff169> ^---
<riffraff169> ```
<riffraff169> makes no sense to me....how did line 30 change? it was working fine before
<oprypin> can i get the size of the allocated memory region when i just have the pointer to it?
<oprypin> riffraff169, you have to show all code
<riffraff169> let me get a minimal example that fails, give me a few
<riffraff169> and sometimes i just need to talk these out loud...i just now figured it out....i was using `token = [] of Array(Token)`, but my code was expecting `token = [] of Token`
<riffraff169> thanks for the sounding board
<riffraff169> is it possible to get a back trace (shards build --error-trace) of just user code? my error was in the middle..at the top was the main call, at the bottom was all the library calls
<oprypin> no
<riffraff169> okthanks
alexherbo20 has joined #crystal-lang
alexherbo2 has quit [Ping timeout: 256 seconds]
alexherbo20 is now known as alexherbo2
<FromGitter> <naqvis> > *<oprypin>* can i get the size of the allocated memory region when i just have the pointer to it? ⏎ for objects it should be bit easier via `pp sizeof(typeof(ptr.value))` , but for arrays you would have to calculate that via `size * type_size`
<oprypin> @naqvis i don't have `size`
<FromGitter> <naqvis> I was saying that if `typeof` is `Array` then you can invoke `size` method on that via `ptr.value.size`
<FromGitter> <naqvis> so you would have to do a custom code check
kerframil has quit [Ping timeout: 264 seconds]
kerframil has joined #crystal-lang
<FromGitter> <naqvis> oprypin, something like this ⏎ ⏎ ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f577b9349a1df0a12d74c0c]
<FromGitter> <naqvis> yeah, but oprypin question was getting the size of pointer pointing to sth
<FromGitter> <Blacksmoke16> 👍
<riffraff169> are pointers just standard Int64? or Int32? just curious
<oprypin> riffraff169, size_t. arch dependent
<riffraff169> i mean, in size, not actual type
<riffraff169> ah, sure sure
DTZUZU has quit [Ping timeout: 240 seconds]
DTZUZU has joined #crystal-lang
alexherbo21 has joined #crystal-lang
alexherbo2 has quit [Ping timeout: 256 seconds]
alexherbo21 is now known as alexherbo2
alexherbo25 has joined #crystal-lang
alexherbo2 has quit [Ping timeout: 258 seconds]
alexherbo25 is now known as alexherbo2
<FromGitter> <wontruefree> @636f7374 What is the best was to DM you? You are private on most platforms.
zorp has joined #crystal-lang
alexherbo20 has joined #crystal-lang
alexherbo2 has quit [Ping timeout: 240 seconds]
alexherbo20 is now known as alexherbo2
<raz> hmm. i wonder. should `if foo["bar"]` be made to behave the same as `if foo["bar"]?` with regard to nil? at the moment the first version gives a runtime error when the value is nil. is that something that anyone would ever want? (it feels like a trap)
<FromGitter> <Blacksmoke16> but then everywhere you do `foo["bar"]` not in a conditional you would need to handle `nil`
<raz> well, it seems i already have to do that, or i get a surprise runtime error ¯\_(ツ)_/¯
<raz> perhaps `if foo["bar"]` syntax (w/o question mark) should just be disallowed altogether, hmm
<FromGitter> <Blacksmoke16> not really, `nil` or `false` are still valid values
<FromGitter> <Blacksmoke16> the problem you're dealing with is the key not existing
<raz> oh, perhaps i misread the error
<raz> but when would i ever _want_ that to be a runtime error?
<FromGitter> <Blacksmoke16> you cant know at compile time
<FromGitter> <Blacksmoke16> same idea why `arr[1223424]` raises at runtime
<raz> yea but `if foo["bar"]` (without a ==) seems like a trap
<FromGitter> <Blacksmoke16> `if foo["debug"]`
<raz> is there a case where i would want that syntax, ever?
* raz scratches head
<FromGitter> <Blacksmoke16> if the value of `bar` is a bool
<FromGitter> <Blacksmoke16> or you just want to ensure its truthy
<raz> hm hm
<raz> guess i'm just annoyed cause in other places the compiler forces me to repeat .not_nil! all over the place, sometimes even have to copy variables around
<raz> but in that seemingly obvious spot it just lets me fall into a pit
<FromGitter> <Blacksmoke16> id argue that having `.not_nil!` everywhere is a bit of a smell
<raz> it is, but sometimes not avoidable
<raz> like the whole "copy var to local scope" thing
<raz> i don't like it, but i do like the protection it gives. the above case just feels like sth where the compiler should also yell. forgetting that '?' is just so easy, esp. when coming from ruby
<raz> but i see it's prob not trivial
<FromGitter> <Blacksmoke16> no but thats not the right approach
<FromGitter> <Blacksmoke16> i think you're combining two issues into one problem
<FromGitter> <Blacksmoke16> `if hash["key"]` is totally valid, but doesnt handle the case where there is no `"key"` key
<FromGitter> <Blacksmoke16> `if v = hash["key"]?` handles the case where the hash might not have that key
<raz> yea, but shouldn't i be forced to make that existence-check?
<FromGitter> <Blacksmoke16> but doesnt handle the case where that value is falsey
<FromGitter> <Blacksmoke16> there was an thread about this
<raz> well, i can almost imagine how it went
<raz> probably hard to track whether the existence was checked earlier
<raz> (so it doesn't have to be repeated all over)
<raz> but then again, seems kinda like the same issue as with not_nil! to me
<FromGitter> <Blacksmoke16> so you think `arr[idx]` should return nil by default too?
<raz> nah it should yell at me at compile time if i haven't done bounds checking, i think
<raz> but could be missing something
<FromGitter> <Blacksmoke16> what so `if arr[0]` should be a compile time error?
<FromGitter> <Blacksmoke16> thats sounds like a pain
<raz> yup, if there isn't a `if arr[0].size > 0` nearby i suppose
<FromGitter> <Blacksmoke16> 👎
<FromGitter> <Blacksmoke16> you have the tools to do that now, idt it should be a compile time error because the compiler cant know about the context its in
<raz> well, that's the problem, it should know! :p
<FromGitter> <Blacksmoke16> it cant, because the array only exists at runtime...let me see if i can dig up that thread
<raz> well, it could do taint checks i think
<raz> but might not be easy
<FromGitter> <Blacksmoke16> i think the thread was one a troll made so it might have been removed..
<raz> and yea... ok, might not be possible for `if arr[other_runtime_var]` :(
<raz> yeh, i just hate runtime errors
<FromGitter> <Blacksmoke16> write all your code in macros :S
<raz> might be worth a try. just one macro for every possible input :p
<FromGitter> <Blacksmoke16> heh
alexherbo2 has quit [Ping timeout: 258 seconds]
alexherbo2 has joined #crystal-lang
renich has joined #crystal-lang
renich has quit [Quit: Leaving.]
<repo> Blacksmoke16: hey o/
<FromGitter> <Blacksmoke16> o/
<repo> i picked up an old athena project again and i'm having some trouble porting it to the latest version
<repo> i'm currently struggling a bit with param converters
<FromGitter> <Blacksmoke16> oh? whats the error?
<FromGitter> <Blacksmoke16> ah yea, generics arent supported like that
<repo> ah dang
<repo> can i pass the class to the converter over the configuration?
<repo> ah yeah
<FromGitter> <Blacksmoke16> `configuration model : Granite::Base.class` allows you to do like `model: User` within the ann
<repo> exactly
<repo> thought so
<repo> awesome, thanks
<FromGitter> <Blacksmoke16> np
<FromGitter> <Blacksmoke16> i also dont think `path_params` is still a thing?
<FromGitter> <Blacksmoke16> should get it out of `request.attributes`
alexherbo2 has quit [Ping timeout: 265 seconds]
<FromGitter> <RespiteSage> Is there something in stdlib for a terminal prompt?
<FromGitter> <Blacksmoke16> ```print "Enter your name: " ⏎ name = gets``` [https://gitter.im/crystal-lang/crystal?at=5f57d359d4f0f55ebb0e14f4]
<FromGitter> <Blacksmoke16> other than that, no
<repo> yeah
<FromGitter> <Blacksmoke16> @watzon has some shards related to that tho
<repo> Blacksmoke16: another thing: https://p.jokke.space/7ZrC/
<repo> i get record is of type Nil here
<FromGitter> <Blacksmoke16> does it include the module?
<FromGitter> <Blacksmoke16> ah `model : ASR::Serializable.class` idt you can do this
<FromGitter> <Blacksmoke16> would be better off making some parent abstract type you can inherit from
<repo> ah ok ic
<repo> will try that
<FromGitter> <Blacksmoke16> im not a fan of the implementation it feels brittle/hacky to me
<FromGitter> <Blacksmoke16> might revisit it at some point
<repo> hm i don't seem to be able to include Assert in an inherited macro
<FromGitter> <Blacksmoke16> oh?
alexherbo2 has joined #crystal-lang
<repo> Error: class variable '@@validators' of ...Models::Timeslot already defined as Array(NamedTuple(field: String, message: String, block: Proc(...Models::Base, Bool))) in ...Models::Base
<FromGitter> <Blacksmoke16> thats a granite bug
<FromGitter> <Blacksmoke16> i think
<FromGitter> <Blacksmoke16> granite doesnt support inheritence
<repo> oh?
<FromGitter> <Blacksmoke16> oh hm
<FromGitter> <Blacksmoke16> yea i still think its the same issue
<FromGitter> <Blacksmoke16> assert doesnt use a class var like that
<FromGitter> <Blacksmoke16> try reopening granite base and add the inherited macro tehre
<repo> mhm ok
<repo> hm still record is nil
<FromGitter> <Blacksmoke16> https://github.com/Blacksmoke16/athena-blog-tutorial/blob/master/src/converters/request_body_converter.cr#L39-L42 is the key and have the type defined as `Granite::Base.class`
<repo> aahhhh ASR:Model!
<repo> not ASR::Serializable
<FromGitter> <Blacksmoke16> yea
<repo> i have the feeling i bumped into this already
<repo> :D
<FromGitter> <Blacksmoke16> yea its annoying
<FromGitter> <Blacksmoke16> > im not a fan of the implementation it feels brittle/hacky to me ⏎ > might revisit it at some point ⏎ ⏎ :P [https://gitter.im/crystal-lang/crystal?at=5f57d9f0482378093771b4d5]
<repo> :)
<repo> hmm now i'm having some trouble with the Choice assertion. I suspect i cannot use it this way? https://p.jokke.space/N1Zs/
<repo> i'm seeing: Error: wrong number of type vars for Assert::Assertions::Choice(PropertyType, ChoicesType) (given 1, expected 2)
<FromGitter> <Blacksmoke16> hm
<FromGitter> <Blacksmoke16> sec
<FromGitter> <Blacksmoke16> try doing like `@[Assert::Choice(String, choices: Backend.config.available_locales)]`
<FromGitter> <Blacksmoke16> er `Array(String)`
<repo> ah ok
<repo> yeah that works!
<FromGitter> <Blacksmoke16> poorly documented on my part i guess but 👍
<FromGitter> <Blacksmoke16> `Assert` shard is going to be deprecated soonish in favor of the validator component as well
<FromGitter> <Blacksmoke16> just need to finish some things up on it and write up the dos
<FromGitter> <Blacksmoke16> docs
<repo> cool
<repo> this is the converter https://p.jokke.space/Cy5/
<FromGitter> <Blacksmoke16> what version of granite are you on?
<FromGitter> <Blacksmoke16> pretty sure i fixed that
<repo> 0.19.0
<FromGitter> <Blacksmoke16> yea that sounds old
<repo> oh
<FromGitter> <Blacksmoke16> try like `version: 0.21.1`
<repo> ah yeah
<repo> i had locked micrate on a commit for some reason
<FromGitter> <j8r> on the shard.yml?
<repo> yeah
<FromGitter> <j8r> it is better to not put any restriction on shard.yml, same for other package management files (unless strictly necessary)
<FromGitter> <Blacksmoke16> :0
<FromGitter> <j8r> the lock file does that for us :)
<FromGitter> <Blacksmoke16> im not so sure i agree
<FromGitter> <Blacksmoke16> you do one `shards update` and boom everything's broken
<FromGitter> <j8r> yeah, that's the plan
<FromGitter> <Blacksmoke16> :thinking:
<FromGitter> <Blacksmoke16> id at least do something like `version ~> 0.21.0`
<FromGitter> <j8r> no, because it will never be updated
<FromGitter> <Blacksmoke16> hm?
<FromGitter> <j8r> because you will never know there is a new `0.22.0`
<FromGitter> <j8r> then, this will be stuck for long time
<FromGitter> <Blacksmoke16> thats better than blindly upgrading
<FromGitter> <j8r> my take: ⏎ `shards update` ⏎ `git diff` the lock file ⏎ fix all, or each dependency at a time if too tedious [https://gitter.im/crystal-lang/crystal?at=5f57e03d49148b41c9a1ae39]
<FromGitter> <j8r> @Blacksmoke16 I disagree
<FromGitter> <j8r> like repo, you can quickly end up with very old versions
<FromGitter> <Blacksmoke16> maybe if the project is a pet project
<FromGitter> <j8r> insecure, buggy, etc
<FromGitter> <Blacksmoke16> but doing that with an actual project is prob not the best idea
<FromGitter> <j8r> no, because you can have bugs then
<FromGitter> <j8r> how do you keep track for 20 dependencies then?
<FromGitter> <j8r> how do you know the new versions?
<FromGitter> <Blacksmoke16> if crystal had dependabot, that would be one way
<FromGitter> <Blacksmoke16> otherwise setup release notifications
<FromGitter> <j8r> Even for production stuff, I do like this: ⏎ I update all, I test, then deploy.
<FromGitter> <Blacksmoke16> i guess our views on this are just different
<FromGitter> <j8r> I won't do this for like 20 dependencies, I prefer to test 1 version of my app
<FromGitter> <Blacksmoke16> id rather keep a known working state and only upgrade when i need new feature/bug fixed
<FromGitter> <j8r> the known working state is in the lockfile
<FromGitter> <j8r> my only issue is "when i need new feature/bug fixed"
<FromGitter> <j8r> no way to know easily :/
<FromGitter> <j8r> unless keeping track of changelogs of each lib, to see if it fix a thing :/
<FromGitter> <Blacksmoke16> thats what i do :P granted my projects dont have *too* many deps and it also probs help im a maintainer/creator of most of them 😆
<FromGitter> <j8r> that's also how we do with our Infrastructure hosts (I work as a DevOps)
<FromGitter> <j8r> one of my team does as you said
<FromGitter> <j8r> for Debian, see the changelogs
<FromGitter> <j8r> he gave up, too too wide :P
<FromGitter> <Blacksmoke16> thats not exactly the same thing
<FromGitter> <Blacksmoke16> but yes, too much to look at there
<FromGitter> <j8r> it can be similar: we can pin library versions in most UNIX
<FromGitter> <j8r> and they impact the applications running on
<FromGitter> <Blacksmoke16> also dont get me wrong in saying "you should hard pin your versions" i think a lot of it depends on what the specific dep is
<repo> Blacksmoke16: any hints to how to serialize a has_one/has_many relationship with ASR?
<FromGitter> <Blacksmoke16> are some that you should treat differently than others
<FromGitter> <j8r> @Blacksmoke16 kind of agree yeah
<FromGitter> <Blacksmoke16> what happens if you just define some methods and throw like `@[ASRA::VirtualProperty]` on them?
<repo> i thought i'd just slap on a VirtualProperty onto the has_one macro but that didn't work :)
<FromGitter> <Blacksmoke16> macro prob defines the ivar first so it doesnt get applied correctly
<repo> most likely
<repo> but i could just call the method whatever and rename it with ASRA::Name i guess
<FromGitter> <Blacksmoke16> yea
<FromGitter> <j8r> for example we have `ansible ~> 2.8` in our project, because we know 2.9 requires times to upgrade to fix warnings/breaks
<FromGitter> <Blacksmoke16> or just do like
<FromGitter> <Blacksmoke16> ```@[ASRA::VirtualProperty] ⏎ def user ⏎ previous_def ⏎ end``` [https://gitter.im/crystal-lang/crystal?at=5f57e2d2a5788a3c29e0f945]
<FromGitter> <Blacksmoke16> @j8r yes exactly
<FromGitter> <Blacksmoke16> are some things that need more effort to upgrade so you dont want those changing at a whim
<repo> ah right
<repo> i rarely use previous_def. it's so neat
<FromGitter> <Blacksmoke16> we have a thing at work where you can annotate an association property with `Expandable`, then in the related endpoint request you can add a like `?expand=user` query param and it calls/sets the related obj
<FromGitter> <Blacksmoke16> versus having it be included everytime
<FromGitter> <j8r> that's this. On the other hand, we know we need `boto3` for ansible, but no version is specifically said. So, we take the most up-to-date, ans let it pinned in the lock
sagax has joined #crystal-lang
<FromGitter> <j8r> the we try some playbooks to ensure most things works, at least. OFC, regretions can happen
<FromGitter> <Blacksmoke16> i started using ansible for another side project of mine, is pretty slick
<FromGitter> <Blacksmoke16> still trying to figure out how to best utilize it
<FromGitter> <j8r> what do you mean?
<FromGitter> <j8r> We use it also with Packer
<FromGitter> <j8r> to build Amazons VM images, AMIs
<FromGitter> <Blacksmoke16> like atm im just using it to seed credentials (docker secrets) and scaffolding servers (installing required deps and such)
<FromGitter> <Blacksmoke16> but also working on how to best handle deploys, which will essentially just be updating tag of some docker services
<FromGitter> <j8r> that's a good use, VM setup
<FromGitter> <j8r> for Docker deployment, not sure
<FromGitter> <Blacksmoke16> exactly yea
<FromGitter> <Blacksmoke16> not sure how to handle CD in that regard
<FromGitter> <j8r> have you a Jenkins, something like that?
<FromGitter> <Blacksmoke16> naw
<FromGitter> <Blacksmoke16> if anything was thinking of a GH action in a private repo?
<FromGitter> <j8r> GitLab can do a fine CD too
<FromGitter> <j8r> GitHub Actions, not sure
<FromGitter> <Blacksmoke16> triggered on webhook from a new release on other repos
<FromGitter> <Blacksmoke16> :shrug: atm im just doing it manually, is simple enough atm
<FromGitter> <j8r> yep
<FromGitter> <j8r> why not adding to the repo itself?
<FromGitter> <j8r> If there is a new release, ssh to the host and deploy?
<FromGitter> <j8r> there are repo secrets
<FromGitter> <j8r> and org secrets AFAIK
<FromGitter> <Blacksmoke16> hm
<FromGitter> <Blacksmoke16> possibly, code is going to be open source, but the config that hosts it is not
<FromGitter> <Blacksmoke16> didnt think thru if thats going to be a problem or not yet
<FromGitter> <j8r> if the deployment is rare, manual deployment is not a problem indeed
<FromGitter> <Blacksmoke16> yea no super worried about it atm
<FromGitter> <Blacksmoke16> (this is the contract concurrency thing i asked about a while ago)
<FromGitter> <Blacksmoke16> its in a pretty good spot now, runs in like 20sec or so
<FromGitter> <Blacksmoke16> been running last day or so w/o issue
<repo> hmm i've got another problem with granite: if i'm using Int32 primary keys, i can't use relations anymore
<repo> i tried also defining foreign_key: my_model_id : Int32 but that didn't help
<FromGitter> <Blacksmoke16> that should be the way to do it
<FromGitter> <Blacksmoke16> can you share the models?
<repo> yeah
<FromGitter> <Blacksmoke16> that makes sense tho no?
<FromGitter> <Blacksmoke16> you're not allowing `nil`
<FromGitter> <Blacksmoke16> `foreign_key: my_model_id : Int32?` if its optional
<repo> ah
<repo> ahh
<repo> dang
<FromGitter> <Blacksmoke16> ye
<repo> hm no
<repo> it's not optional
<FromGitter> <Blacksmoke16> compiler seems to think the value you're assigning can be nilable tho
<repo> ah maybe on the has_one side
<repo> hm no
<repo> there i can't set the type
<FromGitter> <Blacksmoke16> can `time_slot_id` be `nil`?
<repo> ooohh
<repo> save has to be before the assignment of course
<repo> clever compiler!
<repo> although i don't even have to set that
kerframil has quit [Ping timeout: 256 seconds]
<repo> Blacksmoke16: i haven't found a nice way to update records yet in athena.. :/
<FromGitter> <Blacksmoke16> like with a PUT request?
<repo> yeah
<repo> i tried something like this: https://p.jokke.space/zf5E9k/
<FromGitter> <Blacksmoke16> did you see the db constructor?
<repo> but this fails to set the association
<repo> yeah i have the request body converter
<FromGitter> <Blacksmoke16> slightly diff thing
<FromGitter> <Blacksmoke16> this sources the obj from the db then applies the changes to it
<FromGitter> <Blacksmoke16> so keys/timestamps etc are preserved
<FromGitter> <Blacksmoke16> which would help with your problem i think? since the fks wouldnt need to be set again
<repo> oh ok i see
<FromGitter> <Blacksmoke16> as opposed to creating a new obj directly from the body of the request
<repo> but i wonder why object_from_db.update!(new_object.to_h) doesn't work
<FromGitter> <Blacksmoke16> not sure
<repo> i'll get a runtime error that time_slot_id is nil
<FromGitter> <Blacksmoke16> is it included in the `new_object`?
<repo> no
<FromGitter> <Blacksmoke16> that might be why then?
<repo> but doesn't update! just update attributes passed to it (leaving untouched attributes be)
<FromGitter> <Blacksmoke16> update the log level and see what the query is?
<repo> ah yeah ok it makes sense, because to_h will include the nil values as well
<repo> so it'll be overwritten then
<repo> the issue with the code you linked is that it's very hard to understand without getting deep into the whole athena universe :/
<FromGitter> <Blacksmoke16> a bit yea
<FromGitter> <Blacksmoke16> not *terrible* tho
zorp has quit [Ping timeout: 260 seconds]
alexherbo2 has quit [Ping timeout: 246 seconds]
HumanGeek has quit [Remote host closed the connection]
HumanGeek has joined #crystal-lang
lunarkitty has joined #crystal-lang