<dkubb>
snusnu: I just left a comment on the commit
<dkubb>
snusnu: I don't think my idea is substantially better, maybe just a minor improvement in a couple of loc removal
<snusnu>
dkubb: omg, thx a bunch for reminding me about Array#[]= :D
<snusnu>
dkubb: i did not think about that *at all*
<dkubb>
hehe
<snusnu>
hehe
<dkubb>
snusnu: I assume the order of the processors is significant?
<snusnu>
dkubb: you know, in my mind i was envisioning it as "ok, delete the element at that index, then add the new one ..."
<snusnu>
dkubb: yes
<dkubb>
ahh ok
<dkubb>
well, given we're targetting 1.9 only with gems, you could use a Hash for the processors
<snusnu>
dkubb: and i didn't bother with a hash index, because that code gets run only once, when initially constructing the chains
<dkubb>
since a Hash now maintains order
<snusnu>
dkubb: good point .. hmm
<dkubb>
you'd index each processor by it's name
<snusnu>
dkubb: i dunno why it still feels weird :)
<snusnu>
dkubb: yeah
<dkubb>
when you add those error processors, do they have the same name as the original?
<snusnu>
yeah
<dkubb>
oh interesting
<dkubb>
I have no idea if doing hash[:existing_key] = new_value adds the new value to the end of the list or replaces the original
<dkubb>
if it does replace the original, then you're set. you can simplify things even more
<snusnu>
basically, the idea is that you define a "raw" action, then you define a common "web" action, and then you define html or json actions on top of those web actions … all the time reusing what's there, but possibly modifying the chain that gets invoked on failure
<dkubb>
you can change that detect loop to a nice Hash#fetch and use a block to raise the exception
<snusnu>
right, i need to test that
<dkubb>
so like @processors.fetch(name) { raise UnknownProcessor, "No processor named #{name.inspect} is registered" }
<snusnu>
lemme try that now
<snusnu>
actually, i think i should get over my dislike for ordered hashes
<snusnu>
they're in ruby, so ...
<dkubb>
hehe
<dkubb>
yeah, use the features of the language if they're explicit
<snusnu>
yup
<dkubb>
it's not like you're relying on some implementation side effect
<dkubb>
even though I have a distaste for them too, I will use them as much as I can
<dkubb>
I think I'd rather have a faster/lighter Hash than one that maintains order.. but I don't have any say in it ;)
<dkubb>
oh neat, it looks like when you overwrite a hash value the new value keeps the position of the original
<dkubb>
hehe, it's funny that I don't know this. I've been trying to write cross platform ruby for so long
<snusnu>
sweet! thx for testing that! … and yeah, i'd actually would at least like to have 2 different hashes, a plain and an ordered one
<snusnu>
also, hehe
<dkubb>
bbiab, dinnertime
cored has quit [Read error: Connection reset by peer]
postmodern has joined #rom-rb
xargoon has quit [Ping timeout: 264 seconds]
xargoon has joined #rom-rb
knowtheory has quit [Ping timeout: 264 seconds]
knowtheory has joined #rom-rb
snusnu has quit [Quit: Leaving.]
zekefast has joined #rom-rb
xargoon has quit [Ping timeout: 248 seconds]
xargoon has joined #rom-rb
knowtheory has quit [Ping timeout: 264 seconds]
mbj has joined #rom-rb
<mbj>
dkubb: morning
<mbj>
dkubb: sitting in that train :D
<mbj>
dkubb: You had some new ideas regarding mutant and the strategy dilemma?
zekefast has quit [Quit: Leaving.]
knowtheory has joined #rom-rb
dkubb has quit [Ping timeout: 252 seconds]
dkubb has joined #rom-rb
<dkubb>
mbj: good evening
<dkubb>
mbj: yeah, I don't think anything groundbreaking. just the observation that if we're documenting our public interface methods in isolation that should be enough to get full mutation coverage
fphilipe has joined #rom-rb
mbj has quit [Ping timeout: 240 seconds]
<dkubb>
mbj: so rather than relying only on visibility, we use @api and @private to determine what needs mutation coverage. in the absence of those tags we default to the current behaviour
mbj has joined #rom-rb
<dkubb>
mbj: heh, I keep missing you. I left a few tiny notes in the irc log
<mbj>
dkubb: hehe, internet is very unstable here
<mbj>
dkubb: south germany lots of hills and tunnels
<mbj>
dkubb: Will catch up via logger
<dkubb>
mbj: no worries. I'm logging off for the night anyway. will ttyl
mbj has quit [Read error: Connection reset by peer]
fphilipe has quit [Ping timeout: 246 seconds]
mbj has joined #rom-rb
dbussink_ has joined #rom-rb
dkubb has quit [*.net *.split]
dbussink has quit [*.net *.split]
jdsiegel has quit [*.net *.split]
coxandrew has quit [*.net *.split]
kapowaz has quit [*.net *.split]
shingara has quit [*.net *.split]
coxandrew has joined #rom-rb
jdsiegel has joined #rom-rb
dkubb has joined #rom-rb
shingara has joined #rom-rb
kapowaz has joined #rom-rb
zekefast has joined #rom-rb
mbj has quit [Read error: Connection reset by peer]
mbj has joined #rom-rb
mbj has quit [Ping timeout: 264 seconds]
mbj has joined #rom-rb
postmodern has quit [Quit: Leaving]
cored has joined #rom-rb
knowtheo1y has joined #rom-rb
knowtheory has quit [Ping timeout: 264 seconds]
knowtheory has joined #rom-rb
knowtheo1y has quit [Ping timeout: 246 seconds]
dbussink_ has quit [Quit: bye]
dbussink has joined #rom-rb
snusnu has joined #rom-rb
<snusnu>
mbj: yo, around?
<snusnu>
mbj: quick one: are you fine with registering an optional (otherwise empty) failure chain for every chain? it would get called in case any registered handler raises an uncaught exception
<snusnu>
mbj: that would make sure that *every* chain returns a valid response (wrapping the exception and the "closest" response available at failure time
knowtheory has quit [Quit: Computer has gone to sleep]
knowtheory has joined #rom-rb
snusnu has quit [Quit: Leaving.]
snusnu has joined #rom-rb
<dkubb>
good morning
mbj has quit [Ping timeout: 240 seconds]
<snusnu>
dkubb: hey
<snusnu>
dkubb: did you run into situations where you asserted sth like: it { should eql(something) } .. and expected and actual actually are printed out as equal by rspec (i.e. the diff is empty) but still it fails?
<snusnu>
dkubb: could it be that i need "equalized" exceptions for this to work?
<snusnu>
dkubb: all other objects in the graph are equalized properly
<dkubb>
snusnu: interesting..
<dkubb>
snusnu: do mocks have proper #eql? and #== methods?
<snusnu>
dkubb: yeah, i suppose so, i use them quite in a lot of places, and never had a problem
<snusnu>
dkubb: the only thing different with this spec, is that the asserted object actually is composed of some data and the raised and rescued exception instance
<dkubb>
snusnu: yeah, I think you're right. I've never seen Spec::Exception before, what is that from?
<snusnu>
dkubb: the Spec::Exception you see in the pastie, is an instance of: module Spec; Exception = Class.new(StandardError); end
Guest96408 is now known as mongrelion
mongrelion has quit [Changing host]
mongrelion has joined #rom-rb
<snusnu>
dkubb: i just tried that because regular RuntimeError didn't work either
<dkubb>
snusnu: what's your Equalizer definition look like for Substation::Response::Failure ?
<snusnu>
dkubb: include Equalizer.new(:request, :output)
<snusnu>
dkubb: output in the case of this spec is an instance of FailureData which equalizes on #data and #exception
<snusnu>
dkubb: hence, my assumption that equalizing on #exception doesn't work
<dkubb>
snusnu: as a test, try both Equalizer.new(:request) and Equalizer.new(:output) .. if one of them works, then you've isolated the problem to the other attribute
<snusnu>
dkubb: heh, it works if i equalize on :request only
<snusnu>
dkubb: but i obviously don't want to do that :)
<dkubb>
snusnu: well yeah, but at least you know there's something wrong inside the output equalizer
<snusnu>
dkubb: so yeah, the problem is within equalizing :output .. the exception most likely
<snusnu>
dkubb: yeah
<dkubb>
snusnu: what I would do next is go to the output object, and manually make an #eql? method that equalizes on some, but not all, of the attributes. through the process of elimination you should be able to narrow it down
<snusnu>
dkubb: heh of course: puts exception.eql?(other.exception).inspect #=> false
<dkubb>
snusnu: sure, old-school debugging ftw
<snusnu>
dkubb: yo!
<snusnu>
thx for the tip!
<dkubb>
I debug like that all the time. it's not pretty, but it gets the answer I'm looking for fast, which is all that matters
<snusnu>
true
<snusnu>
dkubb: so i wonder, should i just not equalize on FailureData#exception or equalize manually on some more specific part of exception, like the message
<snusnu>
dkubb: or do a regular assertion on exception.message
<dkubb>
snusnu: I always think of an exception message as being for the end user, but if I was comparing exceptions I would only compare on the class
<snusnu>
dkubb: btw, the idea behind that code is that for every substation chain, you can declare a failure chain that gets invoked in case any of the registered processors doesn't rescue from an exception
<dkubb>
snusnu: if you are raising two exceptions with different meaning, but using the same class, I think that's probably wrong
<snusnu>
dkubb: dunno if it'd be feasible for equalizer to detect exception instances?
<snusnu>
i guess not
<dkubb>
snusnu: what about defining the exception to compare on classes only in it's #eql?
<snusnu>
dkubb: well, the point is to provide a "completely failsafe" chain .. that code gets invoked in case any called handler in a chain doesn't rescue from a raised exception
<snusnu>
dkubb: so, it's not only for client defined exceptions, but really anything (inheriting from StandardError)
<dkubb>
ahh ok
<snusnu>
dkubb: the point being, i don't ever want to bail out of a web action, i *always* want to render/serialize something
<dkubb>
what would happen if you did include Equalizer.new(:data), and then in your #eql? method you did: super && other.instance_of?(exception.class)
<snusnu>
dkubb: yeah, i just refactored to that, while it's a bit more code, it's better to rely on super imo
<dkubb>
snusnu: does concord setup the equalizer too?
<dkubb>
snusnu: btw, you *could* make data and exception protected if you didn't want to expose that in the public interface
<snusnu>
dkubb: yeah, concord sets up the equalizer, that's why i can't use it in that case … and i need #data and #exception to be publicly accessible, as i need that for further processing (like, displaying an error page, or app internal "trackers" that record errorneous usecases
<snusnu>
dkubb: all substation cares about, is that it makes the info accessible to the outside, clients can obviously do what they want
<snusnu>
dkubb: it's a bit unfortunate tho, i started out with FailureData = Struct.new(:data, :exception) and i really thought that would be enough .. all this additional code is simply because of exception equalizing behavior :/
<dkubb>
snusnu: I wonder if we can break up equalizer to create overridable comparison methods per attribute
<dkubb>
snusnu: so maybe we'd have data_eql?(other_data) and exception_eql?(other_exception), both as private methods that you could override
<dkubb>
snusnu: then you could use concord, and then override exception_eql? and that'd be it
<snusnu>
dkubb: that would actually be nice yeah! i could definitely use that, then again i wonder, since i'm (probably?) the first one to encounter it .. is it already worth it?
<snusnu>
dkubb: otoh, i see no downsides
<dkubb>
snusnu: well, we can watch for more cases. it's an easy change I think
<snusnu>
dkubb: yeah, shouldn't be too hard
<dkubb>
snusnu: if we see it pop up one or two more times, we can make that change without breaking anything
<snusnu>
dkubb: yeah, let's do that
<dkubb>
snusnu: I may have a case in Axiom::Relation#eql? where I could've used this. I like how equalizer works, I much prefer it to writing my own equalization methods, because inevitably I end up defining an #eql? without making a corresponding #hash method that works the same way
<dkubb>
snusnu: which I think you may have done in this case actually ;)
<dkubb>
snusnu: although it's not really a bug. all it means is two objects that could be eql could have different #hash values. in a Hash, since they'll never occupy the same "slot", they'll never really be compared to equality so it doesn't matter too much
<dkubb>
snusnu: I would probably define a #== that works the same way though.. #eql? and #hash are more for Hash to use, while #== is more for end users to rely on
<dkubb>
snusnu: so the cmp? method accepts an operator and the other object, then you could do: def cmp?(comparator, other) super && exception.send(comparator, other.exception) end
<dkubb>
snusnu: then you get your #eql? and #== defined for you
<dkubb>
snusnu: this might even allow you to use concord now, without needing to make those exception_eql? methods
<snusnu>
dkubb: interesting, what about #hash tho?
<snusnu>
dkubb: hmm, can't get it to work with just def cmp?(comparator, other) super && exception.class.send(comparator, other.exception.class) end
<snusnu>
dkubb: wait, it works of course
<dkubb>
:)
<dkubb>
snusnu: it doesn't do anything for #hash. you'd still have to do what you're doing now.. except s/to_hash/hash/ of course ;)
<snusnu>
dkubb: of course i can only equalize on :data then .. which still means i can't use concord, but it's less code
<dkubb>
ahh ok
pdswan has quit [Quit: pdswan]
<dkubb>
yeah, less code == less potential bugs
<snusnu>
yup
<dkubb>
so I guess we *could* define cmp_exception? and cmp_data? inside equalizer and it would fit in with the way it's designed
pdswan has quit [Read error: Connection reset by peer]
fphilipe has joined #rom-rb
xargoon has quit [Read error: Operation timed out]
pdswan has joined #rom-rb
xargoon has joined #rom-rb
fphilipe has quit [Ping timeout: 240 seconds]
postmodern has joined #rom-rb
snusnu has quit [Quit: Leaving.]
knowtheo1y has joined #rom-rb
snusnu has joined #rom-rb
snusnu1 has joined #rom-rb
knowtheory has quit [Ping timeout: 240 seconds]
knowtheo1y has quit [Ping timeout: 264 seconds]
snusnu has quit [Ping timeout: 264 seconds]
knowtheory has joined #rom-rb
mbj has joined #rom-rb
<mbj>
hol
<mbj>
a
<solnic>
mbj: hey
<mbj>
solnic: hola
<mbj>
solnic: Thinking hard to simplify rspec test selection for mutant
<mbj>
I more and more think we should move to "free form", just loading spec/**/*_spec.rb and selecting tests via description.
<mbj>
So removing all "strategy" implementations.
<mbj>
We'd start with a most narrow selection, if we dont find a spec go up with scope.
<mbj>
So giving Foo::Bar#baz as subject the could would look for a spec with description Foo::Bar#baz, than Foo::Bar, than Foo
<mbj>
Most likely I'll have to fight rspec world preserving issues again, but it should speed up the whole killing process a lot.
zekefast has joined #rom-rb
<solnic>
mbj: sounds good
<solnic>
mbj: if performance is still a problem we could maybe run stuff in parallel
<solnic>
mbj: I think the most important thing is to not have mutant forcing you to write specs that test implementation details
<solnic>
this is what happens with --rspec-dm2 strategy
<solnic>
mutant changes behavior so if tests check that behavior they will break
<mbj>
solnic: Yeah
<mbj>
We already have per subject speed numbers, the summary could list the 10 slowest subjects, so developer can decide if he has to come up with more narrow specs for these.
<solnic>
mbj: I'm also not sure if writing spec for every-single-public-method makes sense
<mbj>
In the strategy I showed above mutant will be happy if it can kill mutations somehow
<mbj>
It first checks if a dedicated spec exists
<mbj>
Than goes up a level etc.
<solnic>
yeah that's good
<mbj>
So you can put all stuff in a giant "app_spec.rb" if you like :D
<solnic>
I'm curious what dkubb thinks about this bkz I saw him writing that you should have specs for every public method
<mbj>
I like this sceme to spec all public methods, but I dont think mutant should enforce this.
<mbj>
I want to be sloppy also, sometimes. Refactoring is a good example.
<mbj>
As a nice side effect of "preloading" spec/**/*_spec.rb kill times will be reduced.
<mbj>
currently the code within spec/ gets reparsed and loaded into the vm per mutation killfork.
<solnic>
there are cases where it feels silly
<solnic>
for instance there's #track and #tracked? in rom-session
<solnic>
I don't feel like writing specs for tracked?
<solnic>
because I can use that method inside spec for #track
<mbj>
Jo
<solnic>
there are plenty of cases like that
<mbj>
For me there is only one rule: Kill mutations.
<solnic>
yeah that's why we use mutant :)
<mbj>
And that efficiently. And efficience in mutation kills is not only about the mutant runtime :D
<solnic>
we just need to improve the way we write specs IMHO
<mbj>
If you spend 6 addtional hours for a 30sec mutant runtime improvement something is wrong.
<solnic>
and maybe improve how mutant runs stuff
<mbj>
sync
<mbj>
Have to go to sleep now to sync my rythm
<solnic>
:)
<solnic>
me too
<mbj>
woke up 04:00 in the morning to take a train, slept in train for some hours, client meeting coding, slept from 19:00 till 23:00
<solnic>
I'm in Oslo by the way, it just got dark here
<mbj>
And now I'm awake, but not awake... scary.
<mbj>
hehe
<mbj>
Greez from munich
<solnic>
:)
<solnic>
ok sleep tight then and ttyt
<mbj>
I could also do some coding, and still need to finalize some stuff for tomorrow. But dunno of some additional hours sleeping will improve or decrease my coding.
<mbj>
My net sleep hours is okay: 8-9 hours in 24h
<mbj>
But the distribution is suboptimal :D
<mbj>
solnic: I think I'll first target some more low hanging fruits in mutant, adding --fail-fast is easy
<mbj>
snusnu: sitting in hotel in munich and trying to debug how I misuse substation
<mbj>
But I'll go to sleep now, just letting you know I'm lost a bit. But cannot summarize a real questio now.-
<snusnu>
mbj: heh ok, well, feel free to ask anytime
<mbj>
snusnu: the demo is still uptodate?
<snusnu>
mbj: no
<snusnu>
mbj: in fact, lemme push the newest changes
<mbj>
snusnu: thx!
<snusnu>
mbj: well, not to the demo, but to substation
<snusnu>
:)
<mbj>
omg :D
<mbj>
snusnu: But the demo is in sync with latest release?
<snusnu>
mbj: i added default failure chains per chain .. invoked in case any handler didn't rescue an exception
<snusnu>
mbj: nope, not really i guess
<snusnu>
mbj: not sure tho
<mbj>
snusnu: okay
<snusnu>
mbj: lemme quickly show you what works now
<mbj>
snusnu: But the README is in sync with the cod I guess?
<mbj>
*code
<snusnu>
mbj: if you dig that, you know what can be done (in a few minutes): http://pastie.org/8126207
<snusnu>
mbj: yeah, the readme is good
<snusnu>
mbj: only, it doesn't contain "everything"
<snusnu>
mbj: that's part of the beauty tho, substation provides building blocks
<mbj>
heh
<snusnu>
mbj: you can use it to build apps, imo it makes no sense to add a complete app to the readme
<mbj>
I have to admit I'm kinda lost iwth the lastest changs.
<snusnu>
mbj: it could be built differently anyway
<mbj>
yeah
<snusnu>
mbj: what's changed between you last synced, is the possibility to register a failure chain for every processor, plus a failure chain *per chain*, that gets invoked for any uncaught exception inheriting from standard error
<mbj>
But maybe I need A single app that works and uses the feature :D
<snusnu>
mbj: it's obviously stupid, but exerices all features in (soon to be) master
<mbj>
Somehow the base concepts still make lots of sense, but all that code around failure chains make me stare and say: Looks nice, but I cannot use it.
<mbj>
But this is from the fact I dont really have time to let the message sink in.
<mbj>
Why do you have a Handler::Result class?
<snusnu>
mbj: this just shows how to implement a handler that supports the same interface as ducktrap and vanguard (they're both used with Processor::Evaluator::Data)
<snusnu>
mbj: ducktrap and vanguard are plugged into substation via the builting Evaluator::Data
<snusnu>
mbj: the authentication and authorization handlers in that example are also plugged in using Evaluator::Data .. hence they must support that interface
<mbj>
okay
<snusnu>
mbj: i might push that down to substation proper, but i guess it's not worth it atm
<snusnu>
i mean, the result class
<mbj>
got it
<snusnu>
it's so simple, why care
<mbj>
snusnu: A JSON unserializer processor would look like? Input is the full request with #content_type and #body.
<snusnu>
mbj: the failure chains are really useful imo, they can centralize behavior on failure
<mbj>
I see how I can use Substation::Processor::Data to connect with vanguard and ducktrap
<snusnu>
mbj: yeah, there are Evaluator::Data which passes request.input to handler#call, and there's Evaluator::Request which passes the complete request to handler#call
<mbj>
makes sense
<snusnu>
mbj: that connection is done in the demo too iirc
<mbj>
jo
<snusnu>
vanguard and ducktrap work fine
<mbj>
But just show me a JSON unserializer
<mbj>
where in put is the Rack::Request instance.
<snusnu>
as incoming or outgoing processor?
<mbj>
unserializer is incoming :D
<mbj>
so items of a chain are now named "processor" ?
<snusnu>
ok, you'd use Evaluator::Data or Evaluator::Data
<snusnu>
well, processors take care of the flow through the chain, and handlers are the actual client code that perform work
<snusnu>
so processors are responsible for invoking handlers
<snusnu>
and for passing on the result to the next processor
<snusnu>
or invoke a failure chain
<mbj>
I was using from the day where we did not had processors.
<mbj>
So most likely this is my fault.
<snusnu>
no, we always had them
<snusnu>
they were called handlers before
<mbj>
ahh
<snusnu>
but i decided to promote them to processors, and add them to substation proper .. and handlers are now the arguments sent to the processors in the dsl .. basically, your client code
<mbj>
Okay.
<mbj>
so can you show me that json unserializer processor
<snusnu>
because Evaluator::{Data, Request, Pivot, Transformer, Wrapper} are generic concepts
<snusnu>
mbj: this assumes that once the chain reaches unserialize, it has something in request.input, that responds to #body
<mbj>
snusnu: What about failure signalling?
<mbj>
snusnu: So if I check request.input.content_type before
<mbj>
And want to stop here if it doesnt match my expecations?
<snusnu>
mbj: an unserialize specific failure chain can be done like this: http://pastie.org/8126253
<snusnu>
mbj: you'd implement that check inside a dedicated handler, register it using Evaluator::{Data, Request} processor, and perform the check inside handler#call
<mbj>
snusnu: I dont get that one
<snusnu>
mbj: what, the failure chain or the content type thing?