faustinoaq changed the topic of #amber to: Welcome to Amber Framework community! | https://amberframework.org | Developer happiness, productivity and bare metal performance | GH: https://github.com/amberframework | Docs: https://docs.amberframework.org | Gitter: https://gitter.im/amberframework/amber | IRC Logger: https://irclog.whitequark.org/amber | Amber::Server.start
<FromGitter> <Blacksmoke16> 1) denotes a splat
<FromGitter> <Blacksmoke16> Could use an enum maybe
<FromGitter> <andrewc910> Why an enum? Off topic: Does crystal follow the same style as ruby for constsants? Technically you only need the first letter capitalized but everyone just does full uppercase for clarity?
<FromGitter> <Blacksmoke16> `Although not enforced by the compiler, constants are usually named with all capital letters and underscores to separate words.`
<FromGitter> <Blacksmoke16> because you want to represent a static "list" of providers
<FromGitter> <Blacksmoke16> ```enum Provider ⏎ Google ⏎ Github ⏎ end``` [https://gitter.im/amberframework/amber?at=5e1d0664cb2aaa2d78338d6f]
<FromGitter> <andrewc910> Oh and the final thing you now in the contrive.cr initializer you need to require the individual providers like: ⏎ ⏎ ```code paste, see link``` ⏎ ⏎ So now, you only load the providers you require and no extra code is loaded. [https://gitter.im/amberframework/amber?at=5e1d06780e65654fa0db5165]
<FromGitter> <Blacksmoke16> ah
<FromGitter> <andrewc910> Because what i stated above, using something static may error out(atleast the case/switch did) because Provider::Google or someting may not be defined
<FromGitter> <Blacksmoke16> fwiw code that isnt used isnt included in the binary
<FromGitter> <Blacksmoke16> so debatable if you're saving anything by doing that
<FromGitter> <andrewc910> I would give you the angry face emoji right now if i could. I did not know that was the case...
<FromGitter> <Blacksmoke16> yea iirc if a method or something is never called its just omitted from the binary
<FromGitter> <andrewc910> So should i just require everything and move some logic to be more static?
<FromGitter> <Blacksmoke16> could prob try it by building the binary with and without them
<FromGitter> <Blacksmoke16> could do something like
<FromGitter> <andrewc910> I could try that. I am curious how the compiler would know though. Because the providers are called from a controller based on the given parameters. The compiler would have to guess and presume all 5 providers could be called and then would include them?
<FromGitter> <andrewc910> Here's the controller method that actually invokes the login ⏎ ⏎ ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d07638d9f831bc5239e79]
<FromGitter> <Blacksmoke16> enums can have methods too
<FromGitter> <Blacksmoke16> but really you would just be ussing the enum to get an instance/reference to the actual provider class
<FromGitter> <Blacksmoke16> ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d08318d9f831bc523a506]
<FromGitter> <Blacksmoke16> something like that
<FromGitter> <Blacksmoke16> think id be more inclined to have the `configuration` method live within the actual provider's initializer
<FromGitter> <Blacksmoke16> utilize inheritance or something, idk whats all needed per provider tho
<FromGitter> <Blacksmoke16> ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d0aca6be93b6b36d93bc9]
<FromGitter> <Blacksmoke16> would be a more OOP approach
<FromGitter> <Blacksmoke16> child providers can override the `client_id`, `client_secret` methods if needed
<FromGitter> <Blacksmoke16> or can add a custom initializer
<FromGitter> <Blacksmoke16> like
<FromGitter> <Blacksmoke16> ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d0b088d9f831bc523b4ad]
<FromGitter> <Blacksmoke16> ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d0ba16f60415299377193]
<FromGitter> <Blacksmoke16> could use a record here
<FromGitter> <Blacksmoke16> since its immutable
<FromGitter> <Blacksmoke16> are all of your providers OAuth2?
<FromGitter> <Blacksmoke16> if so maybe not best idea to hide the clientId/secret logic inside the type, maybe make it part of the constructor but default to ENV vars?
<FromGitter> <Blacksmoke16> /shrug
<FromGitter> <andrewc910> @Blacksmoke16 Aww man that code is getting a little advanced for me. I'm out right now. When I get home I will go through and try and understand everything. At a glance, I definitely need to read about methods/macros in enums, abstract classes and private classes. Never work with any of those before.
<FromGitter> <andrewc910> No, 1 or 2 are Oauth. I'd have to look but I know at least one of them isn't oauth2.
<FromGitter> <Blacksmoke16> Some custom thing then?
<FromGitter> <Blacksmoke16> 👍 ill be around for a few more hours if you have any questions
<FromGitter> <andrewc910> I wanted the clientid and key in the config so it could be changed in the initializer. I personally like namespacing my credentials based on environment. I didn't want people to be stuck with `ENV[Google]` as it would make your credentials less flexibles. Currently in the initializer, you can do `omniauth.config.google_id = ` which I thought was nice
<FromGitter> <andrewc910> I'm pretty strong with basic oop. The program I'm in had
<FromGitter> <andrewc910> Oops.
<FromGitter> <andrewc910> The learning program I'm in spent over 200 class hours on oop including classes, objects, instances, inheritance, modules, self, getter/setter, private protected. But that was about the depth of it. The things I mentioned weren't touched on.
<FromGitter> <Blacksmoke16> gotcha
<FromGitter> <Blacksmoke16> true, i was thinking that someone wouldnt be using more than 1 provider at a time
<FromGitter> <Blacksmoke16> which i suppose isnt true
<FromGitter> <andrewc910> And thanks again for all the help! I really appreciate it. If I have any questions, I'll let you know!
<FromGitter> <andrewc910> Where I work, we use 2(Facebook and Google). I imagine 2 would be the average.
<FromGitter> <Blacksmoke16> i would think about the providers you want to support then try and break them down into abstractions
<FromGitter> <Blacksmoke16> like `AbstractProvider` would have things related to all providers, could then split to like `OAuthProvider` etc
<FromGitter> <Blacksmoke16> like that example i gave earlier, but with clientId/clientSecret in the constructor, would be the better way to go
<FromGitter> <Blacksmoke16> im not familiar with Ruby's devise, but https://github.com/thephpleague/oauth2-client/blob/master/src/Provider/AbstractProvider.php might be a good reference
<FromGitter> <Blacksmoke16> https://github.com/hayageek/oauth2-yahoo/blob/master/src/Provider/Yahoo.php and a specific implementation of it
<FromGitter> <Blacksmoke16> (ofc not going to map 1:1, but mainly to get the structure of what could/would go into an `AbstractOAuthProvider` for example)
<FromGitter> <Blacksmoke16> define a like `def client : OAuth2::Client` on the abstract `OAuthProvider`, that uses the values defines by the child
<FromGitter> <andrewc910> May I ask what would be the benefit of abstract class vs what it is now. Provider is a superclass while fb, Google, etc all inherit? Hard to do a cost/benefit analysis when I don't even understand what abstract classes are.
<FromGitter> <andrewc910> I'll take a look at the links you provided for reference! Thanks! Don't know php so it'll take me a while to comprehend.
<FromGitter> <Blacksmoke16> `abstract` means it cant be instantiated on its own
<FromGitter> <Blacksmoke16> since it wouldnt make sense to do `OAuthProvider.new`
<FromGitter> <Blacksmoke16> just makes it clear that its not something that should be used on its own, that its required to use a specific implementation of it
<FromGitter> <Blacksmoke16> just look at the method names really, those defines on the parent type and those that are abstract
<FromGitter> <Blacksmoke16> also the implementation of `getResourceOwnerDetailsUrl` is pretty helpful
<FromGitter> <Blacksmoke16> would return an object representing the user in that API, similar to your `GhUser` class
<FromGitter> <andrewc910> Oh! So abstract classes are best as superclasses we plan to never instantiate on their own? So provider is a good abstract class because we actually instantiate it's child classes(fb, Google)? Am I understanding that correctly? It's just another way to tighten the public interface?
<FromGitter> <Blacksmoke16> correct
<FromGitter> <andrewc910> That's pretty cool! Thanks :D
<FromGitter> <Blacksmoke16> its just a way to define common logic that all providers need
<FromGitter> <andrewc910> Okay, I'll just read the method names and think about implementation a big.
<FromGitter> <andrewc910> Okay, I have a question about types
<FromGitter> <Blacksmoke16> are also comments on them too, deff wont need *all* the methods
<FromGitter> <Blacksmoke16> like `GrantFactory` and stuff
<FromGitter> <Blacksmoke16> is also fairly common to define an interface that your abstract class implements, but not a big deal right now
<FromGitter> <Blacksmoke16> oh yea, also abstract classes wont force you to implement abstract methods
<FromGitter> <andrewc910> Are abstract methods methods you can only invoke on subclasses?
<FromGitter> <Blacksmoke16> abstract methods allows the compiler to know that "this method will exist but its not implemented here"
<FromGitter> <Blacksmoke16> like if you have an abstract method on your abstract class, the child will be required to implement it
<FromGitter> <Blacksmoke16> (granted in crystal its not as big of a deal since it wouldnt compile anyway, but defining the abstract methods makes requirements of the class more well known, allows you to document it (can use `# :inherit:` doc comment))
<FromGitter> <Blacksmoke16> from the book `By marking a method as abstract the compiler will check that all subclasses implement this method, even if a program doesn't use them.`
<FromGitter> <andrewc910> I'm a little confused. Don't all subclass automatically implement a parent class merhod jus t because of inheritance? Or do you only define(no actual method implementation) an abstract class and then each subclass is no required to implement these methods in their own way?
<FromGitter> <andrewc910> Or do you only define an abstract method* accidentally put class
<FromGitter> <andrewc910> Is now required* damn mobile.
<FromGitter> <Blacksmoke16> sec
<FromGitter> <Blacksmoke16> https://play.crystal-lang.org/#/r/8e4u
<FromGitter> <Blacksmoke16> `abstract class` makes it so you cant do `Foo.new`
<FromGitter> <Blacksmoke16> `abstract def` says that a child *MUST* implement that method
<FromGitter> <Blacksmoke16> and it must return a `String`
<FromGitter> <Blacksmoke16> i.e. you're declaring a method, but not implementing its functionality
<FromGitter> <andrewc910> That all makes 100% sense! Thank you for that example 😁
<FromGitter> <andrewc910> May I ask, how long have you been working with Crystal? You seem to have a solid grasp of the language and the nuances. I've only been using it for like 2 weeks...
<FromGitter> <Blacksmoke16> Wow, like 2 years by now
<FromGitter> <Blacksmoke16> Most of this is standard oop, not specific to crystal
<FromGitter> <Blacksmoke16> yea i feel your pain, i rewrote all the shards i made when first started
<FromGitter> <Blacksmoke16> by the time i was done i figured out a better implementation ha
<FromGitter> <andrewc910> I get it's standard oop, I was just curious! Man you've been using crystal longer than I've been programming. I was finally feeling like a programmer until I moved into this gitter room.
<FromGitter> <Blacksmoke16> haha gotta start somewhere
<FromGitter> <andrewc910> c'est la vie
<FromGitter> <andrewc910> That's how I feel contrive will be. With my work, and everyone's I put, including and especially you(really thanks for all the help), it's going to get a total rewrite.
<FromGitter> <Blacksmoke16> usually how it goes, will look back in 6 months and be like "wow this was bad'
<FromGitter> <Blacksmoke16> then same thing another 6 months from then :P
<FromGitter> <andrewc910> It never gets better? I figured the law of diminishing returns eventually hits programmers. How many more language specific & programming concepts can you actually learn after 2+ years in a specific language and 5-10+ years in other languages?
<FromGitter> <andrewc910> How much do you grow and learn in 6 months when you're practically an expert? I thought that rule typically applied to noobs and intermediates because there is so much to learn?
<FromGitter> <Blacksmoke16> granted you can ofc max out a language
<FromGitter> <Blacksmoke16> like know all the ins and outs
<FromGitter> <Blacksmoke16> but then there is also the non programming aspect of things
<FromGitter> <Blacksmoke16> how to approach problems, the architecture of things etc
<FromGitter> <Blacksmoke16> writing good documentation
<FromGitter> <andrewc910> I understand that. So applying it to my questions above, I imagine you're not thinking "wow this code sucks" but more of "I don't like this implementation, I know a better way"? Is that correct?
<FromGitter> <Blacksmoke16> yea, like that will be easier to understand/maintain/expand/test
<FromGitter> <Blacksmoke16> and in other code some stuff just jumps out at me like "you can optimize this a bit"
<FromGitter> <andrewc910> That makes sense! I'm already thinking about that with the cli. My first thought is "well it's really easy to add templates" and then I think "okay, test it"
<FromGitter> <Blacksmoke16> one that just came to mind iss like
<FromGitter> <Blacksmoke16> ```if val == true ⏎ # do stuff ⏎ else ⏎ # do other stuff ⏎ end``` [https://gitter.im/amberframework/amber?at=5e1d3d251cf5106b35189ceb]
<FromGitter> <Blacksmoke16> when `val` is a `Bool`
<FromGitter> <andrewc910> "you can optimize this a bit" is an understatement when you're reading my code 😂
<FromGitter> <andrewc910> Without your answer I would make that ternary. Any if/else, I assume ternary
<FromGitter> <Blacksmoke16> ternary is nice if its short
<FromGitter> <Blacksmoke16> like `val = bool ? 1 : 0`
<FromGitter> <Blacksmoke16> if there is more than like 1 line cleaner to make it its own thing
<FromGitter> <andrewc910> If it's long I go:
<FromGitter> <Blacksmoke16> esp if its nested
<FromGitter> <andrewc910> `return expression if true`
<FromGitter> <andrewc910> `second expreseion`
<FromGitter> <andrewc910> Sorry can't enter on mobile
<FromGitter> <Blacksmoke16> those are called guard clauses in ruby
<FromGitter> <Blacksmoke16> or in any lang i suppose
<FromGitter> <Blacksmoke16> is much cleaner than like
<FromGitter> <Blacksmoke16> ``````
<FromGitter> <andrewc910> Yessir. My teachers have described me as 'very verbose'
<FromGitter> <Blacksmoke16> ```if expression ⏎ else ⏎ second expresssion ⏎ end``` [https://gitter.im/amberframework/amber?at=5e1d3de63254b6754ca4b3f2]
<FromGitter> <andrewc910> I'm curious how you would rewrite that if/else. I only gave my input prior to display my thought process.
<FromGitter> <Blacksmoke16> hm?
<FromGitter> <andrewc910> The original if/else you gave about 10 messages above. How would you rewrite it and why? I'm just curious.
<FromGitter> <Blacksmoke16> this one?
<FromGitter> <Blacksmoke16> depends what `do stuff/do other stuff` is exactly
<FromGitter> <andrewc910> Yeah
<FromGitter> <Blacksmoke16> context is important
<FromGitter> <andrewc910> Okay, so it just depends on the situation. You don't have a specific reason to use one specific concept over another?
<FromGitter> <andrewc910> And i imagine this applies to anything. In some contexts, you can use a multitude of concepts for implementation but the context will guide the decision?
<FromGitter> <andrewc910> How do you gain that intuition? Solely experience?
<FromGitter> <Blacksmoke16> im going to go with yes, the context of the problem directly affects the solution i would choose
<FromGitter> <andrewc910> Finally at a comp. Gitter mobile seriously sucks. Time to review the code you gave me and be confused! :)
<FromGitter> <andrewc910> Makes sense
<FromGitter> <Blacksmoke16> short if/else use ternary, longer use full block, lot of code guarded by simple expressions use guard clauses
<FromGitter> <Blacksmoke16> method gets too big, think about abstracting something somewhere else
<FromGitter> <Blacksmoke16> always keeping test-ability in mind
<FromGitter> <andrewc910> So when writing code, one of the first thoughts should be "how testable and maintainable is this"?
<FromGitter> <Blacksmoke16> id say work was super helpful for me, seeing how other more experienced devs went about the problem
<FromGitter> <Blacksmoke16> and getting their thoughts on stuff
<FromGitter> <Blacksmoke16> not a bad mentality to have
<FromGitter> <Blacksmoke16> as remember most of the time you're not the only one reading/working with your code (esp in a rl work env)
<FromGitter> <Blacksmoke16> ofc context comes into play here, if the application is a side project that only you are going to use, not a big deal if you just throw it together and get it working and then not worry about it anymore
<FromGitter> <andrewc910> That's literally my problem in work, hence why i am here. I work for a small startup and i am the second most experienced dev. Sure, i can spin up aws, install all dependencies, deploy, set up dns, work frontend, backend, make sure we have decent test coverage. But my code needs to be reviewed. I need an expert to say "yeah this works, but here's an edgecase" or "heres a better implementation that is faster, easier
<FromGitter> ... to maintain and is more readable"
<FromGitter> <Blacksmoke16> maybe make an effort to work more with the other dev?
<FromGitter> <Blacksmoke16> pair programming on some task etc?
<FromGitter> <andrewc910> Would you say this is a fine implementation for a controller: ⏎ ⏎ ```code paste, see link``` ⏎ ⏎ This is a controller method. Fakeuser is my user class in contrive. IDK what columns a specific lib user will have in their user class. So this block allows devs to assign any values in the fakeuser to their actual user class. What would you do? [https://gitter.im/ambe
<FromGitter> ... rframework/amber?at=5e1d4123b990d50d81ac124c]
<FromGitter> <andrewc910> And he is a consultant that comes in once a month for a day or two :(
<FromGitter> <Blacksmoke16> ah :/
<FromGitter> <andrewc910> We only really message him about implementation.
<FromGitter> <Blacksmoke16> that doesnt help
<FromGitter> <andrewc910> I think of a implementation, then ask why or why not.
<FromGitter> <andrewc910> here is the method the controller is invoking: ⏎ ⏎ ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d41900aba1f0d802de894]
<FromGitter> <Blacksmoke16> i dont think i know enough to have a good answer
<FromGitter> <Blacksmoke16> framework we use has a separate user for auth
<FromGitter> <Blacksmoke16> like `SecurityUser` that represents username/password/token/roles etc
<FromGitter> <andrewc910> Just looking at it, I am hardcoding "User.new" on line 5. Will have to think of something else if i want others to be able to name their user class something else
<FromGitter> <Blacksmoke16> granted dont think thats real helpful here (without a big rewrite of amber prob)
<FromGitter> <Blacksmoke16> whats the use case for doing all this?
<FromGitter> <Blacksmoke16> why not just let the user define the controller action on their own and just have your thing abstract handling the exchange of the `code` for a token?
<FromGitter> <andrewc910> The first method i provided was the registration controller for oauth. The second code block is the method we invoked in the first.
<FromGitter> <Blacksmoke16> right
<FromGitter> <Blacksmoke16> idt you have to do *everything*
<FromGitter> <Blacksmoke16> just handle the auth part and leave the rest up to the user?
<FromGitter> <Blacksmoke16> https://crystal-lang.org/api/master/OAuth2/Client.html like how this does it
<FromGitter> <Blacksmoke16> `def get_access_token_using_authorization_code(authorization_code : String) : AccessToken`
<FromGitter> <Blacksmoke16> ```def callback ⏎ token = get_access_token_using_authorization_code(param["code"]) ⏎ end``` [https://gitter.im/amberframework/amber?at=5e1d42b165badf754d84a1e0]
<FromGitter> <andrewc910> I guess i am trying to abstract as much from the user as possible. One of my thoughts right now is crystal doesn't have enough "easy" shards/frameworks for newbies to get into it. My GF is learning programming. Although she has been programming the same time as me, she doesn't read documentation at bars like me. She isn't on gitter trying to make stuff just to learn. ⏎ ⏎ With that said, if she came into
<FromGitter> ... crystal, she would quit just because there isn't enough resources. I wanted to make a shard that was easy for beginners.
<FromGitter> <andrewc910> So i am trying to keep in mind both the advanced users and the beginners.
<FromGitter> <andrewc910> so `omniauth_register` can be ripped into multiple methods. One handles just the token. The single absolute piece required. However, i can build a couple 'helper' methods for beginners. At any time, an advanced user can delete the `omniauth_register` invokation, implement their own controller method and call `callback` when needed. How does that sound?
<FromGitter> <Blacksmoke16> *personal opinion* essentially dumbing something down so it'll be easier on new programmers will just make them take stuff for granted and not learn how it actually works. you cant make up for someone's lack of motivation by making your code appear simple, abstracting for sake of abstracting doesnt help anyone. it has to be the *right* abstraction *personal opinion*
<FromGitter> <Blacksmoke16> whats the end goal of your callback method? create a `Users` object from an oauth callback `code`?
<FromGitter> <andrewc910> That makes sense. So how do you help the new users that want to learn? Where is the compromise?
<FromGitter> <andrewc910> Create and save, yes.
<FromGitter> <Blacksmoke16> documentation and taking the time to help them/answer their questions
<FromGitter> <Blacksmoke16> examples are also really helpful
<FromGitter> <Blacksmoke16> versus just a wall of text
<FromGitter> <Blacksmoke16> examples + comments explaining each line
<FromGitter> <Blacksmoke16> with links to more detailed docs
<FromGitter> <andrewc910> Okay, so rather than me writing everything in contrive, i should probably pull some methods out into examples about implementations? I understand there is no right answer and this is all subjective, jsut curious on your thoughts.
<FromGitter> <Blacksmoke16> id write everything in the API docs
<FromGitter> <Blacksmoke16> i.e. `crystal docs`
<FromGitter> <Blacksmoke16> by just adding doc comment blocks to stuff
<FromGitter> <Blacksmoke16> surround types in backticks links to their own "page"
<FromGitter> <andrewc910> How's this: ⏎ ⏎ ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d44fb6be93b6b36dabe09]
<FromGitter> <Blacksmoke16> runnable examples might be a bit harder since it depends on dbs and stuff
<FromGitter> <andrewc910> Now, omniauth_register only returns the 'fakeuser'(my user class in oauth) and the rest is up to the user.
<FromGitter> <Blacksmoke16> hmm
<FromGitter> <andrewc910> I will read about crystal docs! I thought a lot of work went into making those things!
<FromGitter> <Blacksmoke16> nope, stupid simple ;p
<FromGitter> <Blacksmoke16> what properties does your `fakeuser` have?
<FromGitter> <Blacksmoke16> and your `.user` method essentially, exchanges the code for a token, fetches user info using that token and returns a new `User` objet?
<FromGitter> <andrewc910> Exactly.
<FromGitter> <andrewc910> I would love to remove the fakeuser class of mine altogether but i can't think of a way without a bunch of catch exceptions for 'No method image='
<FromGitter> <Blacksmoke16> wouldnt each provider's representation of a user be different?
<FromGitter> <andrewc910> Yeah, this encompasses the usual data given. That's the reason every property is nilable. Some providers only give a handful of the properties.
<FromGitter> <Blacksmoke16> i like how that php lib handles this i think
<FromGitter> <Blacksmoke16> each provider has its own `User` representation
<FromGitter> <Blacksmoke16> with an abstract `getResourceOwnerDetailsUrl` and `createResourceOwner` methods
<FromGitter> <Blacksmoke16> i can make an example if you want...
<FromGitter> <Blacksmoke16> (in crystal)
<FromGitter> <andrewc910> I'll take a look. but it is still returning some type of fake user, right? Like the user object they return is not the same object you save in your db? So you still have to pull any data you want out? I am hoping to circumvent that.
<FromGitter> <andrewc910> I couldn't ask you to do that. Thank you though!
<FromGitter> <Blacksmoke16> its not really a "fake" user, its the object that represents that user in an external system
<FromGitter> <Blacksmoke16> you would have to make data from them to your internal `User`representation
<FromGitter> <Blacksmoke16> wouldnt want to tightly couple your internal user to an external user representation
<FromGitter> <andrewc910> Yeah i was hoping to circumvent that but whatever, i will focus on other parts of the shard.
<FromGitter> <Blacksmoke16> are you requiring that the internal user object has specific fields defined?
<FromGitter> <Blacksmoke16> if so this gets a lot easier
<FromGitter> <Blacksmoke16> i.e. is `email` always going to be it, or can i set it to like `usr_email` etc?
<FromGitter> <andrewc910> We can change this to a macro. But that would complicate other parts.
<FromGitter> <Blacksmoke16> alright, so they're static things
<FromGitter> <Blacksmoke16> in that case you could do like
<FromGitter> <andrewc910> ` property location : String?` so the fakeuser is designed because i don't want to require you, the lib user, to be forced to have a location column if you didn't want to
<FromGitter> <Blacksmoke16> isnt that just more of a reason to separate the two types of user?
<FromGitter> <Blacksmoke16> could also do this to make it easier
<FromGitter> <andrewc910> Yeah but wouldn't passing in the internal user in and something like: ⏎ ⏎ ```user.location = fakeuser.location if user.location.exists?``` ⏎ ⏎ be best? [https://gitter.im/amberframework/amber?at=5e1d4a78b990d50d81ac4c1d]
<FromGitter> <Blacksmoke16> ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d4a796f6041529939139b]
<FromGitter> <Blacksmoke16> something along those lines
<FromGitter> <Blacksmoke16> obs a simple contrived example but you get the idea?
<FromGitter> <Blacksmoke16> so user defines 1 constructor to initialize a `User` object from a provider's user representation that will work with any provider
<FromGitter> <Blacksmoke16> `.is_a?` checks or other constructors could be used for specific providers
<FromGitter> <Blacksmoke16> interfaces and overloads ftw
<FromGitter> <andrewc910> Yeah i do. I like this.
<FromGitter> <andrewc910> ```module ResourceOwnerInterface ⏎ abstract def username : String ⏎ end``` ⏎ ⏎ Because we include this module, we require every class the includes this to implement a `username` method. That's correct? Why? Just so other maintainers understand this is a neccessary method? [https://gitter.im/amberframework/amber?at=5e1d4ba7cb2aaa2d78356267]
<FromGitter> <Blacksmoke16> that and so it provides a common interface for all provider user representations
<FromGitter> <Blacksmoke16> is what allows doing `@email = user.username`
<FromGitter> <Blacksmoke16> probably a bad example...
<FromGitter> <Blacksmoke16> my idea was it allows mapping each user representation's unique id to a single method
<FromGitter> <Blacksmoke16> `abstract def id` might be better?
<FromGitter> <Blacksmoke16> then it can be a uuid, email etc
<FromGitter> <andrewc910> Got it. So it's a double whammy. By making the lib classes stricter, it keeps the maintainers stricter. By keeping the maintainers strict, lib users know they can invoke `username` on any ProviderUser without error.
<FromGitter> <Blacksmoke16> right, idk if that the correct abstraction here to be clear, was just an example
<FromGitter> <andrewc910> And username doesn't have to equal username. It's just an identifier.
<FromGitter> <Blacksmoke16> right, which is why `id` is prob a better name
<FromGitter> <andrewc910> UUID, email, username, whatever said provider prefers
<FromGitter> <Blacksmoke16> exactly
<FromGitter> <Blacksmoke16> leaves it up to the implementation to define what it should use
<FromGitter> <andrewc910> I think that's a good example then, thank you:)
<FromGitter> <andrewc910> ...did you just link me to an exact line?...
<FromGitter> <Blacksmoke16> example of how that PHP lib does it, child implements `createResourceOwner` method that defines how to create the user representation for that type
<FromGitter> <Blacksmoke16> yes
<FromGitter> <Blacksmoke16> in crystal land that could look like
<FromGitter> <Blacksmoke16> ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d4d3ab990d50d81ac5dca]
<FromGitter> <Blacksmoke16> most of these are going to be JSON yea?
<FromGitter> <andrewc910> Yeah about the json
<FromGitter> <andrewc910> Just to clarify this conversation. You believe it is best each provider has it's own user class with only the data said provider returns. However, each user has the same methods(excluding specific data from a certain provider) to make that user more generic. ⏎ ⏎ What are the benefits of this opposed to a single fakeuser class that can handle any provider? Less nilable values?
<FromGitter> <Blacksmoke16> > You believe it is best each provider has it's own user class with only the data said provider returns
<FromGitter> <Blacksmoke16> yes
<FromGitter> <Blacksmoke16> > each user has the same methods(excluding specific data from a certain provider) to make that user more generic ⏎ ⏎ each user here being the internal `User` obj?
<FromGitter> <andrewc910> External. In my lib
<FromGitter> <andrewc910> IE: FacebookUser
<FromGitter> <Blacksmoke16> id define an interface that each provider's user type must implement
<FromGitter> <Blacksmoke16> what methods are on that interface is still TBD, might just be like `id`
<FromGitter> <andrewc910> I get that. My confusion is my last question
<FromGitter> <andrewc910> What are the benefits of this opposed to a single fakeuser class that can handle any provider? Less nilable values?
<FromGitter> <Blacksmoke16> the benefit is that you're in a compiled language, you should use the type system to your advantage
<FromGitter> <Blacksmoke16> could define overloads for one specific provider while letting the others use a diff one, all the while keeping the same public api
<FromGitter> <Blacksmoke16> `internaluser.new provider_user`
<FromGitter> <andrewc910> This can be summed into, try to have as little nil values as possible for any given class? Is that correct?
<FromGitter> <andrewc910> Be strict about types.
<FromGitter> <Blacksmoke16> your type would essentially be a `Hash` with a union of all the types/methods from all the providers
<FromGitter> <Blacksmoke16> like what happens if `id` is a string on one and an int on another? would have to do `getter id : Int32 | String` and things start to get messy
<FromGitter> <Blacksmoke16> nilable types are fine if that value is optional, is when you get a class with 20+ properties all nillable that you might want to ask yourself is there a better way
<FromGitter> <andrewc910> To remove the union, you use: ⏎ ⏎ ```module ResourceOwnerInterface ⏎ abstract def username : String ⏎ end``` [https://gitter.im/amberframework/amber?at=5e1d4f826be93b6b36db020c]
<FromGitter> <andrewc910> That's why you had that, right?
<FromGitter> <Blacksmoke16> so you can do this
<FromGitter> <Blacksmoke16> ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1d4fc8cb2aaa2d78357e00]
<FromGitter> <Blacksmoke16> github users will hit that init method, all others will hit the other
<FromGitter> <Blacksmoke16> can even define more interfaces if you wanted to better group the user objs
<FromGitter> <Blacksmoke16> overall its just more flexible
<FromGitter> <Blacksmoke16> id be curious to hear if anyone else in this ch has any thoughts on this, but thats how i would do it :shrug:
<FromGitter> <andrewc910> I understand that. I don't feel like i am using the type system at all. I feel like it is getting in my way rather than taking advantage of it.
<FromGitter> <andrewc910> I am still wrapping my head around it.
<FromGitter> <Blacksmoke16> yea takes a while to get used to
<FromGitter> <andrewc910> Another intuition thing?
<FromGitter> <Blacksmoke16> more specific to crystal, just learning how/why it does certain things
<FromGitter> <Blacksmoke16> and how to take advantage of it
<FromGitter> <andrewc910> Any good guides you recommend? Gitter is the best place currently?
<FromGitter> <Blacksmoke16> https://crystal-lang.org/reference if you havent already
<FromGitter> <Blacksmoke16> could prob switch to #crystal channel as well, kinda got offtopic a bit here :p
<FromGitter> <Blacksmoke16> well not too far, since this is for amber
<FromGitter> <andrewc910> Yeah i have read quite a bit of it. I was aware of most of these things but it didn't really click. While coding i thought a tighter type system in method definitions would be best but i didn't have anything concrete to go off of. This lesson about the various topics has been nice because i have real-world use cases.
<FromGitter> <andrewc910> I agree about switching channels. I do feel bad.
<FromGitter> <Blacksmoke16> meh its fine, not like we were interfering with any other convo... :cricket:
<FromGitter> <Blacksmoke16> i usually try to be strict and give method return type and arg restrictions when i can
<FromGitter> <Blacksmoke16> makes errors and docs better
<FromGitter> <Blacksmoke16> anyway ill leave you to it, imma get to bed
<FromGitter> <andrewc910> Well again, thank you. ttyl
<FromGitter> <damianham> @andrewc910 If I was doing this I would add it to the ambercommunity github organisation (I sent you an invite) so other people can help you with it and I would base it on the way MEAN (https://github.com/meanjs/mean) uses passport (http://www.passportjs.org/). I would have something like this ⏎ ⏎ ```code paste, see link``` [https://gitter.im/amberframework/amber?at=5e1daf5da859c14fa1e363d6]
<FromGitter> <Blacksmoke16> MTI would be great for this :/
<FromGitter> <Blacksmoke16> that `data` column is kinda gross
<FromGitter> <damianham> it's the most sensible way to store the extra provider data which will be different for each provider
<FromGitter> <Blacksmoke16> since we dont have MTI yea
<FromGitter> <Blacksmoke16> (in granite at least)
<FromGitter> <damianham> I have changed my mind on having a separate repository for each strategy as the code for each strategy will be very small - better to have the strategies within the amberauth repo - as you pointed out earlier - code that is not called is not compiled into the binary
FromGitter has quit [Remote host closed the connection]
FromGitter has joined #amber