<FromGitter>
<anamba> it seems like this should work, but it doesn't https://play.crystal-lang.org/#/r/6tce seems to have something to do with the implementation of `Hash#has_key?`, which is nothing like what i expected
<FromGitter>
<Blacksmoke16> might also have to do with it being a class, object_ids are prob diff
<FromGitter>
<tenebrousedge> man, Crystal always has a useful macro
<FromGitter>
<anamba> @Blacksmoke16 yeah, that was it. so overriding #hash did what i was looking for in this case
<FromGitter>
<Blacksmoke16> 👍
<FromGitter>
<malkomalko> anybody know a way other than ECR to copy files that are bundled in your binary to another location? I don't want to create a separate template class for every file
<FromGitter>
<Blacksmoke16> use the `postinstall` script in shards.yml
<FromGitter>
<Blacksmoke16> and could use `Makefile` to do it?
<FromGitter>
<malkomalko> Let me take a look at that
<FromGitter>
<malkomalko> 👍
<FromGitter>
<tenebrousedge> which is the weird method that uses `===`? did I dream this?
<FromGitter>
<malkomalko> are you thinking about case?
<FromGitter>
<tenebrousedge> no
<FromGitter>
<tenebrousedge> maybe the `Enumerable` methods with regexes. `all?` `none?` and a few others
return0e_ has quit [Ping timeout: 245 seconds]
return0e has joined #crystal-lang
<FromGitter>
<jaydorsey> I’m doing something with term box/crystal bindings and seeing an error. I’m doing `w = Window.new;width = w.width; w.shutdown` to get the width of a terminal window. This works great when my program has a small about of output (i’m writing out a list of files). When there’s a large list, I get an error `tb_shutdown() should not be called twice.`
<FromGitter>
<jaydorsey> if I *don’t* call shutdown, something doesn’t seem to get GC’d because my terminal gets all screwed up (output gets shifted/mis-aligned and I have to close the window)
<FromGitter>
<jaydorsey> My question is: is there a way to ensure that only gets called once? Because it seems to be called automatically, but only sometimes
laaron has quit [Remote host closed the connection]
laaron has joined #crystal-lang
lucasb has joined #crystal-lang
ua has joined #crystal-lang
waheedi has quit [Quit: waheedi]
<FromGitter>
<Blacksmoke16> @jaydorsey if i had to guess you were manually calling shutdown, then it was also getting called when GC'd since it was in your `finalize` method
<FromGitter>
<jaydorsey> I was calling it manually, because that’s what was in the docs/examples. I didn’t have it in my finalize. I had it right at the beginning of my code. I called it every time, which only error’d when I was in a directory w/ a large number of files. If it was smaller-ish folder (100 files? I didn’t test what the limits were) it never errored
<FromGitter>
<jaydorsey> I think it’s because that Termbox::Window wrapper calls shutdown in finalize, but I wanted to call it right away, so it was getting called twice. But, fi I didn’t call it, it’s like it wasn’t getting called because output was all screwed up. Almost like the app finished executing so fast it didn’t have time to clean up
<FromGitter>
<Blacksmoke16> > I think it’s because that Termbox::Window wrapper calls shutdown in finalize, but I wanted to call it right away, so it was getting called twice. ⏎ yup
<FromGitter>
<Blacksmoke16> strange
<FromGitter>
<jaydorsey> Is it possible that finalize wouldn’t get called when using that code?
<FromGitter>
<Blacksmoke16> if that obj never gets GC'd
<FromGitter>
<jaydorsey> So, potentially if the app finishes so quickly it wouldn’t get GC’d and run finalize in that code?
<FromGitter>
<jaydorsey> Taht would explain the behavior I saw. It was pretty easy to work around. Much easier than I expected to be honest. I didn’t think I oculd call it using those TermboxBindings calls but it just worked which is great
<FromGitter>
<Blacksmoke16> yea afaik
<FromGitter>
<Blacksmoke16> but for larger files the obj would be ready for GC and auto shutdown by the finalize method
<FromGitter>
<Blacksmoke16> while others are still processing
<FromGitter>
<Blacksmoke16> got a playground link?
<FromGitter>
<malkomalko> Let me come up with one. Also, @straight-shoota thanks for the https://github.com/schovi/baked_file_system link that looks just like what I needed
return0e_ has joined #crystal-lang
<FromGitter>
<malkomalko> Nevermind, figured it out.
<FromGitter>
<Blacksmoke16> 👍
return0e has quit [Ping timeout: 244 seconds]
<FromGitter>
<mwlang> Is there a better way to define a Hash of options to pass as parameters to a method? https://play.crystal-lang.org/#/r/6tfq uses a workaround I found googling. It works, but seems hacky: "def initialize(options = {} of Symbol => Registrable)" where Registrable is an empty module
<FromGitter>
<mwlang> I know the options will be Nil, String, Int32 or Float64 so thought I might define a union assigned to a global variable that includes these, but that seems hacky, too.
<FromGitter>
<mwlang> or maybe there's a special "type" in Crystal already for Scalar?
<FromGitter>
<Blacksmoke16> just dont use a hash and pass the directly to constructor?
<FromGitter>
<Blacksmoke16> or use a namedTuple and splat it on new
<FromGitter>
<Blacksmoke16> but ofc that only works if you know the types/values ahead of time, otherwise if its all runtime then id have to think more...
<FromGitter>
<Blacksmoke16> @mwlang ^
<FromGitter>
<j8r> 👍
<FromGitter>
<mwlang> In the calling code, I would indeed have an options variable like you did in line #6 Some of the values will be literal, some computed
<FromGitter>
<mwlang> if splatted, does order of the keys matter?
<FromGitter>
<Blacksmoke16> no
<FromGitter>
<Blacksmoke16> but why bother with the hash?
<FromGitter>
<mwlang> seems to be a reasonable approach without the empty module to
<FromGitter>
<Blacksmoke16> could also make a class method like `def self.from_options(hash : Hash(Symbol, Number | String | Bool | Nil))`
<FromGitter>
<mwlang> and those options will be passed to various indicators like RSI, Bollinger, etc. so it serves as a cleaner way to pass arguments down the chain without requiring every sub indicator class take the same set of arguments
<FromGitter>
<mwlang> I like that idea.
<FromGitter>
<Blacksmoke16> structs are also an option, easier to deal with than hashes
<FromGitter>
<Blacksmoke16> im still not sold on needing the hashes in the first place. just new up some structs with the values, then can pass them around
<FromGitter>
<Blacksmoke16> if using the `record` macro they are immutable by default
<FromGitter>
<Blacksmoke16> then you would have objects for OOP like `def calculate_xx` or have default values on initialize
<FromGitter>
<mwlang> I need to read up on structs and try that idea out. part of unlearning some Ruby habits here.
<FromGitter>
<Blacksmoke16> struct is a class that is passed by reference, ideally used for immutable data
<FromGitter>
<mwlang> then it's the right choice here.
<FromGitter>
<Blacksmoke16> or even like `Hash(String, Indicator)` :shrug: depends on what you're doing exactly
<FromGitter>
<mwlang> That part comes next, I think.
<FromGitter>
<mwlang> I've got enough now to work this out with the examples in front of me. Thanks for the help!
<FromGitter>
<Blacksmoke16> np
return0e has joined #crystal-lang
return0e_ has quit [Ping timeout: 245 seconds]
jeremycw has joined #crystal-lang
jeremycw has quit [Quit: leaving]
<FromGitter>
<mwlang> I was reading some of the Crystal source code and particularly, Arrays. when defining a class with an argument, what is "T" in this case: "class Array(T)"
jeremycw has joined #crystal-lang
<FromGitter>
<mwlang> I get that "T" is the type to be held in the array. "An Array is an ordered, integer-indexed collection of objects of type T." from the docs, but I didn't quite get how Crystal relates T to a type.
<FromGitter>
<Blacksmoke16> its whatever type you pass it
<FromGitter>
<Blacksmoke16> `Array(String)` or `Array(User)` etc
<FromGitter>
<Blacksmoke16> it would be `String` and `User` in those examples
<FromGitter>
<Blacksmoke16> its just a var the is the type
<FromGitter>
<Blacksmoke16> if that makes sense...
<FromGitter>
<mwlang> so when you say foo = [] of User That's the same as foo = Array(User) ?
<FromGitter>
<Blacksmoke16> close
<FromGitter>
<Blacksmoke16> `foo = [] of User` is equivalent to `foo = Array(User).new`
<FromGitter>
<Blacksmoke16> but yea, the type of foo in either case is `Array(User)`
<FromGitter>
<j8r> The `{} of T, U` and `[] of T` are ambiguous (IMO) syntax for `Hash(T, U).new` and `Array(T).new`. I proposes to remove them but some want to keep them
<FromGitter>
<j8r> That's this type of topic that advanced user find them useful and see no problem, but beginners have confusions related to them
<FromGitter>
<Blacksmoke16> TIL `{} of T, U` works, i been doing `{} of K => V`
<FromGitter>
<j8r> ... that's sad to complexify a language and have various styles for this type of useless details :(
<FromGitter>
<Blacksmoke16> :S
<FromGitter>
<j8r> Everyone like to know why, and at the end there is no difference and we lose time...
<FromGitter>
<Blacksmoke16> :shrug: i dont care enough to defend one way or the other
<FromGitter>
<Blacksmoke16> not a big deal to me
<FromGitter>
<j8r> But having both (3 ways in fact)? That's the problem
rohitpaulk has joined #crystal-lang
<FromGitter>
<Blacksmoke16> for the benefit it brings prob not worth it
<FromGitter>
<Blacksmoke16> to have more than 1
<FromGitter>
<j8r> Yes totally @Blacksmoke16 . Each time a new beginner ask a question about this, as I am in the past, as everybody probably. I find this infortunate.
<FromGitter>
<j8r> Anyway, that's not really the philosophy of the language so...
<FromGitter>
<bajro17> Do you know any interesting project in PHP that need in crystal. I want keep practice like that
<FromGitter>
<j8r> Nextcloud – lol
<FromGitter>
<j8r> I don't think it's really the best to just translate an existing project to Crystal
<FromGitter>
<mwlang> I'm with @j8r -- I'd rather learn it once and use it always than have three ways to do the same thing, esp. when we're talking about something like this with Arrays and Hashes.
<FromGitter>
<mwlang> personally, I'd favor Array(T) over [] of T. I get where the latter comes from since Ruby allows that foo = [] is same as foo = Array.new
<FromGitter>
<mwlang> and similarly for Hashes
<FromGitter>
<r00ster91> I think this syntax should be removed: `Array(String | Int32){0, "a"}`. The other two should be kept. I like `of`'s readability
<FromGitter>
<mwlang> and truth be told, first thing I did was copy over some Ruby code and started trying to compile it as Crystal and of course one of the first errors hit upon was "foo = []" and it was fairly natural to just extend the syntax as suggested by the compiler error message.
<FromGitter>
<mwlang> I miss most having parentheses being optional on method definitions. :-p
<FromGitter>
<Blacksmoke16> they are still optional
<FromGitter>
<Blacksmoke16> `method 1` would be same as `method(1)`
<FromGitter>
<r00ster91> no he means on definitions, like `def a b c`
<FromGitter>
<Blacksmoke16> ah
<FromGitter>
<Blacksmoke16> gotcha
<FromGitter>
<mwlang> If I parse a JSON formatted text file in Ruby and there's no blank line at end of file, I get back what I expect...an array of hashes with the last item in array being the last JSON string parsed in the file. When I parse same way in Crystal, I'm encountering Unhandled exception: unexpected token 'EOF' at 1:1 (JSON::ParseException) on the last line, which apparently is seen as a blank line if I had to hazard a
<FromGitter>
<Blacksmoke16> like `File.read_lines("data.json").map { |l| JSON.parse(l) }`
<FromGitter>
<mwlang> ok, that works.
<FromGitter>
<mwlang> I have rarely used read_lines in Ruby.
rohitpaulk has joined #crystal-lang
<FromGitter>
<Blacksmoke16> well there you go :p
<FromGitter>
<mwlang> works similarly in Ruby as well...but still, seems like a bug that read with split on "\n" isn't behaving the same as #read_lines
<FromGitter>
<mwlang> Ruby gives same results both ways.
<FromGitter>
<r00ster91> does `split('\n', remove_empty: true)` fix it maybeß
<FromGitter>
<Blacksmoke16> yes
<FromGitter>
<Blacksmoke16> ruby must do that internally
<FromGitter>
<mwlang> yeah, that fixes it, too.
<FromGitter>
<r00ster91> yea crystal doesn't remove empty strings by default
<FromGitter>
<mwlang> Does Ruby? I didn't think it did.
<FromGitter>
<Blacksmoke16> it must
<FromGitter>
<mwlang> and woule remove_empty remove blank entries in middle of the file as well?
<FromGitter>
<Blacksmoke16> prob yes
rohitpaulk has quit [Ping timeout: 268 seconds]
<FromGitter>
<Blacksmoke16> `If remove_empty is true, any empty strings are removed from the result.`
<FromGitter>
<mwlang> confirmed.
<FromGitter>
<mwlang> crystal will work with empty lines in the middle of the file with remove_empty set to true.
<FromGitter>
<mwlang> Ruby doesn't have same option on it's split
<FromGitter>
<mwlang> ok, moving on. Now I just need to find a non-trivial example of using websockets to run in background with spawn and update ticker data and I have a live stock monitoring app all in Crystal. :-)
<FromGitter>
<mwlang> seen several examples of writing a websocket server in crystal, but all client side examples are Javascript.
<FromGitter>
<Blacksmoke16> couldnt just use a cron style thing for that
<FromGitter>
<Blacksmoke16> run a task every x minutes?
<jeremycw>
What are you using for the client? Anything other than javascript probably shouldn't use websockets.
<FromGitter>
<mwlang> Web browsers are not always the goal. I'm collecting data from exchanges for automated trading strategies. I've built it all in Ruby, but it's gotten too slow overall as things have progressed in complexity.
<FromGitter>
<mwlang> well, tickle me pink, what I tried actually worked after a couple of attempts. No idea if it's the "right way" but it works: https://play.crystal-lang.org/#/r/6tik
<FromGitter>
<Blacksmoke16> :shrug:
<jeremycw>
sure, but if you have control of the client and server there are better options for communication than websocket.
<FromGitter>
<asterite> the code looks good to me (and it looks simple too :-))
<FromGitter>
<mwlang> @jeremycw, unfortunately, I have no control of the servers. They offer RESTful API and Websockets and the REST interface is rate limited
<FromGitter>
<asterite> @mwlang you can also do `File.read(...).lines` instead of `File.read(...).split("\n")`. A bit more idiomatic and will return what you wanted
<FromGitter>
<asterite> (or, well, `File.read_lines` as suggested before)
<FromGitter>
<kinxer> Does anyone have a basic list of which parts of the standard library require particular shared libraries? Is it just the "recommended but not required" libraries (e.g. here (https://crystal-lang.org/reference/installation/on_debian_and_ubuntu.html)) and the classes/modules in the corresponding comments?
<FromGitter>
<kinxer> I'm trying to write some simple documentation for statically compiling Crystal binaries.
<FromGitter>
<mwlang> Thanks @asterite that's not a bad idiom there. I think I'll go with read_lines for now...it's the closest to what I need at the end of the day without having to concern myself with handling edge cases
<FromGitter>
<mwlang> Has anyone written out some of the common Crystal Idioms, yet? Google's giving lots of results for "crystal clear" but nothing for the language "crystal language idioms"
<FromGitter>
<asterite> @kinxer not a list, but off the top of my head: regex (pcre), big nums (libgmp), yaml (libyaml2), xml (libxml)... then everything depends on libc, and https and stuff needs openssl. Oh, and unicode handling depends on `libiconv`. Readline depends on `libreadline`. I think that's it
<Yxhuvud>
libgc?
alexherbo2 has quit [Read error: Connection reset by peer]
<FromGitter>
<kinxer> Good to know. None of the ones for specific classes/modules (e.g. `::XML`) are automatically included when compiling statically, correct?
alexherbo2 has joined #crystal-lang
<FromGitter>
<asterite> there's libevent, libgc... but these always come with the compiler (I think?)
<FromGitter>
<mwlang> If I were to have, say 25 websocket clients spawned separately, is it safe to use just one Channel shared between them all so I can use a common routine to process the incoming data or is that going to get me into trouble with shared memory issues?
<FromGitter>
<mwlang> does the on_close event cause Websocket#run to abort and thus exit the spawn block?
<FromGitter>
<mwlang> nevermind on that last one, I found answer in the code. An error causes the run loop to attempt to call on_close and then breaks the loop.
<FromGitter>
<mwlang> also breaks specifically on a close event being received.
bmcginty has quit [Ping timeout: 250 seconds]
rohitpaulk has joined #crystal-lang
<FromGitter>
<asterite> @kinxer I'm not sure I understand the question, but I'm also not familiar with static linking, so...
<FromGitter>
<kinxer> @asterite That's fair. Basically, I've found that if you statically compile a Crystal program that uses `::XML`, you have to add a couple extra `--link-flags`. I figured that was something to be worried about with other library bindings in the standard library, but as far as I can tell that's the only one. However, if there are other packages in the standard library that depend on `::XML`, those would probably
<FromGitter>
<mwlang> Having a bit of trouble with spawning multiple websockets. Basically, it appears I'm forced to add a sleep(1) in the main program loop for the program to run at all. Without it, listeners evaluate immediately to #stopped? state. https://play.crystal-lang.org/#/r/6tm1
<FromGitter>
<mwlang> and if I move the sleep(1) to above the loop, then one loop processes fine, then I hear the CPU fan kick into high gear and program runs indefinitely, but nothing ever printed to console.
<FromGitter>
<mwlang> another thing...haven't quite figured out how to launch all the listeners with just one spawn. I need to spawn each listener's websocket for it's while loop to process incoming messages, then have to spawn the listener itself for the main program loop to connect up all listeners.
<FromGitter>
<mwlang> This variation does work without sleep(1) and I did figure out how not to double-spawn by sharing the channel and processing messages in main loop -- not quite the design I had in mind where I was intending to start processing locally inside each Listener instance, but I can make it work. Still highly curious about first variation's problem, though.
<FromGitter>
<mwlang> This is a key bit to know about channels: " Internally, a channel implements all the locking mechanisms to avoid data races, but from the outside you use them as communication primitives, so you (the user) don't have to use locks."
<FromGitter>
<mwlang> and this: " a fiber is not executed as soon as it is spawned." That's what was tripping me up and why the second spawn and loop simply exited the program.