<FromGitter>
<Blacksmoke16> dont mind too much of it, but the jist is its a listener that sets the format of the request based on content negotiation
<FromGitter>
<rmarronnier> Hey all, does anyone of you have experience with the crystal-db logger ? I'm using `DB::Log.level = :debug` and correctly get `▸ Executing query` lines in my terminal... but without the associated SQL query. How can I display it (https://github.com/crystal-lang/crystal-db/blob/master/src/db/statement.cr#L145)?
<FromGitter>
<Blacksmoke16> which seems like it would be a good way to handle this? Also would give a place to set the allowed priorities for each path
<FromGitter>
<Blacksmoke16> @rmarronnier maybe open that file in `lib/` and add a `pp` to see what `command` is?
<FromGitter>
<Blacksmoke16> also prob going to default that listener to disabled, just so there isnt any sudden change in behavior w/o opting in
<FromGitter>
<Blacksmoke16> @rmarronnier also may be possible that the log formatter thats being used isnt rendering context/metadata
<raz>
blacksmoke16: yup that rules format looks great. feels like that should go in an annotation like `@ART::Accept(priorities: ['json', 'xml'], fallback_format: json, prefer_extension: false)]`.
<FromGitter>
<rmarronnier> > maybe open that file in lib/ and add a pp to see what command is? ⏎ Thanks, I'll try that ⏎ >also may be possible that the log formatter thats being used isnt rendering context/metadata ⏎ Mmm... good point. Thanks for your help :-) [https://gitter.im/crystal-lang/crystal?at=5fe3de99dbb17f28c59832ad]
<FromGitter>
<Blacksmoke16> raz: instead of the yaml file like the cors stuff?
<FromGitter>
<Blacksmoke16> like what would you apply the annotation to?
<raz>
hm, i haven't seen the cors stuff yet. i was thinking like `@[ART::Accept(prio: 'json']; @[ART::Get("/")]; def get_json; ..; end` and then possibly having multiple of them for each desired format. but... i can see how that can get tricky in all sorts of ways. maybe not worth it
<FromGitter>
<Daniel-Worrall> Can we get a makefile option to build for windows
<raz>
ah! hmm yea, that should work, too. tbh i doubt it will see much use anyway. Accept-trickery is just too much of a footgun, i don't remember ever seeing it in the wild on a real API.
<FromGitter>
<Blacksmoke16> better than needing separate routes for something tho
<FromGitter>
<Blacksmoke16> or like a `/posts` and `posts.html` or whatever
<FromGitter>
<Blacksmoke16> :shrug:
<FromGitter>
<Blacksmoke16> but how you figure its a footgun, seems pretty straightforward to me
<raz>
that's because you're a framework-dev. ask your average web-dev what they know about the Accept-header ;)
<raz>
but that doesn't mean athena shouldn't have the feature. it can def be useful, even tho it's probably relatively rare. the footgun part starts with having endpoints support multiple formats (most people struggle with keeping just one format consistent), logging/debugging opacity (path alone doesn't tel what format was requested/returned) etc.
<FromGitter>
<Blacksmoke16> granted idk how often an API needs to return more than one type
<FromGitter>
<Blacksmoke16> thats where this implementation comes into play
<FromGitter>
<Blacksmoke16> same endpoint, the view obj abstracts the data/status/etc as we talked about. Then its up to the format handlers to actually handle rendering the data in the expected format
<raz>
hmm yea, looks like a nice way of doing it for some use-cases. e.g. i could in theory use that for my json:api wrapping. except... in practice i don't ever want to return it in a different format anyway ;)
<raz>
but yup, that looks clean enough for cases where it's needed to me
<FromGitter>
<Blacksmoke16> main use cases i could see are like maybe json, csv, rss, xml
<FromGitter>
<Blacksmoke16> and ofc html
<FromGitter>
<Blacksmoke16> how to exactly render those things in that format is ofc up to the dev
<raz>
yup
<FromGitter>
<Blacksmoke16> a future enhancement could be like versioning
<FromGitter>
<Blacksmoke16> he has 2 why he likes the accept header approach
<raz>
yup, key phrase `I have the luxury of controlling both the API and the primary consumer of it`
<FromGitter>
<Blacksmoke16> right, depends on what exactly you want to get out of it queryparam/custom header seem like the 2 least favorable options
<raz>
amusingly custom header is actually the 2nd most popular option after path
<raz>
many big apis (e.g. stripe) do that in addition to path-versioning, because the accept-header also has a tendency to get mangled by proxies, can mess with caching, etc.
<FromGitter>
<Blacksmoke16> :shrug:
<raz>
but anyway, none of that means athena shouldn't have the feature :)
<FromGitter>
<Blacksmoke16> saving that part for a future date
<FromGitter>
<Blacksmoke16> lib it based things off of doesnt support it so maybe by the time i go to do it they'll have done it already xD
<FromGitter>
<Blacksmoke16> lib I
<raz>
yeh, it's def a bit on the esoteric/advanced side. but then again, i don't see much else that's still missing from athena :D
<FromGitter>
<Blacksmoke16> security stuff
<FromGitter>
<Blacksmoke16> and still not 100% sure how i want to handle configuration
<FromGitter>
<Blacksmoke16> and the orm...but thats a pita ha
<raz>
hmm true, that can't hurt. although most security stuff is only relevant if you want to make it full web-framework. for an api, auth and rate-limiting are pretty much the only two
<FromGitter>
<Blacksmoke16> even an api needs to be able to restrict content to those with access
<FromGitter>
<Blacksmoke16> i.e. the authorization side of things
<raz>
yea ok, but that's a separate framework
<raz>
authena
<raz>
and it sounds like ormthena is also on your list
<FromGitter>
<Blacksmoke16> ha, should be pretty easy on the authorization side of things
<FromGitter>
<Blacksmoke16> just needs done, and the configuration thing plays into it
<raz>
yeh, i've just rolled my own with clear. but my needs are fairly simple
<FromGitter>
<Blacksmoke16> we'll get there
<raz>
💪
<FromGitter>
<Blacksmoke16> also not a huge fan of the devise gem
<FromGitter>
<Blacksmoke16> er not that one, pundit i think
<FromGitter>
<Blacksmoke16> nvm, its actually pretty close to what i was going to go with
postmodern has joined #crystal-lang
<raz>
yeh i think cancancan is the latest hot thing
<raz>
but there's no one-size-fits-all. i've had my share of devise, warden, sorcery, omniauth and what not
<raz>
never made it far beyond the hello-world examples before things got terrible
<raz>
yup, coming up with a good API is actually not that hard. problem then tends to be performance and how tightly it has to integrate with the ORM
<raz>
rails has an edge there in theory because it ships with its own orm. but in practice, the queries generated by those gems tended to be horrifying
<FromGitter>
<Blacksmoke16> oh? voter stuff doesnt really interact with it at all
<raz>
ah ok, well, i don't know symfony ;)
<FromGitter>
<Blacksmoke16> as you pass in the obj you want to validate against
<FromGitter>
<Blacksmoke16> so in theory it could be used by any framework
<raz>
hmm yea... but that's the easy case
<FromGitter>
<Blacksmoke16> whats the hard case?
<FromGitter>
<Blacksmoke16> like current user permissions?
<raz>
in my experience the one where you go "query this table only for the items that the current user is allowed to see, based on their group memberships/capabilities/subscription status and moon phase"
<FromGitter>
<Blacksmoke16> fair point
<FromGitter>
<Blacksmoke16> i actually dont know off hand what you can do to handle that
<FromGitter>
<Blacksmoke16> type safe reusable query building
<FromGitter>
<Blacksmoke16> in a non painful way
<FromGitter>
<Blacksmoke16> we use a lot of MTI at work
<FromGitter>
<Blacksmoke16> STI works well enough if each type shares the majority of the columns
<FromGitter>
<Blacksmoke16> otherwise better off with parent table with common stuff and child table for each type's specific stuff
<raz>
yup, but for ACLs the above is the way to go. MTI / polymorphism isn't required, but the auth-layer does need to tell the ORM what queries/joins to make.
<raz>
otherwise you end up with some "fetch-everything, filter in memory" construct that only works for small datasets
<FromGitter>
<Blacksmoke16> yea thats not feasible
<FromGitter>
<Blacksmoke16> sounds like a tomorrow problem :P
<raz>
yeh, just use clear for the ORM. it's really, really good
<FromGitter>
<Blacksmoke16> for an AR style at least 😉
<FromGitter>
<Blacksmoke16> was going to go for a more data mapping/repo based approach
<FromGitter>
<Blacksmoke16> has some cool benefits, but also cons ofc
<FromGitter>
<Blacksmoke16> as an endpoint, how do you test that easily?
<FromGitter>
<Blacksmoke16> well bad example as you'd just use an integration test in a test env
<raz>
yea, that's what i do, i just test the json reply
<raz>
since that's the only thing i care about anyway ¯\_(ツ)_/¯
<FromGitter>
<Blacksmoke16> one solution i can see is creating a dedicated service to wrap the DB calls, i.e. follow the repository pattern but have the implementation just be AR based
<raz>
i dunno. i just have my clear models. and call stuff from the controller handlers.
<FromGitter>
<Blacksmoke16> tada, unit test w/o needing db
<raz>
yeh, i disagree with that testing style
<raz>
i'll never understand why people go to these lengths just to abstract everything away that makes their tests meaningful :P
<FromGitter>
<Blacksmoke16> oh? why?
<raz>
when testing an API i want to send a request, and verify the reply. if the reply is correct, then the stuff in between probably did the right thing.
<FromGitter>
<Blacksmoke16> ofc this is a contrived example, but its easy to see the concept and why it would be helpful
<raz>
yea, in some cases stuff like external APIs need to be mocked out, that's the hard part
<FromGitter>
<Blacksmoke16> to be clear, integration testing like that is totally the way to go for controllers
<FromGitter>
<Blacksmoke16> but unit tests are also good to have
<FromGitter>
<Blacksmoke16> in which case this pattern allows more easily getting the service into each test state
<FromGitter>
<Blacksmoke16> esp for things that your app does that *arent* part of the request/response flow
<raz>
integration tests cover these too, or they are incomplete
<raz>
make request, check that email is sent if it should, check that db record was created, check that queue job executes, verify reply.
<raz>
i.e. verify all effects that the request should have
<FromGitter>
<Blacksmoke16> im not doubting the usefulness of integration tests, they're deff the way to go most of the time
<FromGitter>
<Blacksmoke16> i just think you should not forsake unit tests entirely for them
<FromGitter>
<Blacksmoke16> like following along with the service style approach from above, if you have good test coverage on that service it can be assumed wherever it's used, it'll function properly
<FromGitter>
<Blacksmoke16> which might not all be covered in integration tests, depending on how the caller uses it
<FromGitter>
<Blacksmoke16> granted assuming you write another integration test for something else that uses it, that could find the bug as wel
<FromGitter>
<Blacksmoke16> with the downside that it may be harder to find whats actually failing :shrug:
<raz>
yeh, if it's a good unit test, i'll take it, cause why not. i just way too often see people obsess over testing that a method really gets called when they call it.
<FromGitter>
<Blacksmoke16> i think its deff possible to write *too* many tests
<FromGitter>
<Blacksmoke16> i.e. unit testing something for sake of unit testing it
<FromGitter>
<Blacksmoke16> which is a smell in of itself
<raz>
switch the dots to doc mode and make sure your suite tells a proper story
<raz>
that's even more satisfying ;)
<raz>
tho ofc only applicable when there's an actual story to tell, like in a business app. for a framework like athena it would be a rather abstract and technical tale ;)
<FromGitter>
<asterite> There are the more precise bits32 and bits64 flags
<FromGitter>
<watzon> Oh nice, even better. Another question. I'm trying to use `sizeof` in a macro to check the cumulative size of the generic splat arguments, but it won't work because `sizeof` only works on constants. Any ideas? This is what I'm trying now: ⏎ ⏎ ```code paste, see link``` [https://gitter.im/crystal-lang/crystal?at=5fe463b7de608143153ebb0e]
<FromGitter>
<watzon> Something like this works, but it seems hacky `{{ @type.type_vars[0].type_vars.map { |v| "sizeof(#{v})".id } }}.sum`
<FromGitter>
<naqvis> how about `instance_sizeof` ?
<FromGitter>
<watzon> Oh I didn't know that was a thing
<FromGitter>
<watzon> Ahh not what I was hoping for. I need the size of the type, not the size of the instance
<FromGitter>
<watzon> `instance_sizeof` still requires a type definition as well
<FromGitter>
<naqvis> | Returns the instance size of the given class as number of bytes. ⏎ | See sizeof for determining the size of value types.
<FromGitter>
<bararchy> Do we have anything like IO.select(socket1, socket2) to get an array of read to read from sockets?
<hightower2>
When using -Dpreview_mt, does one need to start at least 1 worker per CPU core to have fibers be on multiple cores, or not?
<hightower2>
(by worker I mean Thread)
<FromGitter>
<naqvis> no you don't. Scheduler takes care of that
<FromGitter>
<naqvis> though you can ask the scheduler to run fibers in same or different(default option) threads
<hightower2>
right, I referred to the forum post you linked recently, thanks
<hightower2>
On a different topic, how about this idea -- I often change shard.yml to point to my local versions of dependencies. But then I need to revert it back to official/internet locations before committing to git. Can we make crystal search for e.g. shard.yml.local first? This would make this possible without committing the .local file to git.
<hightower2>
Doing @x, @y = 1, 2 in the shown case fails, while doing it separately in 2 lines works
<oprypin>
hightower2, from the code i see that you probably realize that you should just delete that assignment and that's the right way to do it. but just saying.
<hightower2>
Yes but this is because I want the function to set instance vars
<oprypin>
you're setting them inside `initialize`
<hightower2>
I assign inside initialize() just to get around the issue of indirect initialization not being supported
<hightower2>
(and I don't want to use getter!)
<oprypin>
i dont quite understand, is there anything preventing you from having such a function without the ivar assignment
<hightower2>
yes, I want the function to set those variables. If I do it without ivar assignment, then I need another function which will do the assignment
<hightower2>
the point is... this function will be called many times to (re)set the values, not just once in initialize
<hightower2>
and I want to just call find_out() every time, and not do: @x, @y = find_out()
<oprypin>
regarding your question if it's a bug, yea it's an interesting one
<hightower2>
I'll file a bug report
<hightower2>
thanks for the discussion, as always
<oprypin>
if there was ever a quiz on compiler knowledge, explaining it should be in it
<oprypin>
straight-shoota, in the array assignemt pull request you show that you know what code something expands to. is there any easy way to print that?
<FromGitter>
<christopherzimmerman> Is LLVM8 still the default for Crystal?
<straight-shoota>
oprypin, oh right. I even tried it with a type name and it was in the results, so I didn't look further :D
<straight-shoota>
I don't think there is a direct way to print the "final" code. Those transformations happen in different semantic visitors like literal_expander and normalizer
<straight-shoota>
You can look at the implementations or specs
<straight-shoota>
IIRC there was some idea to add an --emit option that prints the final code right before the codegen phase
<straight-shoota>
that shouldn't be hard to to, actually
<hightower2>
Regarding another way of getting around indirect initialization not supported, I thought of this: https://carc.in/#/r/a5zg , but seems testing the var with || is prevented. Would anyone know if this is a hard limitation (one basically identical to having indirect initialization), or some provisions could be made for this to work?
<straight-shoota>
@christopherzimmerman I think LLVM 10 should be considered standard, but a lot of older versions are supported
<hightower2>
Is there a faster way to write multiple args to an IO other than args.each { |a| io << a } ?
<FromGitter>
<Blacksmoke16> did you try `io << args`?
<hightower2>
if I do that it prints me e.g. "{1,2,3,4}" (the tuple that was passed as *args to the method)
<FromGitter>
<Blacksmoke16> what about `args.inspect io`
<FromGitter>
<Blacksmoke16> otherwise what you have is prob fine
<hightower2>
{1, 2, 3, 4}
<hightower2>
ok, sticking with the each method, thanks
ua_ has quit [Ping timeout: 246 seconds]
postmodern has joined #crystal-lang
ua_ has joined #crystal-lang
woodruffw has quit [Ping timeout: 256 seconds]
<FromGitter>
<watzon> Who was it the other day that told me about their project for packing and unpacking bytes into structs?
woodruffw has joined #crystal-lang
<FromGitter>
<Blacksmoke16> @j8r
<FromGitter>
<watzon> Ahh yeah
<FromGitter>
<watzon> I guess I was remembering crystalizer as being something it's not. I'm looking for a more user friendly way to pack and unpack binary data.
<FromGitter>
<watzon> I'm working on something right now that's kind of like Python's struct, but more type friendly
<FromGitter>
<watzon> I know that ByteFormat exists, it's just not a very convenient API. The goal would be to wrap it in something a little more user friendly.
<FromGitter>
<Blacksmoke16> ah
<FromGitter>
<Blacksmoke16> :shrug:
<FromGitter>
<watzon> I'm working with RPC packets, each of which (and there's 1000s) needs to be able to be packed and unpacked from a byte slice (which is what gets sent over the network). The library I'm basing mine off of is written in Python, so they use a ton of `struct.pack` and `struct.unpack`. I want to do something a bit more Crystal-esque though, so I'm thinking when I generate the types I can just have each field be
<FromGitter>
... annotated and have a different library do all the byte magic.
<FromGitter>
<asterite> It's in the spider gazelle repo so it must be excellent
<FromGitter>
<watzon> Aha! I knew I'd seen something like this before
<raz>
namedtuples as return values seem to prone to cause seg faults 😓
<raz>
can't really condense it down to a small sample, but seems like something gets confused when two methods on a class return tuples that differ only in a value-type (e.g. `def a : { foo: ClassA }` and `def b : { foo: classB }`)
<FromGitter>
<Blacksmoke16> Use structs
<raz>
when i change the return-type on one of them to anything else, the segfault goes away
<raz>
yea that's what i'm doing now. was a bit wary of those tuples anyway, just surprised they crash so hard
<FromGitter>
<rukkiddo> > mom, it was because gnupg2 was missing ⏎ hey @straight-shoota this was the answer thanks & @Blacksmoke16 thanks for following the issue with me, now it works fine
ua_ has joined #crystal-lang
<oprypin>
raz, tried that on crystal master?
<oprypin>
indeed there's no idiomatic use of namedtuples as return types, ever, but yea...
<FromGitter>
<watzon> Sometimes I wish we had a Hash initializer that works like python's `defaultdict`. It's possible to intiaalize the hash with a block to give it the same behavior, but it's not very succinct, especially when you're doing it multiple times.
<FromGitter>
<Blacksmoke16> im also a fan of the idea if an overload takes a block it should also take a proc
_ht has quit [Remote host closed the connection]
<FromGitter>
<Blacksmoke16> but fwiw cant you do like `namespace_functions = namespace_types = namespace_patched = Hash(String, ...`?
woodruffw has quit [Ping timeout: 240 seconds]
r0bby has quit [Ping timeout: 268 seconds]
r0bby has joined #crystal-lang
<FromGitter>
<watzon> Oh that's true, good idea
hightower2 has joined #crystal-lang
<FromGitter>
<watzon> I think it would be nice to have an initializer for Hash like `Hash(String, Array(TLObject)).default` where it will try to just call `.new` on the value type if the key doesn't exist, but I don't envision something like that getting merged.
<hightower2>
Hey folks does anyone know what's the progress or status re. missing support for indirect initialization? At this point I would list it the #1 issue that's bothering me
<oprypin>
what's indirect iniaizliaztion
<hightower2>
that thing where you can't initialize a non-null variable from a method other than initialize()
<hightower2>
like, can't say "@a : Int32" and then call set_a() from initialize(), but need to do "@a = set_a()"
<oprypin>
hightower2, it's declared working as intended
<oprypin>
show your real use case and let's see what you could do there
<hightower2>
The solutions to get around this is to either say "@a = set_a", or to define "getter! a : Int32", both of which have consequences for the rest of the code
<oprypin>
hightower2, no show your real use case
<hightower2>
Any method which is intended to set values of ivars, and which I don't want to effectively have coded twice (one time in the method, and one time in the initialize() to get around this issue)
<FromGitter>
<Blacksmoke16> have you considered just not doing that
<raz>
oprypin: re: tried that on master - nope, i'm on 0.35.1 only. not too worried since it was reliably reproducible (most likely something someone already has their eyes on - and i try to avoid NamedTuples anyway, so not a big loss there for me)
<FromGitter>
<Blacksmoke16> `Error: no overload matches 'Regex.new' with types YAML::ParseContext, YAML::Nodes::Node+` rip
<raz>
blacksmoke16: can i configure the cors stuff without a config file somehow? config files are the devil
<FromGitter>
<Blacksmoke16> umm
<FromGitter>
<Blacksmoke16> can overload `def Athena::Config.load : ACF::Base`
<FromGitter>
<Blacksmoke16> and things should just work
<raz>
and feed it yaml?
<FromGitter>
<Blacksmoke16> sure
<FromGitter>
<Blacksmoke16> i dont think the structs have initializers so that would prob work :shrug:
<FromGitter>
<Blacksmoke16> still not 100% sure how i want configuration to be longer term
<FromGitter>
<Blacksmoke16> i like env vars, but you're kinda limited with them
<FromGitter>
<Blacksmoke16> i.e. like would be kinda hard to configure cors with them..
<FromGitter>
<Blacksmoke16> its a part of the config component
<FromGitter>
<mattrberry> Cleanest way to add tuples component-wise? I’d say zip + map, but zip returns an array. Could build a tuple back out of that, but that isn’t super nice. Any nicer ways to do it in a single line?
<FromGitter>
<Blacksmoke16> they type of each item have to be known at compile time, so you cant just dynamically turn an arbitrary array into one
<FromGitter>
<Blacksmoke16> the type of each item has*
<FromGitter>
<mattrberry> Yeah that was my thought too :/ Oh well
<FromGitter>
<watzon> Any idea what might be the most succinct way to do a `String#join` with a prefix for the last item? I want to join items and have the last one have "and " before it.
<oprypin>
oh..
<FromGitter>
<Blacksmoke16> prob replicate what join does then add something like `if idx == collection.size -1`
<FromGitter>
<watzon> So far I have `names = params.join(", ") { |p| p == params.last ? "and " + p :cp }`, but idk if there's a better way
<oprypin>
watzon, wait this one does ", and ", not "and "
<oprypin>
if that's fine then this is the way
<FromGitter>
<watzon> I was today years old when I learned that `join` takes a block
<FromGitter>
<watzon> Yeah that's intended. I just want the oxford comma with an "and"
<FromGitter>
<watzon> In this case I shouldn't have any repeated items, so checking the value against the list should be fine. This wouldn't work well if items in the list repeat, but then it probably wouldn't make as much sense to have an "and" anyway.
<hightower2>
nice hint about join + block
<oprypin>
watzon, hmmm could do something like `if (i += 1) == params.size`
<FromGitter>
<watzon> It would be nice if the block had an index passed to it, but that's not a bad idea
<FromGitter>
<watzon> I'm doing some codegen right now. It's crazy to see a .tl file go in, and hundreds of classes come out.
<FromGitter>
<Blacksmoke16> could do something like