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
zorp has quit [Ping timeout: 258 seconds]
alexherbo2 has quit [Ping timeout: 260 seconds]
FromGitter has quit [Ping timeout: 260 seconds]
oprypin has quit [Ping timeout: 260 seconds]
FromGitter has joined #crystal-lang
deavmi has quit [Read error: Connection reset by peer]
deavmi has joined #crystal-lang
<FromGitter> <wyhaines> I have a bunch of market pricing related code that I have been thinking of writing into Crystal. 👀
<FromGitter> <wyhaines> @mwlang In the third version, if you look at what you get from the symbol.partition.reject chain, for example, on "BTCUSDT", you get this: `{"BTC", "USDT"}` ⏎ ⏎ Then you then call `map`, on the array, which means that each individual element of the array is getting passed into the block. Those are Strings, so `market[0]` returns the first letter, and `market[1]` returns the second.
<FromGitter> <wyhaines> @mwlang Replace your `map` with `tap` and you will get what you want.
sagax has quit [Remote host closed the connection]
<FromGitter> <mwlang> @wyhaines I'm loving Crystal for automated market trading. I'm working on translating a HFT bot from Ruby to Crystal and so far, it's blowing what I could do in Ruby out of the water.
<FromGitter> <mwlang> Doesn't `#tap` return what it opens with? At least in Ruby it does. [1,2,3,4].tap{|t| puts t} => [1,2,3,4]
<FromGitter> <mwlang> hmmm...using tap just returns an Array of Arrays with two strings in it.
<FromGitter> <mwlang> ok, I think I get what's going on at least in 3rd case. Since I knew I was going to have an Array with two strings in it, I was really trying to do the Ruby thing of having as many block arguments as elements and have those auto-map into the argument variables. That doesn't happen here. we actually iterate over each element (string) in the array and, as you pointed out, pull first and second character of the
<FromGitter> ... strings out.
djuber has quit [Remote host closed the connection]
sagax has joined #crystal-lang
<FromGitter> <mixflame> I'm not sure if I posted this yet but here's the link to my app which benefits from Crystal. https://apps.apple.com/us/app/globalchat-draw/id1525630738?mt=12
<postmodern> what do you normally do with C APIs where LibC::UInt or LibC::SizeT are returned? Should I just pass those back, or convert them to UInt32 or UInt64?
<postmodern> i also really wish crystal doc would include the inferred types in method signatures
<FromGitter> <Blacksmoke16> that could be a big union...
<postmodern> also wish you could include literals in the return type of method signatures. like what if your method only returns true or 0.
<FromGitter> <wyhaines> @mwlang Sorry that I disappeared. I had other things in front of me. Did you get it sorted? Tap in Crystal does the same thing as in Ruby. And yeah, I guess just using tap didn't give you exactly what you wanted: ⏎ ⏎ ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f448d0849148b41c96e8e90]
tsujp has quit [Ping timeout: 246 seconds]
<FromGitter> <wyhaines> @mwlang I have a long history of writing Ruby code for various mutual fund focused sites and apps, data tables, transformations, etc....
tsujp has joined #crystal-lang
<FromGitter> <wyhaines> @mwlang Here's what you actually want for that third version:
<FromGitter> <wyhaines> ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f448ed3c3aa024ef99bf05b]
<postmodern> although tap won't let you modify structs because it makes a copy when yielded
<FromGitter> <grkek> @dscottboggs Super nice, good work on the webview
<FromGitter> <dscottboggs> thanks!
<FromGitter> <grkek> Does it support MacOS?
* FromGitter * dscottboggs crosses fingers
<FromGitter> <dscottboggs> I have no way of testing it, but supposedly, yes
<FromGitter> <grkek> I can test it
<FromGitter> <dscottboggs> and even windows, tooo
<FromGitter> <dscottboggs> go for it. I'm going to bed, but please create an issue if you can't build the samples on MacOS detailing why it doesn't build
<FromGitter> <grkek> Okay :)
<FromGitter> <grkek> Well there goes a bug in the compiler
postmodern has quit [Quit: Leaving]
oprypin has joined #crystal-lang
<FromGitter> <confact> I seems to not be able to use structs with property in the release but in normal/dev build. Is that a known issue?
<FromGitter> <Daniel-Worrall> No. Post example
<FromGitter> <confact> @Daniel-Worrall I slim down classes using struct and then it builds fine, so I am trying to find that class that went to struct and broke my build. I will post here when I find that class ^^
<FromGitter> <mtsmmp_gitlab> @Blacksmoke16 i got to solve my problem from earlier with your code
<FromGitter> <mtsmmp_gitlab> thank you!
<FromGitter> <mtsmmp_gitlab> actually what led me to my problem:
<FromGitter> <mtsmmp_gitlab> One of my classes - Home can be instantiated with or without a Config class (a class that holds some data). And this Home class is instantiated like a thousand times (for example).
<FromGitter> <mtsmmp_gitlab> Does it make a difference in terms of performance if i instantiate the Config class first and then pass it as a param to Home like a thousand times or just let Home instantiate Config always by default?
<FromGitter> <j8r> @dscottboggs right
alexherbo29 has joined #crystal-lang
alexherbo29 has quit [Ping timeout: 240 seconds]
alexherbo2 has joined #crystal-lang
zorp has joined #crystal-lang
<FromGitter> <confact> @Daniel-Worrall randomly it worked while i softly changed them back to struct 2 classes at a time. So it builds fine now. Thanks anyway.
melthelesbian has quit [Ping timeout: 244 seconds]
kevinsjoberg has quit [Ping timeout: 260 seconds]
melthelesbian has joined #crystal-lang
kevinsjoberg has joined #crystal-lang
woodruffw has quit [Ping timeout: 265 seconds]
woodruffw has joined #crystal-lang
<FromGitter> <wyhaines> @mtsmmp_gitlab If I understand you, you are asking if there is a performance difference between `config = Config.new; home = Home.new(config)` versus `home = Home.new # Home.new has Config.new in it's #initialize method`? ⏎ ⏎ There should be no difference. In both cases you are creating a new Home and a new Config. ⏎ ⏎ However, as a general practice, when you are 😕 or ❓ the speed of two
<FromGitter> ... options, write a little file to test the options with `Benchmark.ips`. [https://gitter.im/crystal-lang/crystal?at=5f44f877ddc2d041c0c704dd]
alexherbo2 has quit [Ping timeout: 240 seconds]
alexherbo2 has joined #crystal-lang
<FromGitter> <mtsmmp_gitlab> > @mtsmmp_gitlab If I understand you, you are asking if there is a performance difference between `config = Config.new; home = Home.new(config)` versus `home = Home.new # Home.new has Config.new in it's #initialize method`? ⏎ > ⏎ > There should be no difference. In both cases you are creating a new Home and a new Config. ⏎ > ⏎ > However, as a general practice, when you are 😕 or ❓ the speed
<FromGitter> ... of two options, write a little file to test the options with `Benchmark.ips`. ... [https://gitter.im/crystal-lang/crystal?at=5f44fc2e9bad075eacd6aa57]
<FromGitter> <mtsmmp_gitlab> but lets suppose that home = Home.new will be called 1000 times
<FromGitter> <mtsmmp_gitlab> it will instantiate Config.new everytime
<FromGitter> <mtsmmp_gitlab> if i instantiate Config.new first and then pass it as an argument for the 1000 calls
<FromGitter> <mtsmmp_gitlab> in theory wouldnt it be better?
<FromGitter> <wyhaines> Is my example an accurate reflection of what you are doing? In either case you create 1000 new Config objects, too? ⏎ ⏎ Or are you just creating a single Config object? Because just creating a single object is going to obviously be faster than creating a bunch of them. Creating objects is always slower than not creating object. And again....you can just benchmark. i.e. No code is faster than no code.
<FromGitter> <wyhaines> ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f44ffe836e6f709fdff8f7a]
<FromGitter> ... no code. ⏎ ⏎ that was my question exactly [https://gitter.im/crystal-lang/crystal?at=5f4501365580fa092b216381]
<FromGitter> <mtsmmp_gitlab> > Is my example an accurate reflection of what you are doing? In either case you create 1000 new Config objects, too? ⏎ > ⏎ > Or are you just creating a single Config object? Because just creating a single object is going to obviously be faster than creating a bunch of them. Creating objects is always slower than not creating object. And again....you can just benchmark. i.e. No code is faster than
<FromGitter> <mtsmmp_gitlab> yes, the idea is to create a single Config object and pass it as a param, instead of letting it instantiate a new one automatically everytime i do Home.new
<FromGitter> <wyhaines> Don't put a lot of credibility into the minute differences, though. Within the micro-variations that were tested, the difference in performance seemed to largely depend on the order that they were ran in....later examples tend to be slightly faster than earlier. ⏎ ⏎ Though in general, even taking that into account, it might be very very very very very marginally faster to have overloaded `initialize`
<FromGitter> ... methods than to have a single one provides a default @config. ⏎ ⏎ In my test script, `Home` does this: `def initialize(@config : Config = Config.new); end` While `Home2` has overloads `initialize` with one method for no config object, and one where there is one. If that is in fact faster to overload it, the difference is incredib ... [https://gitter.im/crystal-lang/crystal?at=5f4501c259ac794e02c57dd2]
<FromGitter> <mtsmmp_gitlab> great info dude!
<FromGitter> <mtsmmp_gitlab> i imagined that the difference would be small too
<FromGitter> <mtsmmp_gitlab> thank you for this
<FromGitter> <wyhaines> @mtsmmp_gitlab Your question boils down to what are really the most basic rules of performance. ⏎ ⏎ 1) No code is faster than no code. That is, all other things being equal, more code being executed takes more time to execute it. ⏎ 2) Creating objects, particularly on the Heap, isn't cheap, so the less of that which you do, the faster your code runs. ⏎ ... [ht
<FromGitter> ... tps://gitter.im/crystal-lang/crystal?at=5f4502fc49a1df0a12a67792]
<FromGitter> <wyhaines> These benchmarks only find a difference because it's doing the work tens of millions of times per second.
<FromGitter> <mtsmmp_gitlab> right
<FromGitter> <mtsmmp_gitlab> great insights dude
<FromGitter> <mtsmmp_gitlab> i have been learning a lot here :D
<FromGitter> <mtsmmp_gitlab> @wyhaines thank u so much
<FromGitter> <wyhaines> You are welcome.
<FromGitter> <wyhaines> As an interesting sidenote, here are the same benchmarks for Ruby 2.7.1: ⏎ ⏎ ```class Home ⏎ def initialize(config = Config.new) ⏎ @config = config ⏎ end ⏎ end``` ⏎ ⏎ And on balance, just for creating a bunch of objects and doing nothing else, Crystal does it about 8x-9x times faster than Ruby. [https://gitter.im/crystal-lang/crystal?at=5f4505b34823780937406fef]
<FromGitter> <wyhaines> (Playing with the ordering doesn't matter, it seems, with the Ruby benchmark -- the Config before Home is always very marginally slower, though most of the time the margin isn't as big as in the run that I pasted) ⏎ ⏎ Benchmarking for fun and profit!
<FromGitter> <mtsmmp_gitlab> wow
<FromGitter> <mtsmmp_gitlab> 8x - 9x is crazy
<FromGitter> <Blacksmoke16> if config is read from a file, you could prob use a struct which would be even more performant
<FromGitter> <mtsmmp_gitlab> yes Config is only a wrapper for config.yml
<FromGitter> <Blacksmoke16> is there a reason you're not using `YAML:Serializable`? ?
<FromGitter> <mtsmmp_gitlab> > is there a reason you're not using `YAML:Serializable`? ⏎ ⏎ i just didnt try this approach
<FromGitter> <mtsmmp_gitlab> no specific reason
<FromGitter> <Blacksmoke16> that would be the better approach than `YAML::Any` imo
<FromGitter> <mtsmmp_gitlab> hmm
<FromGitter> <mtsmmp_gitlab> ill check
<FromGitter> <mtsmmp_gitlab> anyways i should use a struct for this?
<FromGitter> <mtsmmp_gitlab> is it faster or what
<FromGitter> <Blacksmoke16> the main point its the memory is allocated on the stack, so the GC doesn't need to track it
<yxhuvud> really though, reading the yml from the file should probably take longer than the parsing unless it is really big. And config files typically only get read once, so it will likely not matter one way or the other
<FromGitter> <Blacksmoke16> they are best used when you have immutable objects, like assuming you're not changing any values after reading it from the file
<FromGitter> <mtsmmp_gitlab> its a small file and the data is immutable
alexherbo2 has quit [Ping timeout: 240 seconds]
alexherbo2 has joined #crystal-lang
<FromGitter> <alehander92> your filesystem
<FromGitter> <alehander92> can have caching i think
<FromGitter> <alehander92> reading really shouldn't be slow
<FromGitter> <alehander92> (the syscall still takes time probably)
<FromGitter> <wyhaines> I can probably find it in the docs or the code, but....does anyone know off the top of their heads if there any built in mechanism for the DB connection pooling to automatically discard (and reconnect) database connections that have failed or have been terminated? ⏎ ⏎ i.e. if there is a network failure, and connections held by the pool fail, when it comes back, and the code asks for a connection, it gets
<FromGitter> ... one that no longer works -- `#<DB::ConnectionLost:>` ⏎ ⏎ I'd love it if there's already something simple that can eject that connection from the pool and create a new one on a failure like that. [https://gitter.im/crystal-lang/crystal?at=5f452bbb9bad075eacd73946]
<FromGitter> <Blacksmoke16> does the connection not try to reconnect?
<FromGitter> <wyhaines> It does not.
<FromGitter> <wyhaines> Which is not the behavior that I expected, but is the behavior that I observe.
<FromGitter> <Blacksmoke16> might need to play with some of the retry config?
<FromGitter> <wyhaines> Once the connection fails, it is failed forever.
<FromGitter> <Blacksmoke16> you can set `retry_attempts` and `retry_delay`, by default it only tries once
<FromGitter> <wyhaines> Yeah. Hrmm. I am going to look at the code a little. I can circumvent the issue by making `retry_attempts` very high, but it'd be nice to, as part of an exception handler, be able to eject a specific connection from a pool and request another in a more declarative way, too.
<FromGitter> <Blacksmoke16> maybe worth an issue/forum thread?
<FromGitter> <wyhaines> Yeah. I'll noodle through the code a bit, and see if there is an easy/obvious approach or a simple PR that can provide the capability.
<FromGitter> <wyhaines> Then go from there.
<FromGitter> <Blacksmoke16> 👍
<FromGitter> <naqvis> @wyhaines Connection pool document states ⏎ ⏎ > The retry logic only happens when the statement is sent through the DB::Database . If it is sent through a DB::Connection or DB::Transaction no retry is performed since the code will state that certain connection object was expected to be used.
<FromGitter> <naqvis> and `retry` is the method which does what you are asking for i.e. if `DB::ConnectionLost` is raised, it will discard the connection from the pool
<FromGitter> <naqvis> so if one wants to make use of pool and ensure stale connections are discarded, one just need to stick to `DB::Database`
<FromGitter> <grkek> Is this normal?
<FromGitter> <Blacksmoke16> normal?
<FromGitter> <grkek> Yeah does that fit crystal lang syntax
<FromGitter> <grkek> does it look bad
<FromGitter> <grkek> does it look good?
<FromGitter> <naqvis> though such idiom very common in JS and Java Stream API, but haven't seen much usage in Crystal
<FromGitter> <grkek> I have seen it couple times, on live streams as well
<FromGitter> <naqvis> also its just a personal taste, i'm sure there will be many who are found of this named param idiom
<FromGitter> <grkek> Named param idiom?
<FromGitter> <grkek> I mean the function chaining
<FromGitter> <grkek> Ah excuse me
<FromGitter> <naqvis> :D
<FromGitter> <grkek> the lack of my knowledge compels me
<FromGitter> <naqvis> nah, its just too many acronyms floating around the web
<FromGitter> <grkek> named parameter idiom
<FromGitter> <grkek> this sounds stupid and not descriptive at all
<FromGitter> <grkek> method chaining
<FromGitter> <grkek> this sounds nice and descriptive
<FromGitter> <naqvis> agree
<FromGitter> <naqvis> but then someone will argue that this is Builder pattern :P
<FromGitter> <grkek> @Blacksmoke16 already did
<FromGitter> <naqvis> but yeah, stick to what clicks
<FromGitter> <grkek> Have you tried Grip?
<FromGitter> <naqvis> not into web development
<FromGitter> <naqvis> but previously had looked into Athena and I'm already sold :P
<FromGitter> <grkek> You should take a look at grip as well
<FromGitter> <grkek> not as good as athena
<FromGitter> <grkek> but much more user friendly
<FromGitter> <naqvis> tbh i'm not into Sinatra style approach
<FromGitter> <grkek> its not
<FromGitter> <grkek> synatra style
<FromGitter> <grkek> where do you see synatra style at all?
<FromGitter> <naqvis> might be due to my previous background in other languages
<FromGitter> <grkek> sinatra*
<FromGitter> <naqvis> isn't it derived from Kemal?
<FromGitter> <naqvis> and isn't Kemal sinatra style?
<FromGitter> <grkek> It is but sinatra style is absolutely removed from grip
<FromGitter> <naqvis> awsome 👍
<FromGitter> <grkek> its more of a Phoenix framework style
<FromGitter> <dscottboggs> @grkek I like the method-chaining syntax
<FromGitter> <grkek> routing\
<FromGitter> <naqvis> that's cool
<FromGitter> <grkek> thank you @dscottboggs me too !
<FromGitter> <grkek> plus that it is the fastest framework out there
<FromGitter> <grkek> after h2o.cr which is a c bindings framework
<FromGitter> <grkek> that doesn't count
<FromGitter> <grkek> :p
<FromGitter> <grkek> > plus that it is the fastest framework out there ⏎ ⏎ for crystal ofc
<FromGitter> <dscottboggs> Although... I don't really like the names you chose for the methods
<FromGitter> <grkek> > Although... I don't really like the names you chose for the methods ⏎ ⏎ I copied them from phoenix since I like the names in there + I am more used to them
<FromGitter> <dscottboggs> oh I see
<FromGitter> <grkek> Which one's dont you like?
<FromGitter> <naqvis> route mapping seems like a sinatra++
<FromGitter> <grkek> Route mapping?
<FromGitter> <naqvis> why not follow some other framework approach of defining routes in separate file
<FromGitter> <naqvis> like Play Framework, or golang Revel
<FromGitter> <grkek> it is in a sepparate file
<FromGitter> <grkek> isn't it?
<FromGitter> <naqvis> ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f453a2bd4f0f55ebbdd9829]
<FromGitter> <grkek> Yes and?
<FromGitter> <grkek> I can show you a structured grip app
<FromGitter> <kalinon> Anyone know how to stream a large file and get a `Digest` from it? since we cant `File.read` a file like over 2GB. I went through the docs but didnt see a way to pass a `IO` to https://crystal-lang.org/api/0.35.1/Digest/Base.html
<FromGitter> <grkek> @naqvis https://github.com/grkek/bakuriani
<FromGitter> <grkek> here you go
<FromGitter> <naqvis> by separate file i was referring to definition of routes in stand-alone file like the frameworks I cited above. For example golang revel ⏎ https://github.com/revel/examples/blob/master/booking/conf/routes
<FromGitter> <grkek> No
<FromGitter> <grkek> that looks horrible
<FromGitter> <naqvis> doesn't that answer your questions which you asked earlier on how can one find all of the routes defined?
<FromGitter> <naqvis> :P
<FromGitter> <grkek> all of the routes defined are still in the one text file
<FromGitter> <grkek> it is in the initialize function of the application
<FromGitter> <grkek> 1) that the pipelines error handlers and filters are there as well
<FromGitter> <grkek> which gives you easy access to everything
<FromGitter> <naqvis> @kalinon you can define a new class by wrapping the `IO`, and have digest updated in `read` function and once all data has been streamed, you should have final digest
<FromGitter> <kalinon> i tried, but it still throws the large int error. i think digest caches all of data. https://github.com/crystal-lang/crystal/issues/3209
<FromGitter> <kalinon> ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f453c0789cf2d584b7619df]
<FromGitter> <grkek> @naqvis Youre still mocking my framework calling it sinatra
<FromGitter> <grkek> This is your project on kemal/sinatra
<FromGitter> <Blacksmoke16> thats just bad organization on their part
<FromGitter> <Blacksmoke16> could have had separate files for all the routes
<FromGitter> <grkek> I mean if the framework enforces classes to do so you will just somehow figure it to separate into files.
<FromGitter> <Blacksmoke16> i mean you dont need classes for kemal
<FromGitter> <naqvis> @grkek I didn't mock, i just stated that due to my previous background i'm not fan of sinatra style approach. Also i'm not doing any web development in Crystal, so haven't explored all available options
<FromGitter> <grkek> Aight man aight
<FromGitter> <grkek> I want to force grip on people
<FromGitter> <grkek> just shove it up into ppls faces
<FromGitter> <naqvis> :P
<FromGitter> <grkek> I like forcing people yeah its cool yeahhhh
<FromGitter> <grkek> Im joking ofc
<FromGitter> <naqvis> for sure will give it a try, should i need to do some web work in crystal
<FromGitter> <grkek> ayy nice to hear thank you mate
<FromGitter> <naqvis> definitely good to have multiple options
<FromGitter> <grkek> true that ;)
<FromGitter> <dscottboggs> @kalinon why are you using such a large chunk? what happens if you set it to a reasonable size like 4k or something?
<FromGitter> <dscottboggs> > Im joking ofc ⏎ ⏎ You might be joking but you do talk a lot about Grip :p
<FromGitter> <kalinon> same error. that snipit is wrong i added an extra zero
<FromGitter> <dscottboggs> oh ok
<FromGitter> <kalinon> yeah i think i did 256mb
<FromGitter> <dscottboggs> that seems problematic
<FromGitter> <kalinon> ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f454193a5788a3c29b08e61]
<FromGitter> <grkek> I am obsessed with Grip a lot
<FromGitter> <dscottboggs> > 256mb ⏎ ⏎ That's still pretty big
<FromGitter> <dscottboggs> oh... https://github.com/crystal-lang/crystal/blob/b2b4a8bdc7df81d2a37985b3cc0aefce0a1f336b/src/digest/sha1.cr#L40-L45 ⏎ ⏎ Looks like this code was written before the overflow protection and would've provided a better error
<FromGitter> <kalinon> yeah, writing up a bug. :D
<FromGitter> <naqvis> @kalinon believe it should work ⏎ ⏎ ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f45428049a1df0a12a7395b]
<FromGitter> <kalinon> let me try that
<FromGitter> <naqvis> sure
<FromGitter> <grkek> what does ... do?
<FromGitter> <naqvis> range operator
<FromGitter> <naqvis> 1) ..count
<FromGitter> <naqvis> `...` excludes the count
<FromGitter> <naqvis> so its same as `0..count-1`
<FromGitter> <grkek> ah thats super cool
<FromGitter> <grkek> I hate javascript
<FromGitter> <naqvis> :P
<FromGitter> <dscottboggs> @kalinon it seems this is also a bug with regards to the max message size of SHA-1 hashes. https://stackoverflow.com/questions/20215646/why-is-the-maximum-message-size-of-the-sha-1-hash-function-264-1-bits
<FromGitter> <kalinon> interesting
<FromGitter> <naqvis> @dscottboggs and also its deprecated in crystal ⏎ | Warning: SHA1 is no longer a cryptographically secure hash, and should not be used in security-related components
<FromGitter> <kalinon> yeah and everywhere
<FromGitter> <kalinon> so is MD5
<FromGitter> <dscottboggs> that doesn't mean that it's not useful for non-security related stuff
<FromGitter> <grkek> Well you calculate a hash
<FromGitter> <grkek> for a file
<FromGitter> <grkek> so it is secure enough I think?
<FromGitter> <kalinon> but. tell that to jfrog/artifactory
<FromGitter> <naqvis> hahaha
<FromGitter> <naqvis> :D
<FromGitter> <grkek> what happened to them?
<FromGitter> <dscottboggs> > so it is secure enough I think? ⏎ ⏎ It depends on the application
<FromGitter> <kalinon> they use SHA1 and MD5 in their artifact identification. I made an artifactory api shard, but it just exploaded when i went to upload a `rpm` our devs made thats 3gb
<FromGitter> <kalinon> im real shit when it comes to OpenSSL and Digest algs. eventually i should learn em
<FromGitter> <kalinon> opened a bug here: #9694
<DeBot> https://github.com/crystal-lang/crystal/issues/9694 (Cannot digest large data. Throws Arithmetic overflow (OverflowError))
<FromGitter> <grkek> I used MD5 for file sepparation
<FromGitter> <naqvis> this isn't a bug imo
<FromGitter> <naqvis> your code has flaw where it was assuming that read will always return the buffer size, but that's not the case
<FromGitter> <dscottboggs> TBH I would use either MD5 (fast, not secure) or SHA2 or 3 (slower, more secure) or Scrypt for passwords. I don't really see the point of SHA1 these days for anything, since if you don't need to protect against hash collision then just use faster MD5
<FromGitter> <naqvis> read returns the number of bytes read
<FromGitter> <naqvis> so always put a check on that
<FromGitter> <naqvis> 0 means eof
<FromGitter> <dscottboggs> OH! that's important
<FromGitter> <dscottboggs> but there's still a bug
<FromGitter> <dscottboggs> that's just memory corruption, wouldn't throw
<FromGitter> <kalinon> @naqvis your snipit gave me the same error D:
<FromGitter> <naqvis> Rip
<FromGitter> <dscottboggs> I would revise your example to avoid the memory corruption
<FromGitter> <kalinon> at the end of the day i really want to be able to do this quick and easy via crystal. as i can do it via `sha1sum` or `md5sum` without loading the full 3gb file and not taking forever.
<FromGitter> <naqvis> quick look at the code doesn't reveal that it stores all of the read data
<FromGitter> <dscottboggs> no, it's an overflow when tracking the size
<FromGitter> <naqvis> it stores the hash in 64 bytes Static Array and that's all
<FromGitter> <kalinon> yeah cuz its still Int32
<FromGitter> <grkek> Wouldn't it be a bit better to accumulate
<FromGitter> <dscottboggs> but the wrong error is being thrown and the size is an Int32 instead of SizeT
<FromGitter> <grkek> start middle and end bytes
<FromGitter> <grkek> and calculate the sha that way
<FromGitter> <dscottboggs> which is something that *really* irks me about Crystal.
<FromGitter> <grkek> instead of loading the entire fucking file
<FromGitter> <dscottboggs> FFS @grkek that's a terrible idea
<FromGitter> <kalinon> @grkek dont know enough about it, i leave things for crypto to smarter people
<FromGitter> <kalinon> @dscottboggs what that Int32 is default?
<FromGitter> <grkek> :DDDD
<FromGitter> <grkek> I know @dscottboggs
<FromGitter> <grkek> I used to work in cyber sec
<FromGitter> <dscottboggs> yes, there are tons of places in the stdlib where sizes are represented as `Int32` which makes absolutely *zero* sense
<FromGitter> <kalinon> yeah ive hit that error many o' time
<FromGitter> <naqvis> yeah `Int32` is default size
<FromGitter> <dscottboggs> I get limiting it to 32 bits for consistency across platforms (although I disagree), but *signed*?? Why?
<FromGitter> <naqvis> might be they were targeting for 32-bit arch
<FromGitter> <dscottboggs> let me just new up this `Bytes` of size `-420` lol
<FromGitter> <naqvis> for kalinon reported bug, its `UInt32`
<FromGitter> <dscottboggs> oh
<FromGitter> <dscottboggs> I got a little carried away there anyway haha
<FromGitter> <naqvis> :P
<FromGitter> <dscottboggs> wait why am I not submitting a PR to fix this right now?
<FromGitter> <grkek> holy fuck C is a horrible language
<FromGitter> <dscottboggs> that's super easy
<FromGitter> <dscottboggs> @grkek meh, there are worse
<FromGitter> <kalinon> ``` @length_low = 0_u32 ⏎ @length_high = 0_u32``` [https://gitter.im/crystal-lang/crystal?at=5f454638ddc2d041c0c7efb0]
<FromGitter> <naqvis> @kalinon curious if you have tried running `shasum` on that huge file?
<FromGitter> <naqvis> is that going to work?
<FromGitter> <kalinon> running now
<FromGitter> <kalinon> i know sha1sum does
<FromGitter> <kalinon> yep shasum works
<FromGitter> <naqvis> that was quick
<FromGitter> <grkek> I wonder how does it do it
<FromGitter> <grkek> chunk by chunk sounds stupid
<FromGitter> <kalinon> yeah thats kinda why i was like "There has to be a better way"
<FromGitter> <grkek> it has to do it by entire file
<FromGitter> <kalinon> instead of loading 3gb
<FromGitter> <grkek> I mean how can one do it without loading the entire file
<FromGitter> <grkek> ?
<FromGitter> <grkek> You need the sha sum of an entire file
<FromGitter> <dscottboggs> as in the examples above
<FromGitter> <kalinon> depends on how the alg works.
<FromGitter> <dscottboggs> read a chunk at a time and call `update` on the hash with the chunk
<FromGitter> <grkek> That makes no sense
<FromGitter> <grkek> how can you call an update on the chunk
<FromGitter> <grkek> while not having the previous chunk
<FromGitter> <grkek> and only having a sha sum
<FromGitter> <grkek> ?
<FromGitter> <dscottboggs> while (count = f.read(slice)) > 0 ⏎ ⏎ ``` ctx.update slice[...count] ⏎ end``` [https://gitter.im/crystal-lang/crystal?at=5f4546d349148b41c970923e]
<FromGitter> <kalinon> it calculates it via stream of the data
<FromGitter> <grkek> Ohhhh
<FromGitter> <grkek> thats interesting
<FromGitter> <kalinon> unsure how. im not a wizzard
<FromGitter> <grkek> It must store previous chunks somewhere
<FromGitter> <dscottboggs> nope
<FromGitter> <dscottboggs> hashes can be calculated as a stream
<FromGitter> <grkek> hm
<FromGitter> <grkek> that is actually a thing
<FromGitter> <grkek> I didn't know that
<FromGitter> <grkek> Can u define the slice
<FromGitter> <grkek> as 4096
<FromGitter> <grkek> ?
<FromGitter> <dscottboggs> yes
<FromGitter> <grkek> ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f4547cea5788a3c29b0a179]
<FromGitter> <grkek> here?
<FromGitter> <grkek> have you tried it?
<FromGitter> <dscottboggs> not this particular example
<FromGitter> <dscottboggs> but the whole slice might not be filled by the file so you still have to keep track of `count`
<FromGitter> <kalinon> also, this bug is in MD5 as well.
<FromGitter> <kalinon> ill have to see if i can fix it up
<FromGitter> <grkek> have you tried this?
<FromGitter> <grkek> ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f454871c3aa024ef99de448]
<FromGitter> <grkek> ?
<FromGitter> <dscottboggs> > ill have to see if i can fix it up ⏎ ⏎ Already on it, thanks though
<FromGitter> <kalinon> oh much appreciate it
<FromGitter> <dscottboggs> glad to
<FromGitter> <kalinon> @grkek yes
<FromGitter> <grkek> did it work?
<FromGitter> <dscottboggs> good god that md5 code is ugly
<FromGitter> <kalinon> @dscottboggs yeah it is. ⏎ @grkek same error
<FromGitter> <dscottboggs> `private def gg(a, b, c, d, x, s, ac)`
<FromGitter> <grkek> what should it raise that error tho
<FromGitter> <grkek> ??
<FromGitter> <kalinon> because the internal count of data size goes above the max of Int32
<FromGitter> <kalinon> i believe
<FromGitter> <dscottboggs> it should raise an error when the size goes above `(1 << 64) - 1`
<FromGitter> <dscottboggs> but a different one
<FromGitter> <kalinon> the message too long one?
<FromGitter> <kalinon> also it takes `7.47s user 0.85s system 97% cpu 8.528 total` for me to sha1sum via the cli. so theres gotta be a better way lol
<FromGitter> <dscottboggs> yes
<FromGitter> <dscottboggs> I'm not messing with that md5 implementation lol
<FromGitter> <naqvis> @kalinon try this one, here using implementation from OpenSSL instead. ⏎ ⏎ ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f454ae6c3aa024ef99def10]
<FromGitter> <naqvis> funnily you might receive some magnitude of warnings, telling you that `Warning: Deprecated OpenSSL::Digest#digest. Use `final` instead.`
<FromGitter> <naqvis> but if you follow that, you will receive error stating that there is no `final` method available on `DigestIO` :P
<FromGitter> <kalinon> @naqvis that worked
<FromGitter> <naqvis> hurray
<FromGitter> <kalinon> awesome
<FromGitter> <dscottboggs> I think they're storing the number of bits and number of bytes in a `UInt32[2]` but this is the code to update it ⏎ ⏎ ```code paste, see link``` ⏎ ⏎ `inLen >> 29` makes me think this implementation is fundamentally dependent on being 32-bits [https://gitter.im/crystal-lang/crystal?at=5f454b449566774dfe30aa77]
<FromGitter> <naqvis> so crystal provided implementation is flawed
<FromGitter> <kalinon> agreed
<FromGitter> <naqvis> you better stick to `OpenSSL` one
<FromGitter> <dscottboggs> yes in that it uses `u32` on 64-bit systems. fine other than that afaik though
<FromGitter> <naqvis> true, but sticking to `OpenSSL` one is good as one can quickly change the algo
<FromGitter> <kalinon> yeah least this is usable workaround until we get the crystal ver fixed up
<FromGitter> <naqvis> its just `OpenSSL::DigestIO.new(f, "SHA1")`
<FromGitter> <naqvis> where it can be changed to `SHA256` or `SHA512` and no other change required
<FromGitter> <kalinon> ` io = OpenSSL::DigestIO.new(f, "MD5")` works as well
<FromGitter> <naqvis> i don't think they will fix that bug, as `Digest::SHA1` is already deprecated
<FromGitter> <naqvis> so don't put that much hope in that
<FromGitter> <naqvis> yeah
<FromGitter> <naqvis> so my suggestion is to stick to `OpenSSL` one
<FromGitter> <kalinon> i agree. glad i can unblock this without needing to using a CLI call.
<FromGitter> <naqvis> Crystal is awsome :P
<FromGitter> <kalinon> yes it is
<FromGitter> <dscottboggs> @kalinon could you try my fix on your large file? https://github.com/dscottboggs/crystal/tree/fix/overflow-in-sha1
<FromGitter> <kalinon> yeah ill give it a try
postmodern has joined #crystal-lang
<postmodern> so why do we have to specify @[Raises(...)]? Why can't the crystal compiler or doc generator scan the methods and look for `raise MyError.new(...)`?
<FromGitter> <naqvis> postmodern `Raises` annotation is for `lib fun` only
<FromGitter> <naqvis> and Crystal compiler has no way of knowing if FFI is going to raise some exception
<FromGitter> <dscottboggs> specifically, it's for passing Crystal `Proc`s as function pointers to C, and raising from within the proc
<FromGitter> <dscottboggs> it's true that static analysys could probably detect this, but I imagine it would slow down compile times by a lot
<FromGitter> <dscottboggs> and it's not super common that you want to do that anyway
<FromGitter> <naqvis> :plus1:
<FromGitter> <kalinon> @dscottboggs its still running. should i have passed it `-D bits64`?
<FromGitter> <dscottboggs> no
<FromGitter> <kalinon> k cool
<FromGitter> <dscottboggs> that's defined on 64-bit systems
<FromGitter> <kalinon> k its still reading the file :D
<FromGitter> <dscottboggs> is it normal for it to take that long to compute?
<FromGitter> <kalinon> it did take it awhile to load and throw the error. at `256_000` bytes it should be around 11,718.75 read loops? if i did that math right
<FromGitter> <kalinon> im around 12000
<FromGitter> <kalinon> good ol crystal play counting them up
<FromGitter> <naqvis> implementation is quite naive as it does that on byte by byte basis
<FromGitter> <naqvis> so I would still recommend to stop using this implementation and better stick to openssl lib implementation
<FromGitter> <dscottboggs> > good ol crystal play counting them up ⏎ ⏎ Writing to stdout will slow things down
<FromGitter> <kalinon> yep
<FromGitter> <kalinon> compiling it now as release just to test
<FromGitter> <kalinon> @naqvis imo we should just sub the crystal imp for the openssl
<FromGitter> <naqvis> agree
<FromGitter> <naqvis> and future version might be going that path
<FromGitter> <dscottboggs> being linked with OpenSSL causes difficulties when using Crystal on different platforms since OpenSSL is released frequently
<FromGitter> <kalinon> yeah thats the only downside
<FromGitter> <kalinon> but the speed difference is huge
<FromGitter> <dscottboggs> Ideally I think we should be trying to avoid increasing dependency on linked libraries in general. What happens if Manas falls on hard times, and the language can't be frequently updated? Many platforms would quickly fall away. This hurts deployment on new targets as well, because it makes things harder to set up in the first place
<FromGitter> <naqvis> @dscottboggs but crystal is already relying on openssl
<FromGitter> <dscottboggs> only for some parts
<FromGitter> <kalinon> @naqvis sorta
<FromGitter> <kalinon> it can be compiled without
<FromGitter> <dscottboggs> And I'm just saying we shouldn't *increase* dependency on that, by taking away features that OpenSSL has
<FromGitter> <naqvis> true, but one won't be able to use HTTP, OAuth etc
<FromGitter> <dscottboggs> in the hopes that in the future the stdlib version catches up with OpenSSL and we can avoid linking OpenSSL someday
<postmodern> naqvis, ah! now i feel stupid, i've been using them everywhere :/
<FromGitter> <kalinon> @dscottboggs unsure if we got an infinite loop here. no mem leak but its been going for awhile (the compiled ver)
<FromGitter> <dscottboggs> I'll have to write a spec then. Should be able to just pass random data to it and see if the overflow check is failing somehow.
<FromGitter> <naqvis> postmodern :P
<FromGitter> <kalinon> can do a `dd if=/dev/zero of=file.txt count=1024 bs=3765620` to give it a test
<FromGitter> <kalinon> to make a large file
<FromGitter> <naqvis> @kalinon nah i think its just slow, i tried crystal implementation on file of size 500M on mac and it spinned the fan on and took long time to finish
<FromGitter> <kalinon> yeah agreed
<FromGitter> <kalinon> it takes forever
<FromGitter> <naqvis> yeah, had to kill few times as thought it got stuck in some loop, so added console output, just for sake of seeing something happen on console :P
<FromGitter> <dscottboggs> just had a thought...maybe it could be parallelized? since this is compute intensive that's probably a big difference
<FromGitter> <dscottboggs> running a test for infinite loop now
Vexatos has quit [Quit: ZNC Quit]
Vexatos has joined #crystal-lang
<FromGitter> <dscottboggs> oh boy... this is slow...too slow. like 5 seconds to overflow `@length_low` and `@length_low` needs to overflow `UInt64::MAX` times to reach the state where it will actually overflow...which means it will take 15 hours to run
<FromGitter> <dscottboggs> nope, wait....that sounded way too low. it's actually 106751991167300.64 days
<FromGitter> <dscottboggs> I don't think my laptop will still be running in 292471208677.536 years
<postmodern> is there any way to define a UInt type that allows for over/under-flowing? I kind of want to create a UInt8 that allows wrapping around, in case any arithmatic operation goes below 0 or above 255
<FromGitter> <dscottboggs> you can do that with a regular `UInt`, just use, for example `a &+ b` instead of `a+b`
<FromGitter> <dscottboggs> prefixing operators with `&` turns off overflow checking
<postmodern> crazy
<postmodern> is there a deeper reason for that syntax or is it custom just for uints?
<FromGitter> <dscottboggs> I think it's just to make it ugly so that you only use it when you know you *really* want a wrapping overflow
<FromGitter> <dscottboggs> even a UInt32::MAX + 1 size file would take 680 years on my laptop to sha1 hash with the Crystal implementation
<FromGitter> <kalinon> oh boy
<FromGitter> <asterite> the syntax was copied from swift
<FromGitter> <asterite> I think the reason for the syntax is that & is used for masking
<FromGitter> <asterite> so it's like you are doing some math and applying a mask to the result
<FromGitter> <dscottboggs> oh that's a good point, interesting
<FromGitter> <3n-k1> is there a way to link only *some* libraries statically? for instance, i wanna link gtk libs statically with everything else being dynamic
<FromGitter> <3n-k1> also uh, what's the linker search path? i'm getting missing static library errors for things that are in /usr/lib
<FromGitter> <dscottboggs> you'd probably have to compile a `.o` and run the linker command manually
<FromGitter> <j8r> You can also look at the `@[Link]` annotation
<FromGitter> <3n-k1> i've been looking at that annotation. i've got all the static files for pcre and i can't even get that to link statically
<FromGitter> <3n-k1> i can absolutely confirm that CRYSTAL_LIBRARY_PATH contains libpcre.a, because i'm setting it manually
<FromGitter> <3n-k1> setting it both with the link annotation and via an export
<FromGitter> <3n-k1> minimal example: https://play.crystal-lang.org/#/r/9l94 running with `export CRYSTAL_LIBRARY_PATH=/usr/lib64; crystal build test.cr && ldd test|grep pcre` tells me `libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fd4002ea000)`
<FromGitter> <3n-k1> is this maybe a snap thing?
<FromGitter> <3n-k1> it isn't
<FromGitter> <mixflame> with snap you need to specify your dependencies in snapcraft.yml
<FromGitter> <3n-k1> it happens with a binary i built from source
<FromGitter> <3n-k1> ohhhh, the link annotation is only for a lib? that's an issue
<FromGitter> <3n-k1> seems that statically linking single libraries is deprecated anyway
<FromGitter> <3n-k1> and annotating a lib still doesn't work
<FromGitter> <dscottboggs> is there any way to emit asm from the `crystal spec` command? I have a bug that shows up only when I run `crystal spec` not `crystal run spec/file.cr`
<oprypin> 3n-k1, people are saying that statically linking literally just breaks, even if u managed to wire it up (which is actually not so difficult)
<FromGitter> <dscottboggs> I can't even run it from a debugger this way...
sorcus has quit [Ping timeout: 260 seconds]
sorcus has joined #crystal-lang
<oprypin> https://carc.in/#/r/9l9s is there any better way to write `Lib.f(x ? pointerof(x).as(Int32*) : Pointer(Int32).null)`? it complains if I do anything less than that.
<oprypin> also wait, where the heck is the 163 coming from :D
<oprypin> oh im stupid,it's the typeid
<FromGitter> <dscottboggs> only other way I can think of to do that is `(x && pointerof(x)) || Pointer(Int32).null).as Pointer(Int32)` which isn't any better
<oprypin> seems like there's a good reason that it's complaining
<FromGitter> <mixflame> Crystal's base 64 encoding doesn't work with Dart
<oprypin> if i cast an actual Pointer(Int32?) to Pointer(Int32) then i get the typeid lol
<FromGitter> <dscottboggs> lol
<FromGitter> <dscottboggs> that's terrible
<FromGitter> <dscottboggs> @mixflame what?
<oprypin> ok now how do i do this, like, my function accepts an optional Int32 and needs to pass it as a pointer which can be null
<FromGitter> <mixflame> crystal's base64 output produces these type of errors in Dart `[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: FormatException: Invalid base64 data (at character 61)`
<FromGitter> <dscottboggs> `int ? pointerof(int) : Pointer(Int32).null`
<FromGitter> <mixflame> because it inroduces what dart thinks are illegal characters
<FromGitter> <dscottboggs> @mixflame what character is that?
<FromGitter> <mixflame> i think it's \n but I'm not sure
<oprypin> dscottboggs, well no, that doesn't work lol, remember the thing i just said about about `Pointer(Int32?)`
<FromGitter> <dscottboggs> oh
<FromGitter> <mixflame> i need to wireshark
<oprypin> mixflame, what's your code to produce base64
<FromGitter> <dscottboggs> ```(i = int) ? pointerof(i) : nullpointer```
<FromGitter> <mixflame> ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5f458a81a5788a3c29b1634f]
<oprypin> I'm **very** glad that i found this in an isolated way, could'be been such a nightmare
<FromGitter> <dscottboggs> omg yeah that's a really nasty bug
<oprypin> the thing is it works fine if a user passes a clean int32 or a clean nil
<oprypin> and then the user will once pass an actual union and omgwtfbbq
<FromGitter> <dscottboggs> @mixflame please post the byte output of the `Base64.encode` call
<oprypin> https://carc.in/#/r/9l9y yea looks like it has newlines every 60 chars
<oprypin> weird
<FromGitter> <mixflame> I will update my crystal server in a bit to use strict_encode
<FromGitter> <mixflame> dart is clearly strict about its base64
<FromGitter> <mixflame> I'm not sure how to get the byte output but I know they are newlines every 60 chars like @oprypin said
<FromGitter> <dscottboggs> oof
<oprypin> wtf even this doesnt work https://carc.in/#/r/9la0
<oprypin> jhass, do u like my gcc invocation at https://carc.in/#/r/9l9s ? i really like
<FromGitter> <dscottboggs> I thought for sure this would do it and that the problem was that `Pointer(T).null` is falsey https://carc.in/#/r/9lab
zorp has quit [Remote host closed the connection]
zorp has joined #crystal-lang
<oprypin> https://carc.in/#/r/9lad best so far - except it's not really applicable cuz combinatorial explosion from mutliple args
<FromGitter> <dscottboggs> oprypin, FWIW I thought your use of gcc to create c file to link to was briliant, but why not have just done https://carc.in/#/r/9laf
<oprypin> dscottboggs, lib functions have special semantics like auto-calling .to_unsafe and casting `nil` to pointer(that_type).null`
<FromGitter> <dscottboggs> oh I see
<FromGitter> <dscottboggs> I think you should report a bug on this one https://carc.in/#/r/9lad
zorp_ has joined #crystal-lang
zorp has quit [Ping timeout: 240 seconds]
<oprypin> maybe on this one you mean https://carc.in/#/r/9la0
<FromGitter> <dscottboggs> no, the one I linked was what I meant. But that one would work too. Maybe include both examples.
<oprypin> what exactly is a bug, though? or you mean just as a discussion
<oprypin> is *the* bug
<FromGitter> <dscottboggs> sorry, I meant an issue on github
zorp_ has quit [Ping timeout: 264 seconds]
<oprypin> new best https://carc.in/#/r/9lal
<oprypin> HA! https://carc.in/#/r/9laq /cc @dscottboggs
<FromGitter> <websymphony_twitter> Please let me know if what am trying to do is stupid. Am trying to cast a parent class instance into a child one and get an error. E.g. ⏎ ⏎ ```code paste, see link``` ⏎ ⏎ get an error that `Error: can't cast Foo to Bar` [https://gitter.im/crystal-lang/crystal?at=5f4591cf5580fa092b230a5f]
<FromGitter> <Blacksmoke16> why?
<FromGitter> <Blacksmoke16> they arent the same type
<FromGitter> <Blacksmoke16> `Bar` is a `Foo`, but `Foo` is not a `Bar`
<oprypin> websymphony_twitter, well yea this is literally against the whole premise of inheritance
<FromGitter> <dscottboggs> > https://carc.in/#/r/9laq ⏎ ⏎ WTF
<oprypin> dscottboggs, i can understand the underlying implementation issue. `x` is literally a union in memory no matter how you look at it
<oprypin> inside an `if x` it doesnt magically become not a union, crystal just knows that it's for sure one and not the other
<oprypin> but then `xx = x` performs the actual memory copy where it can form a new object that's not a union even in memory. and then it's possible to take a poitner of that
<oprypin> of course that's more of an excuse than a reason to make it impossible. crystal would just have to know to special-case this situation to 1) know that it's ok and make the type Pointer(Int32) rather than Pointer(Int32?) and 2) offset the result to skip the typeid of the union
<FromGitter> <dscottboggs> but shouldn't `(xx = x).null ? Pointer(Void).null : xx` prevent that?
<FromGitter> <dscottboggs> shit, I meant `.nil?` instead of `.null`
<oprypin> dscottboggs, https://carc.in/#/r/9las
<oprypin> this is the root of the issue really
<FromGitter> <dscottboggs> this is where I think the bug is, yes
<FromGitter> <dscottboggs> perfect minimal example
<oprypin> dang, wish i could know how to fix this
<oprypin> in the compiler i mean
<FromGitter> <websymphony_twitter> Thanks! Not sure what I was thinking, I could just simply reopen the existing class to add additional behaviour instead of inheritance. Which is what I needed in first place.
<FromGitter> <Blacksmoke16> what are you trying to do?
<FromGitter> <websymphony_twitter> I have a class Foo instance, I wanted some additional attributes on it, before serializing it to JSON
<FromGitter> <Blacksmoke16> got an example?
<FromGitter> <websymphony_twitter> yes, for example: ⏎ ⏎ ```code paste, see link``` ⏎ ⏎ I have instance of Foo -> foo, I want it to have name and age properties. But Foo definition is coming for external Lib. So my thought was to inherit from it and add additional properties. Turns out, I can simply reopen it and add whatever I want. [https://gitter.im/crystal-lang/crystal?at=5f45987edfaaed4ef50cc47c]
<FromGitter> <Blacksmoke16> gotcha
<oprypin> https://github.com/crystal-lang/crystal/issues/9698 welp this is either just a wall of text or hopefully an exciting read for someone