« Back to channel list

#elixir-lang - 10 May 2019

« Back 1 day Forward 1 day »
[00:12:12] jegaxd26: has joined #elixir-lang
[00:35:43] jmcgnh: has joined #elixir-lang
[00:38:23] gvaughn: has joined #elixir-lang
[00:46:12] gvaughn: has joined #elixir-lang
[01:06:07] carldd: has joined #elixir-lang
[01:14:27] greengriminal: has joined #elixir-lang
[01:40:38] laut: has joined #elixir-lang
[01:51:51] tomterl: has joined #elixir-lang
[02:03:20] jegaxd26: has joined #elixir-lang
[02:34:03] uplime: I'm getting this returned by :ssl.connect: {:sslsocket, {:gen_tcp, #Port<0.5>, :tls_connection, :undefined}, [#PID<0.114.0>, #PID<0.113.0>]} is there anywhere I can see what :tls_connection and :undefined are in reference to?
[02:36:59] jegaxd26: has joined #elixir-lang
[02:39:18] jmcgnh: has joined #elixir-lang
[02:55:23] notzmv: has joined #elixir-lang
[03:20:31] carldd: has joined #elixir-lang
[03:25:10] mbuf: has joined #elixir-lang
[03:31:27] kapilp: has joined #elixir-lang
[03:35:47] greengriminal: has joined #elixir-lang
[03:37:15] Saukk: has joined #elixir-lang
[03:59:59] lauromoura__: has joined #elixir-lang
[04:07:19] mozzarella: has joined #elixir-lang
[04:14:30] mozzarella: has joined #elixir-lang
[04:45:15] ur5us: has joined #elixir-lang
[05:08:28] lauromoura_: has joined #elixir-lang
[05:12:26] jnoon2: has joined #elixir-lang
[05:35:22] za1b1tsu: has joined #elixir-lang
[05:40:06] mog: has joined #elixir-lang
[06:06:25] jegaxd26: has joined #elixir-lang
[06:07:21] gvaughn: has joined #elixir-lang
[06:36:25] mbuf: has joined #elixir-lang
[07:15:54] kyrylo: has joined #elixir-lang
[07:16:55] jeffweiss: has joined #elixir-lang
[07:24:00] serafeim: has joined #elixir-lang
[07:24:17] blahdodo: has joined #elixir-lang
[07:24:17] serafeim: good morning
[07:44:35] voltone: has joined #elixir-lang
[07:51:53] gvaughn: has joined #elixir-lang
[07:54:00] PragTob: has joined #elixir-lang
[08:02:10] lexmag: has joined #elixir-lang
[08:42:08] 94KAAC4RD: has joined #elixir-lang
[08:42:15] DTZUZO: has joined #elixir-lang
[08:42:20] ur5us: has joined #elixir-lang
[09:04:21] gvaughn: has joined #elixir-lang
[09:08:09] ur5us: has joined #elixir-lang
[09:42:09] gvaughn: has joined #elixir-lang
[10:28:14] jeffro_: has joined #elixir-lang
[10:39:17] CapNemo: good morning
[10:41:24] hypercore: has joined #elixir-lang
[10:48:34] gvaughn: has joined #elixir-lang
[10:50:38] serafeim: has joined #elixir-lang
[10:51:20] jhill: has joined #elixir-lang
[10:58:46] jhill: has joined #elixir-lang
[11:01:33] drincruz_: has joined #elixir-lang
[11:05:19] lexmag: has joined #elixir-lang
[11:06:38] netrino: has joined #elixir-lang
[11:31:40] drincruz_: has joined #elixir-lang
[11:55:53] nickjj: googling is coming up empty here, is there a way to convert an atom to a struct name? so something like :user would become User (and be usable when calling struct(User, attrs))
[11:56:11] gvaughn: has joined #elixir-lang
[12:03:23] nox: nickjj: Why do you do that? User is already an atom (:"Elixir.User")
[12:04:10] nickjj: nox, currently the function only accepts it in atom form
[12:05:36] nickjj: hmm, it might be working in atom form with a different strategy
[12:08:06] nox: nickjj: User *is* an atom, why not pass that?
[12:09:52] dysfun: it really is an atom
[12:10:31] nickjj: yeah, it's working now
[12:11:05] nickjj: previously i was trying to convert a map to a struct , but then i discovered Map.from_struct which makes this whole problem easier to solve
[12:12:07] nickjj: dysfun, i took what you supplied yesterday as inspiration, what do you think of this set up? https://gist.github.com/nickjj/43372c4b8750324aedcd87cff432b5cd
[12:12:40] nickjj: the overall idea is to be able to supply either a map or a struct (based on what you're doing in your tests)
[12:13:03] dysfun: ah yeah, the use of struct there is quite nice, might make our fakes return a struct and use struct to insert them too
[12:13:37] dysfun: though i note your invalid user isn't returning the User struct, just a map
[12:13:37] nickjj: then in your tests you could in theory use @valid_attrs Factory.valid(:user)
[12:13:48] nickjj: and reference that in your tests
[12:13:57] dysfun: you could. we use setup blocks
[12:14:21] dysfun: if you're going to use faker, module metadata won't work
[12:14:29] nickjj: yep the invalid returns a map , thinking i would attempt to that to pass into a post request (like updating a user's profile with an invalid email)
[12:14:30] dysfun: and it's not at all clear that's the reason why from the error
[12:15:08] nickjj: i do plan to use faker, that would be the next step
[12:15:20] dysfun: well use setup blocks instead of module metadata to declare them then :)
[12:15:21] nickjj: so i guess they need to remain as direct function calls, otherwise it'll get cached?
[12:15:37] dysfun: it's not that it'll get cached, you'll get an unintelligible error
[12:16:45] PragTob: has joined #elixir-lang
[12:17:23] dysfun: https://gist.github.com/jjl/1258067d620bcc32950ff0b921e5be77
[12:17:57] dysfun: you can localise execution of setups by putting them and the tests that want this to happen in a describe block
[12:18:04] dysfun: unfortunately you can't nest describes. ho hum.
[12:18:54] ariedler: has joined #elixir-lang
[12:18:55] nickjj: i haven't looked into using setup yet
[12:19:15] nickjj: would that automatically make tenant and member available in each test, without declaring it?
[12:19:23] nickjj: well not automatically since you set it up :D
[12:19:36] dysfun: this is the way we found that makes most sense for complex scenarios, the tag based system is too simplistic
[12:20:05] dysfun: almost. when you declare a test, you can take the context map as a parameter. this is how conncase gives you a connection
[12:20:24] dysfun: so in that map, you will have these two keys
[12:20:30] dysfun: just bind them
[12:21:22] nickjj: oh, so something like this? test "foo", %{member: member} do
[12:22:07] dysfun: exactly like that
[12:22:34] dysfun: it's your choice whether to bind them directly or just bind the map and use `.`, of course
[12:22:47] dysfun: if you're giving long descriptive names to tests, the latter might help
[12:23:12] dysfun: but it would be TOTALLY AWESOME if you could just nest describe blocks
[12:23:20] nickjj: checking the docs now, searching for "." isn't coming up with anything
[12:23:31] dysfun: %{a: :b}.a == :b
[12:26:55] nickjj: you brought up a good point with the invalid thing too, maybe you would want the struct and not just the map
[12:27:18] dysfun: in this case i think so
[12:27:23] nickjj: Factory.build(:user, Factory.invalid(:user)) -- vs -- :user |> Factory.build(Factory.invalid())
[12:27:27] dysfun: we just used maps til now cause we are always inserting them
[12:27:36] nickjj: both of these seem to return the same result (the intended struct of invalid data)
[12:27:39] dysfun: why are you centralising them in this function?
[12:27:44] drincruz_: has joined #elixir-lang
[12:27:46] nickjj: is there a better way to write that? both of them look a bit weird to me
[12:28:48] dysfun: well you could swap the argument order in Factory.build
[12:28:53] nickjj: centralizing the invalid one? it's so in tests i could do: conn = post(conn, user_path(conn, :create), user: Factory.invalid(:user))
[12:29:19] dysfun: no, why are you always using the `valid` and `invalid` functions? why not one per type?
[12:29:38] dysfun: you might want to split them across multiple modules later
[12:29:57] hypercore: has joined #elixir-lang
[12:30:08] nickjj: i'm not following
[12:30:10] dysfun: if you want to have this sort of centralisation, maybe you want to define a protocol
[12:30:28] dysfun: okay, so in our codebase, we have e.g. fake_user(), fake_member()
[12:30:55] nickjj: oh, and a member is a type of user?
[12:30:58] dysfun: at some point, when we've gotten the test suite in a better state, we are going to look at this very long file and decide to split it up
[12:31:08] dysfun: no, a member is not a type of user
[12:31:18] dysfun: don't ask :)
[12:31:20] nickjj: in this case valid and invalid aren't types of users (in the sense of admin, member, moderator), it's the whether or not the changeset would report it being valid or not
[12:31:27] nickjj: oops jumped the gun haha
[12:31:42] dysfun: yes, what i mean is, i could define fake(:user) fake(:member)
[12:31:48] dysfun: which is presumably what you're going to do
[12:32:04] nickjj: you mean fake(:admin) in my case?
[12:32:04] dysfun: but what do i gain from this apart from having to shove all the code in one file?
[12:32:56] nickjj: probably not much, i will likely have around 15 different resources
[12:32:58] dysfun: i think the correct answer to this is actually to define a protocol for creating test data of a user
[12:33:07] nickjj: meaning fake data would be generated for all of those things in 1 file
[12:33:14] dysfun: you can then implement it in as many files as you like
[12:33:21] dysfun: yes, exactly
[12:33:33] dysfun: you're at risk of having a billion line long function
[12:33:53] nickjj: you mean file?
[12:34:17] dysfun: well that too, but you're going to have to define a case of valid and invalid for every model
[12:34:25] dysfun: so you're literally centralising code for all models in two functions
[12:35:07] nickjj: while i don't have Posts in this project, i added what it would look like here: https://gist.github.com/nickjj/43372c4b8750324aedcd87cff432b5cd
[12:35:31] nickjj: adding a new resource would mean adding build(:post) and invalid(:post) functions, but the existing functions don't change
[12:35:31] dysfun: exactly. you've now added a case to valid and a case to invalid for :post
[12:35:44] dysfun: well yes they do. you added an extra case to them
[12:36:10] dysfun: in this case 5 lines of code for build(:post)
[12:36:24] dysfun: + 5 for invalid
[12:36:30] dysfun: plus some spaces
[12:36:34] nickjj: how would you get around that? the variable data is the struct name and its properties
[12:36:40] dysfun: multiply by a handful of models
[12:36:56] infowolfe: has joined #elixir-lang
[12:36:57] nickjj: it's multi-line now because realistically most of these resources will have 5-10+ attributes
[12:37:11] dysfun: you're going to have very long functions before long
[12:37:22] infowolfe: has joined #elixir-lang
[12:37:22] infowolfe: has joined #elixir-lang
[12:37:28] dysfun: well you can either make them separate functions like we did
[12:37:37] nickjj: but i can't really see getting around that, the attribute names need to be defined somewhere and in the end their values will be supplied by faker's generators
[12:37:38] dysfun: this means you're just not centralising, nor getting the benefits of e.g. having one function to call
[12:38:00] dysfun: this is acceptable to use, because we also have different required parameters etc.
[12:38:15] dysfun: you might consider a protocol if you don't need so much variation as us
[12:38:16] nickjj: in my mind calling build(:user) seemed like nicer syntax than build_user()
[12:38:30] dysfun: stop worrying about syntax, worry about the impact your changes will have
[12:39:10] dysfun: if you settle on a map for the overrides, you'll have an arity of 2 for everything
[12:39:12] nickjj: if i had build_user() instead, wouldn't the same problem exist? then that function would have 5-10+ attributes just like it does now
[12:39:26] dysfun: if you then create a protocol for making test data, you can implement it for various structs
[12:39:39] dysfun: that will compile it into one module you can call into, but your implementations can be wherever you like
[12:40:52] dysfun: ah, but you don't have the struct, you're trying to create the struct, so it would have to be e.g. Factory.build(%User{})
[12:41:23] dysfun: Factory would become a protocol with build and invalid as member functions
[12:41:26] nickjj: yep in the end i'm trying to get back a fully built and ready to go %User
[12:41:36] dysfun: right, this will allow you to do that
[12:41:58] dysfun: you just have to pass in an empty one to abuse protocol dispatch
[12:42:14] nickjj: but in your example of Factory.build(%User{}) what would it look like to situationally overwrite an attribute?
[12:42:26] dysfun: have a second optional parameter which is a map
[12:42:34] nickjj: Factory.build(%User{}, %{email: "foo@bar.com"})?
[12:42:41] dysfun: pretty much
[12:42:59] nickjj: vs Factory.build(:user, email: "foo@bar.com")
[12:43:17] dysfun: well you could have it take an optional keyword list if you like
[12:43:35] dysfun: you could even hide it behind a function and make it Factory.build(User, email: "foo@bar.com")
[12:43:52] nickjj: oh yeah, i saw that this morning when looking around , with Keyword.merge(defaults, attrs) |> Enum.into(%{})
[12:44:17] dysfun: yeah Enum works on things implementing the Enumerable protocol
[12:44:26] nickjj: where defaults is a list , which has key value pairs
[12:45:10] nickjj: i don't think i have the elixir knowledge to really understand the wins of going with your approach though
[12:45:27] dysfun: give me a moment, writing up an example
[12:45:30] nickjj: isn't there still a single build function that pattern matches on a struct now instead of an atom?
[12:49:10] dysfun: https://gist.github.com/jjl/bb943083f0f90998f9ba323cef158bc8
[12:49:23] dysfun: the difference here is that the defimpl can be in any module
[12:50:05] dysfun: but you'd be able to call Factory.build(User, %{email: ""})
[12:50:19] dysfun: Factory.build(User, email: "") event
[12:51:03] nickjj: and now my brain is melted
[12:51:30] dysfun: well look at the api it exposes, it's almost the same as yours, just I pass in the module name instead of an arbitrary atom
[12:51:49] nickjj: on your lines 14 and 19 , this is where you would set a bunch of default attributes?
[12:52:11] nickjj: so you'd duplicate that line for each thing
[12:52:25] dysfun: actually i wouldn't use this syntax myself, i'd use the Map.put_lazy i showed you yesterday and add the __struct__ key manually
[12:53:05] nickjj: so lines 13-15 gets replaced with something like...
[12:53:25] nickjj: |> Map.put_new_lazy ... whatever you had yesterday i forgot the syntax
[12:53:36] nickjj: (but i have the code saved as comments in my project)
[12:53:41] dysfun: with a new line of pipe for each property
[12:54:07] dysfun: and ending in Map.put(:__struct__, User)
[12:54:27] dysfun: although you could abstract that into the build function so it happens in one place, since you already have the atom 'mod'
[12:54:30] nickjj: ok because that was the next question i was going to ask , how do you transform that map into the struct
[12:55:09] dysfun: ah well that's where you happen to know how elixir structs are represented at runtime and abuse that knowledge ;)
[12:55:12] nickjj: so then what happens when you want fake out Posts?
[12:55:24] dysfun: you create a new defimpl for Post
[12:55:25] nickjj: do you duplicate lines 11-22?
[12:55:49] dysfun: well most of those lines, presumably the middles of the functions will be different
[12:56:19] nickjj: you would still need a defimpl Fakeable, for: Post , etc.
[12:57:04] nickjj: wouldn't this end up being even more lines of code in the end? since now you're duplicating 8 or 9ish lines of code that aren't attribute based
[12:57:05] dysfun: except you're now free to move that implementation to e.g. Myapp.Test.Factory.Post
[12:57:55] dysfun: well mine is literally two lines longer, you used at least that in separating lines
[12:58:23] dysfun: except now i can move that to another module
[12:59:01] nickjj: in your case you would need 2 more exposed functions tho right? one to get the invalid map and also the valid map
[12:59:45] dysfun: if you wanted to create a wrapper which called it and then called struct() on it, that's up to you, but you'd only need to write it once
[13:01:24] nickjj: there's a lot of things to fill in (code wise) based on what we talked about here, but
[13:02:02] nickjj: i don't know what a protocol or implementation is yet in elixir, but if you look at the problem set, your solution looks like a good fit based on what those words mean in english
[13:02:18] dysfun: protocol ~= java interface
[13:02:50] nickjj: because the problem is creating a bunch of valid and invalid fake data for different resources , and having some type of protocol to deal with that is the natural way to handle that
[13:04:24] gvaughn: has joined #elixir-lang
[13:05:25] dysfun: it's just a way to execute different code based on what type of struct it is
[13:05:36] dysfun: so for each type, you define a function for getting good and bad data
[13:06:37] dysfun: so from the top, you call build passing in a module name and optional overrides
[13:06:58] nickjj: i forked your gist at https://gist.github.com/nickjj/2d447bc5911aace7927e41dbf7ce483a -- where would that Map.put(:__struct__, User) go?
[13:06:59] dysfun: it synthesises an empty version of that struct and calls the protocol's `good` method on that empty struct
[13:07:06] nickjj: the abstracted version
[13:07:29] dysfun: well actually, you can use self, because it's already an empty struct
[13:08:16] dysfun: ah hang on, i made overrides a keyword list to match your api
[13:08:30] dysfun: so you will want to turn them into a map in the build function, because of Map.put_new_lazy
[13:08:53] kyrylo: has joined #elixir-lang
[13:09:16] dysfun: yeah, so modify build to Map.new(overrides)
[13:09:17] nickjj: i just want to make sure i can access a valid map, invalid map and the built struct without too much pain , and have a way to situationally override the build's attributes
[13:09:25] dysfun: and also at the end to Map.put(:__struct__, mod)
[13:10:16] nickjj: you lost me
[13:11:55] dysfun: you wanted the nice keyword list syntax, yes?
[13:12:23] nickjj: ideally yes
[13:12:56] dysfun: well you're using Map functions in the defimp (Map.put_new_lazy), so that won't work. so build should call Map.new(overrides) to get a map from it
[13:14:46] nickjj: so replace %{__struct__: mod} with Map.new(overrides)? your version is still like 10000% above my paygrade
[13:15:36] dysfun: https://gist.github.com/jjl/bb943083f0f90998f9ba323cef158bc8
[13:17:08] dysfun: only modified build
[13:18:04] nickjj: and use my forked version of lines 13-15? with the map lazy?
[13:18:57] nickjj: so let's say in the future, if this were all broken up into multiple files
[13:18:58] dysfun: or if you decide you can tolerate typing %{} more, you can get rid of the Map.new
[13:19:55] nickjj: the main file would be this , but with the defimpl removed and then i would end up either using or importing each of those files into this main file?
[13:20:18] dysfun: no, you would just alias Factory and call Factory.build
[13:20:21] nickjj: and defimpl gets put into its own module like Myapp.Test.Factory.User?
[13:20:35] dysfun: yes. or a module can contain a group of them
[13:20:38] dysfun: up to you
[13:20:42] dysfun: unlike before
[13:21:17] nickjj: yeah so the tests themselves only ever alias Factory , but the "implementation" can be broken up into multiple files if i choose to do so
[13:21:37] nickjj: which seems like a good design choice
[13:21:49] dysfun: right, it seems like the logical evolution of how both of us are doing things
[13:22:07] nickjj: yeah because this isn't a "what if" scenario, i will for sure have like 15 things to fake
[13:22:33] dysfun: lol don't even get me started on our really long faking file. this is why it's in the forefront of my mind
[13:23:08] dysfun: the other neat thing about a protocol based solution is i can implement it as a library and release it
[13:23:19] dysfun: whereas before, there wasn't much to gain from doing so
[13:23:27] dysfun: but protocols allow us to get a little better reuse
[13:23:32] nickjj: i have a 385 line python "faker" file and that only faked 5 things
[13:24:00] dysfun: we have quite a few models
[13:24:34] nickjj: i also forgot to mention one thing too, which would have made the old model worse
[13:24:34] dysfun: so while the move to fake data has been clearly positive, like i say, we're going to look to clean it up a bit
[13:25:02] nickjj: when testing a lot of examples had @create_attrs , @update_attrs and @invalid_attrs
[13:25:34] nickjj: it would be nice to also preset an update map (like invalid but valid attrs that differ from the original built attrs)
[13:26:08] dysfun: if you're using faker, you won't be able to do that
[13:26:16] dysfun: faker does not like being used at compile time
[13:26:32] nickjj: i was going to use faker for the good states , and manually set the updated and invalid states
[13:26:40] dysfun: then you could do that
[13:27:06] nickjj: although in theory, couldn't faker be used for updates too? it would generate a 2nd iteration of the fake data?
[13:27:15] dysfun: oh, i forgot to mention, i actually use these functions for updates too. just delete the fields you don't want and put it through again
[13:27:25] dysfun: assuming you want to update to valid data
[13:27:30] nickjj: but you also have the 1 in a million edge case where the new == old
[13:27:49] dysfun: yes, it won't always test anything, but we accept this
[13:27:59] dysfun: on the basis that we run the tests often enough it'll be fine
[13:28:23] nickjj: what would the update / valid_attrs functions end up looking line in your example btw?
[13:28:28] nickjj: since they would be maps not structs
[13:29:18] dysfun: update, you'd just delete the fields and call Faker.good()
[13:29:26] dysfun: er Factory.Fakeable.good()
[13:29:43] dysfun: valid_attrs is just good() |> struct()
[13:29:45] nickjj: there's no way the invalid one is this simple right? check line 28 https://gist.github.com/nickjj/2d447bc5911aace7927e41dbf7ce483a
[13:29:56] nickjj: i didn't run that, just edited it in the gist
[13:30:20] dysfun: why wouldn't it be?
[13:30:28] nickjj: i omit the Map.put since i figured it wouldn't be converted to a struct, it stays a map
[13:30:42] nickjj: i dunno because nothing ever works on the first attempt
[13:30:53] dysfun: oh, there is a bug though, Map.put_new_lazy takes a function, you just want Map.put in that case cause it's just a string
[13:31:43] nickjj: thanks, fixed
[13:31:52] dysfun: er Map.put_if_absent maybe
[13:31:59] dysfun: i can't remember and hexdocs is down
[13:32:23] nickjj: put, put_new and put_new_lazy
[13:32:25] dysfun: anyway, we don't use constants, so all ours are Map.put_new_lazy
[13:32:30] nickjj: i have Map opened in hex's docs from before
[13:32:39] nickjj: there is no put_if_absent tho
[13:32:50] dysfun: yeah it'll be put_new
[13:33:05] dysfun: and it'll be called put_if_absent in one of the many other programming languages vying for my attention
[13:33:17] dysfun: i think that one might be rust
[13:33:27] dysfun: whee hexdocs is back
[13:34:24] dysfun: also, you could write valid_attrs - it's all but the last line of build
[13:34:27] nickjj: so here's a stupid question
[13:34:31] dysfun: and then build could just call valid_attrs to get that data
[13:34:37] nickjj: https://gist.github.com/nickjj/2d447bc5911aace7927e41dbf7ce483a
[13:34:51] nickjj: couldn't valid just call the existing good function but not convert it to a struct?
[13:35:17] nickjj: and with faker it would in theory generate a new set of data from what the build function would call?
[13:35:42] dysfun: well no, build would call valid
[13:35:57] nickjj: i updated the gist to show what i mean btw
[13:36:36] dysfun: i see the gist, i don't see what you mean
[13:36:49] dysfun: Faker will in principle return freshly random data
[13:37:05] dysfun: it often won't, but that's why i wrote that library of utilities i showed you yesterday
[13:37:09] nickjj: you call it like this right? Factory.build(User)
[13:37:40] nickjj: so that gets us a fake user with a bunch of random fake data
[13:38:03] nickjj: but Factory.valid(User) , shouldn't that generate a new fake user with a different set of random fake data, but return a map instead of a struct this time?
[13:38:44] nickjj: so then we don't even need an extra implementation function for update , since valid would give "updated" data?
[13:39:00] dysfun: right, you just delete the keys you want it to regenerate
[13:39:25] dysfun: because what is it but a map of overrides?
[13:39:51] jeffro_: has joined #elixir-lang
[13:41:11] dysfun: you could even write update as def update(%{__struct__: mod}=model, keys \\ []), do: Factory.Fakeable.good( Map.drop(model, keys))
[13:42:27] nickjj: what would the valid/update function to call that look like? the same as invalid except you call the update impl instead of bad?
[13:42:29] dysfun: def update(model, keys \\ []), do: Factory.Fakeable.good( Map.drop(model, keys)) # better
[13:42:58] dysfun: you'll want to fix the bug where the Fakeable protocol methods default to lists instead of maps though
[13:43:12] nickjj: this is where it's at now btw https://gist.github.com/nickjj/2d447bc5911aace7927e41dbf7ce483a
[13:45:13] dysfun: https://gist.github.com/jjl/a8bd1e37d5c544f6842197779d81c9ee
[13:45:19] dysfun: there, i wrote update_valid for you
[13:46:50] nickjj: here's some trivia too, i just started to move this into a file to run and it has compile errors -- the compiler immediately said what was wrong so it was easy to fix
[13:46:54] nickjj: can you spot it with your eyeballs?
[13:46:55] either|or: has joined #elixir-lang
[13:47:05] nickjj: (i didn't notice even though i looked at it in the gist form like 20 times)
[13:47:25] dysfun: you're missing dos
[13:48:02] dysfun: also, now we're using maps, you'll want Fakeable's methods to default to empty maps for overrides instead of empty lists
[13:49:43] nickjj: so lines 25,31 and 36 -- those change from [] to %{}?
[13:49:56] nickjj: but the others stay as []?
[13:51:40] dysfun: no, on lines 12 and 17 become %{}
[13:51:48] dysfun: that is where it is a map
[13:52:01] dysfun: they are keyword lists in the others because you wanted to have the keyword list style for arguments
[13:52:55] dysfun: (it must be a map there because you are returning a struct so we convert the keyword list to a map before calling Fakeable.good
[13:53:32] nickjj: this? https://gist.github.com/nickjj/2d447bc5911aace7927e41dbf7ce483a
[13:53:57] dysfun: line 6 and 7 as well, sorry
[13:54:12] dysfun: you do not need that update function in the protocol
[13:54:30] dysfun: update_valid works regardless of struct
[13:56:26] nickjj: what about the def valid tho, that still references update
[13:56:43] nickjj: or is that meant to be deleted
[13:57:09] dysfun: why isn't that Fakeable.good ?
[13:57:24] dysfun: isn't valid just build without returning a struct?
[13:58:30] Nicd-: you've been pretty busy here today
[13:58:49] nickjj: yeah that is what valid is
[13:58:50] dysfun: yes i know, i'm putting off a task i don't want to do :)
[13:59:12] nickjj: ultimate procrastination tactic, live irc pair programming on a faker library!
[13:59:14] Nicd-: procrastination is for pros right
[13:59:24] dysfun: gotta say though, this is pretty awesome
[13:59:37] dysfun: when i showed you that code yesterday, i had no idea we'd settle on a way to make it better
[14:00:01] nickjj: that overall pattern i ended up doing stemmed from reading the ecto pdf, it had an example on making factories
[14:00:31] dysfun: i own that book but haven't gotten around to it
[14:00:54] nickjj: it was the free pdf, what's new with ecto, very short
[14:01:07] dysfun: oh, i own "the ecto book"
[14:01:58] nickjj: is this a real struct that build returns btw?
[14:02:10] nickjj: in iex it looks different than what %User{} returned in my old version
[14:02:23] dysfun: hrm? gist it?
[14:02:48] dysfun: (i think it's almost certainly __struct__ is misspelled
[14:03:44] dysfun: however, a map with an __struct__ key is literally what an elixir struct is
[14:03:48] dysfun: so yes, it's a real user
[14:04:18] nickjj: https://gist.github.com/nickjj/94459b55683cad9facec5831515d8d21
[14:04:34] nickjj: i didn't bother manually removing things from the top file , that's my real user model so far
[14:05:02] nickjj: but it comes back as a "real" Foo.Accounts.User with meta data but the new one does not
[14:05:24] nickjj: the new one looks like a map with a __struct__ attribute? that's a bit too low level for me to understand what's happening
[14:08:01] gvaughn: has joined #elixir-lang
[14:08:09] dysfun: okay that mystifies me
[14:08:46] dysfun: is ecto overloading the display version and rejecting it for not having meta, i wonder
[14:09:18] nickjj: here's the old original gist that generated the old output in the first file listed in the latest gist: https://gist.github.com/nickjj/43372c4b8750324aedcd87cff432b5cd
[14:09:31] nickjj: (ie. the good one)
[14:10:44] dysfun: https://gist.github.com/jjl/3a2272ab003457d6203c461c5e3eea9d
[14:10:59] nickjj: i think the big difference is the old version starts off as a legit struct and gets converted to a map
[14:11:07] dysfun: no, see my gist
[14:12:03] dysfun: structs literally are maps with an __struct__ key
[14:12:06] dysfun: that is all
[14:12:22] dysfun: in terms of actual runtime representation]
[14:12:34] dysfun: what's going on here is clearly display related
[14:12:34] nickjj: so an ecto struct has some extra goodies that we lost in translation since it was never there to begin with?
[14:12:40] dysfun: looks that way
[14:12:54] greengriminal: has joined #elixir-lang
[14:13:12] nickjj: also something was super surprising to me in your version of this factory
[14:13:14] dysfun: i mean i know it has the meta keys
[14:13:25] nickjj: defimpl Fakeable, for: User do
[14:13:28] dysfun: but i also know what a struct is
[14:13:55] nickjj: i thought for sure i would have had to alias User , but for: User in this case seems to act as some type of identifier, not an "object"
[14:14:17] nickjj: i changed it to for: :user , and things still work the same except now i can call it with :user instead of User
[14:14:23] dysfun: User is just the atom User, which in the case you have aliased User, will expand to the alias
[14:14:40] dysfun: er to the module the alias points out
[14:15:17] Nicd-: uppercase words (module names) are atom aliases. `User == :"Elixir.User"`
[14:15:46] dysfun: yes, but alias Foo.User makes references to User mean :"Elixir.Foo.User"
[14:16:19] nickjj: but how could you end up calling Factory.build(User) from a file that doesn't have User aliased?
[14:16:34] dysfun: well that would be the fictional module User
[14:16:43] dysfun: or rather it's :"Elixir.User"
[14:16:57] nickjj: so elixir will be like "that's not aliased, let me look deeper"?
[14:17:09] dysfun: because it's just an atom
[14:17:29] dysfun: however, your example showed the full namespace
[14:17:34] dysfun: so i assumed you had aliased it
[14:17:59] nickjj: how do you propose we fix the factory to generate an ecto struct btw?
[14:18:16] dysfun: well why do you want an ecto struct?
[14:18:25] dysfun: i mean, i only ever want the data in it, not the ecto metadata
[14:18:31] nickjj: these resources are all ecto schemas
[14:18:38] dysfun: your pattern match on %User{} will still work
[14:19:00] nickjj: i don't personally mind, as long as it all works in the end, i just don't want to wake up one day with some crazy bug because ecto expected the meta data to exist
[14:19:11] ariedler: has joined #elixir-lang
[14:19:25] dysfun: if you really need the full ecto metadata, just insert it into the database
[14:20:28] __charly__: has joined #elixir-lang
[14:20:45] dysfun: or figure out how to fake it convincingly enough heh
[14:21:11] nickjj: i think reversing the order (making an ecto struct and turning it into a map) would probably do it? but that sounds like the whole lib would need to be rewritten
[14:21:30] dysfun: again, i ask if you really care about the ecto metadata
[14:21:49] dysfun: you'll only need that if for example you want to update the copy in the database, in which case just insert it into the database
[14:22:23] jmiven: has joined #elixir-lang
[14:22:55] dysfun: but all your functions that just take a User are unlikely to care
[14:23:22] nickjj: this is all for testing various changesets and inserting+updating+deleting data in the db
[14:24:11] dysfun: yes, so to do what we do under this library, you'd use valid_attrs(), create the changeset and then Repo.insert
[14:24:20] dysfun: oh, you want to make it use the right changeset functions
[14:25:13] dysfun: if they're all called changeset, you can match __struct__ out as usual and use apply(mod, :changeset, [%{__struct__: mod}, attrs)
[14:26:20] nickjj: they have different names
[14:26:35] nickjj: sign_up_changeset , update_profile_changeset , etc.
[14:26:50] dysfun: then maybe pass an extra parameter
[14:27:02] dysfun: which is the name of the function
[14:27:40] nickjj: thanks, this was definitely an insightful conversation
[14:28:07] nickjj: also if you decide to try it on your own later, everything does work except update_valid
[14:28:21] dysfun: i'm about to quickly knock up a library
[14:28:30] dysfun: based on what we discussed
[14:28:32] nickjj: update_valid failed with https://gist.github.com/nickjj/5f69295ac9cdbdd86d7c35b3d7ee8935
[14:29:06] dysfun: that's calling it with the atom, not with the struct named by the atom
[14:29:54] nickjj: in iex i had the user aliased
[14:30:55] dysfun: show the current code?
[14:32:14] nickjj: https://gist.github.com/nickjj/2d447bc5911aace7927e41dbf7ce483a , except L83 from the error is really L40 in the gist
[14:32:44] dysfun: yeah that call to Fakeable.valid should just be to valid
[14:33:32] nickjj: same error, but also note that build/valid/invalid all work as is
[14:33:50] dysfun: wait hang on, why are you calling it with an atom?
[14:33:55] dysfun: surely you want to pass in the User to update?
[14:34:09] nickjj: oh yeah, i was playing with :user from before
[14:34:45] nickjj: hmm, still the same issue with User instead of :user on L10
[14:35:06] dysfun: no, i mean in iex
[14:35:08] dysfun: when you are calling it
[14:35:21] dysfun: why should it take the module name instead of the struct?
[14:35:26] dysfun: how will you know what to update?
[14:36:08] nickjj: in iex , User is aliased from Foo.Accounts.User
[14:36:19] dysfun: no, think about what this function is supposed to do
[14:36:32] dysfun: how does one update a user if one doesn't pass a user to update?
[14:37:56] nickjj: so we should be calling it with Factory.update_valid(Factory.build(User))?
[14:38:01] nickjj: this also failed too btw
[14:38:12] dysfun: if that failed, it's more interesting
[14:38:19] wsieroci: has joined #elixir-lang
[14:39:23] nickjj: it failed with: https://gist.githubusercontent.com/nickjj/5f69295ac9cdbdd86d7c35b3d7ee8935/raw/964e26fb3210be5a005f30c71144546e043ecf99/err-2.ex
[14:39:44] nickjj: but if build returns a real user, technically that should have worked?
[14:39:45] dysfun: oh, you'll have to delete the __struct__ key
[14:39:53] duaneb: has joined #elixir-lang
[14:41:48] nickjj: Factory.update_valid(Factory.build(User), [:__struct__])
[14:41:59] dysfun: i did it slightly better here https://gist.github.com/jjl/a8bd1e37d5c544f6842197779d81c9ee
[14:42:09] dysfun: (see update_valid)
[14:42:16] nickjj: yep that was the next step
[14:42:27] dysfun: oh, but mine still uses Fakeable.valid
[14:42:40] lauromoura_: has joined #elixir-lang
[14:43:02] nickjj: now it works
[14:45:59] dysfun: ACTION does mix new testoplasm
[14:47:07] nickjj: nice haha
[14:47:45] nickjj: and if someone asks "but why?" then you can paste them the last 836 lines of chat
[14:48:27] dysfun: i can't wait for nox to tell me what a power move that is
[14:48:59] nox: How much backlog do I need to read to get that joke
[14:49:20] Nicd-: about 300 pages worth
[14:49:34] dysfun: nox: none. remember the thing with clojure stu?
[14:50:24] Saukk: has joined #elixir-lang
[14:50:47] Nicd-: will see a nice spike in the channel stats tomorrow :)
[14:51:02] dysfun: yes, i juts made an injoke only for you, special ickle nox
[14:53:55] dysfun: am i going to cry figuring out how to introspect an ecto schema?
[14:54:16] dysfun: my money is on yes
[14:56:59] nickjj: dysfun, why not try the reverse method of starting with the ecto schema/struct?
[14:57:20] nickjj: that's what i'm noodling with over here, trying to combine your awesome solution with my old "struct to map" stuff
[14:57:57] dysfun: they are functions for building the ecto struct
[14:58:08] nickjj: this way the source is an ecto struct, so we don't lose anything from the start
[14:58:24] dysfun: i'm writing a different function
[14:58:34] nickjj: and valid would just Map.from_struct on it
[14:59:55] nickjj: seems like it might be easier than doing all of the stuff you mentioned before, like having to write things to the db
[14:59:59] nickjj: and messing with changesets
[15:00:12] dysfun: well you could just put the metadata yourself
[15:00:33] nickjj: is that what you're trying to do now?
[15:07:23] __charly__: has joined #elixir-lang
[15:08:29] hypercore: has joined #elixir-lang
[15:11:51] snapet: has joined #elixir-lang
[15:12:40] benwilson512: nickjj: this seems exceptionally complicated
[15:12:55] benwilson512: nickjj: keep in mind that one reason a lot of Factory libraries exist is to avoid conflicts between tests
[15:13:06] benwilson512: but since Ecto isolates test cases you don't have conflicts between tests
[15:13:36] benwilson512: and `%{__struct__: mod}` should basically not be done
[15:14:00] benwilson512: use https://hexdocs.pm/elixir/Kernel.html#struct!/2 if you want to dynamically build a struct
[15:14:06] nickjj: benwilson512, i just wanted a quick way to not have to duplicate the same attrs in @create_attrs across multiple tests
[15:14:35] nickjj: and to be able to quickly create users with some fake data , so i can use those users in my tests
[15:17:24] BitBot: has joined #elixir-lang
[15:29:55] benwilson512: nickjj: that's perfectly fine, but just return struct literals
[15:30:30] benwilson512: def factory(:user, overrides), do: %MyApp.Context.User{name: Faker.name} |> struct!(overrides)
[15:30:37] benwilson512: (as a sketch)
[15:31:00] benwilson512: or if you're trying to actually insert them
[15:31:36] benwilson512: def insert(:user, overrides), do: %MyApp.Context.User{} |> MyApp.Context.User.changeset(%{name: faker.name()} |> Map.merge(overrides)) |> Repo.insert
[15:32:02] benwilson512: a whole protocol seems like overkill
[15:33:59] duaneb: has joined #elixir-lang
[15:35:38] aalmazan: has joined #elixir-lang
[15:35:58] cbw: has joined #elixir-lang
[15:40:22] nickjj: benwilson512, that's really similar to what i had originally
[15:48:08] tnez: has joined #elixir-lang
[15:48:38] hypercore: has joined #elixir-lang
[16:00:42] lexmag: has joined #elixir-lang
[16:02:00] icecreamcohen: has joined #elixir-lang
[16:20:09] ariedler: has joined #elixir-lang
[16:21:29] drincruz_: has joined #elixir-lang
[16:33:35] wsieroci: has joined #elixir-lang
[16:38:52] wsieroci: has joined #elixir-lang
[16:40:54] icecreamcohen: has joined #elixir-lang
[16:41:48] lexmag: has joined #elixir-lang
[16:44:54] sacredfrog: has joined #elixir-lang
[16:45:11] pera: has joined #elixir-lang
[16:48:11] lexmag: has joined #elixir-lang
[16:50:56] jnoon2: has joined #elixir-lang
[16:53:18] greengriminal: has joined #elixir-lang
[17:05:12] drincruz: has joined #elixir-lang
[17:06:00] greengriminal: has joined #elixir-lang
[17:15:04] Heuristic: has joined #elixir-lang
[17:19:23] dysfun: benwilson512: if you do that, the values will not be checked by the changeset function?
[17:27:40] __charly__: has joined #elixir-lang
[17:35:55] icecreamcohen: has joined #elixir-lang
[17:40:07] squall: has joined #elixir-lang
[17:50:44] francof: has joined #elixir-lang
[17:50:44] infowolfe: has joined #elixir-lang
[17:50:44] infowolfe: has joined #elixir-lang
[17:52:07] francof: has joined #elixir-lang
[17:52:20] bind: benwilson512: I just saw https://github.com/ex-aws/ex_aws_s3/issues/50 (because I was looking into ex_aws_s3 and trying to get prefixes streamed too from list_objects). Are there plans yet for a new release?
[17:53:25] bind: or to add more folks to keep the ball rolling because I really enjoy using ex_aws :-)
[17:54:02] francof: has joined #elixir-lang
[17:57:10] sbs__: has joined #elixir-lang
[18:01:52] sacredfrog: has joined #elixir-lang
[18:04:20] Saukk: has joined #elixir-lang
[18:06:29] notzmv: has joined #elixir-lang
[18:08:27] serafeim: has joined #elixir-lang
[18:15:37] mahmudov: has joined #elixir-lang
[18:17:17] greengriminal: has joined #elixir-lang
[18:18:10] benwilson512: dysfun: my second example explicitly calls the changeset function
[18:19:45] dysfun: ugh sorry, having a hard time thinking today
[18:20:47] benwilson512: dysfun: no problem :)
[18:21:05] ariedler: has joined #elixir-lang
[18:27:29] bind: benwilson512: that was quick! :D
[18:29:51] bind: benwilson512: thank you very much <3 if I can be of any help (small donation, extra hands..)
[18:49:34] wsieroci: has joined #elixir-lang
[18:50:22] m1dnight_: has joined #elixir-lang
[18:50:25] lexmag: has joined #elixir-lang
[18:52:55] jmcgnh: has joined #elixir-lang
[18:53:35] m1dnight_: has joined #elixir-lang
[18:54:59] pera: has joined #elixir-lang
[18:58:16] m1dnight_: has joined #elixir-lang
[18:59:48] griffinbyatt: has joined #elixir-lang
[19:05:06] nickjj: does anyone have a working example of how you can use Phoenix.Token.sign inside of a controller test? it throws `key :phoenix_endpoint not found in:` , but google isn't really showing a solution (at least not one that i found to work)
[19:05:34] benwilson512: nickjj: can you gist the full error?
[19:05:40] benwilson512: it looks like you're missing some config somehow
[19:07:34] nickjj: benwilson512, https://gist.github.com/nickjj/161faead87b332c17c56b0c88a4e445e -- this has the full error and the test causing it
[19:07:55] benwilson512: nickjj: how are you calling it?
[19:08:08] nickjj: in the regular controller where it's working in a browser? exactly the same way
[19:08:43] benwilson512: nickjj: just copy and paste the code plase
[19:09:35] benwilson512: or rather show the test case
[19:09:49] benwilson512: what's weird is that it doesn't look like sign is being passed a conn
[19:09:53] benwilson512: when you probably expect it to be a conn
[19:10:03] nickjj: the gist has the test case (1-test.ex)
[19:10:17] nickjj: i also updated it to show you how i'm using it the controller (which isn't even being accessed in the test yet)
[19:11:37] nickjj: but i'm trying to write a test where i insert a user , then generate a valid auth token and then the next steps of the test would be to goto /auth/abc123 (where abc123 is the token) to make sure it logs me in which i had planned to check by looking at cookies
[19:11:49] lexmag_: has joined #elixir-lang
[19:12:05] jmcgnh: has joined #elixir-lang
[19:12:52] benwilson512: apologies, irccloud only showed the one file
[19:13:20] nickjj: also the exact line from the trace throwing the error is line 7 in the gist (L57 in the error msg)
[19:14:10] taylorrf: has joined #elixir-lang
[19:14:22] benwilson512: nickjj: can you `IO.inspect(conn)` prior to that line?
[19:14:32] benwilson512: on line 2 of the test gist
[19:15:35] nickjj: refresh , i added the inspect on line 3 in the test gist
[19:15:47] nickjj: the output is 4-conn.ex
[19:16:18] nickjj: so it seems phoenix_endpoint isn't found in that "private" key
[19:16:28] benwilson512: yeah interesting
[19:16:35] benwilson512: how are you setting up your conn?
[19:16:43] benwilson512: did you used to just have plug and added phoenix later?
[19:16:44] nickjj: do you want to look at my conn case?
[19:16:57] nickjj: this is a standard phoenix 1.4 app btw
[19:18:24] nickjj: 5-conn_case.ex was added to the gist
[19:18:55] benwilson512: nickjj: I just looked in my app and it also doesn't show the phoenix endpoint
[19:19:15] benwilson512: I would consider simply using the `MyAppWeb.Endpoint` as the argument instead of `conn`
[19:19:35] benwilson512: I know why this is happening
[19:19:37] nickjj: i tried that before asking here, resulted in a diff error
[19:20:05] benwilson512: basically, you're using the `conn` before it's been sent through the endpoint's pipeline of plugs. that pipeline however is what _sets_ the endpoint and other information
[19:20:13] benwilson512: basically, a bare initialized plug isn't _suppose_ to have that information
[19:20:40] benwilson512: in your test case you should try using the Endpoint module as the arg
[19:20:44] benwilson512: if that gives an error LMK
[19:20:47] benwilson512: and we can solve that
[19:20:59] nickjj: literally replace conn with Endpoint?
[19:21:06] nickjj: or FooWeb.Endpoint?
[19:21:08] benwilson512: well the full module name
[19:22:07] nickjj: making progress, no more errors yet
[19:23:15] nickjj: what's really interesting is, that overall strategy is being used on a different app where conn works , but he wasn't using Phoenix.Token, instead he generated his own token internally
[19:25:08] nickjj: yeah this seems to be working, but man, i don't know how i could have debugged that alone
[19:25:54] benwilson512: nickjj: your best bet alone I guess would have been to checkout the sign/3 docs and see that you have other things you can pass as the first arg
[19:25:57] benwilson512: that one's tricky
[19:26:28] nickjj: thanks a lot, so in the end is it safe to use conn and Endpoint interchangeably for the Phoenix.Token.x functions?
[19:27:05] nickjj: i ask because as i wrote this test i realized that it seems a little dirty having to replicate that Phoenix.token.sign call in both the test and the controller, but it's not really clear where that would be abstracted to
[19:27:41] nickjj: it doesn't really seem like it should be in a context, or the schema itself right?
[19:28:14] cbw: nickjj: I've noticed conn and Endpointpoint can be used interchangeably in many places
[19:28:31] cbw: Endpoint*
[19:29:46] m1dnight_: has joined #elixir-lang
[19:30:12] nickjj: good to know, i guess it's useful in cases where you don't have access to a connection or a "full" conn in this case?
[19:31:14] cbw: Yeah. Sometimes in templates, for example, you don't have conn readily available, so you can use endpoint instead for generating routes
[19:31:51] nickjj: funny thing is, now this issue actually makes sense https://github.com/phoenixframework/phoenix/issues/1650#issuecomment-211270388
[19:32:27] nickjj: for whatever reason i didn't correlate "NameOfYourEndpoint" to be the Endpoint module, i tried almost everything but that
[19:33:24] sacredfrog: has joined #elixir-lang
[19:33:52] cbw: yeah, its almost like a hidden gem, but once you figure it out, its super helpful in more places than you'd expect at first
[19:34:36] nickjj: think it's worth putting that token generation in a context?
[19:37:38] cbw: its not a big deal either way, just do what feels right to you
[19:42:27] nickjj: i think it might be, not so much because there's a few duplicates lines, but a controller being responsible for determining the token generation strategy seems a little off (to me at least) -- thx everyone
[19:52:35] benwilson512: nickjj: I put token generation inside an Auth context often
[19:52:53] benwilson512: a MyAppWeb.Auth module at least
[19:58:02] kyrylo: has joined #elixir-lang
[20:15:38] orbyt_: has joined #elixir-lang
[20:21:52] ariedler: has joined #elixir-lang
[20:30:01] bars0: has joined #elixir-lang
[20:33:15] Poeticode: how might I update a field in a join table? I've got a many-to-many relationship, and on one of them I'm doing a put_assoc with the current array. I'm hoping to update a field at that time
[20:33:46] Poeticode: basically that field would be their index, since their ordering isn't getting changed :/
[20:34:31] Guest42: has joined #elixir-lang
[21:02:08] jmcgnh: has joined #elixir-lang
[21:02:20] za1b1tsu: has joined #elixir-lang
[21:11:32] __charly__: has joined #elixir-lang
[21:13:59] kapilp: has joined #elixir-lang
[21:23:13] Guest42: has joined #elixir-lang
[21:26:27] gvaughn: has joined #elixir-lang
[21:27:08] sacredfrog: has joined #elixir-lang
[21:30:42] greengriminal: has joined #elixir-lang
[21:52:26] gvaughn_: has joined #elixir-lang
[21:53:29] duaneb: has joined #elixir-lang
[22:08:54] lexmag: has joined #elixir-lang
[22:12:11] lexmag_: has joined #elixir-lang
[22:29:51] Guest42: has joined #elixir-lang
[23:02:24] orbyt_: has joined #elixir-lang
[23:10:20] ariedler: has joined #elixir-lang
[23:21:40] icecreamcohen: has joined #elixir-lang
[23:33:52] kyrylo: has joined #elixir-lang
[23:37:55] drincruz: has joined #elixir-lang
[23:44:35] Guest42: has joined #elixir-lang