Bad news first

We’ve had some trouble with the methods I added to NSDate: time in particular is in conflict with some other method - probably and internal private method. the time method returned a 3-item array with the hour, minute, and second. the other two methods that are also getting updated are date and datetime. the new names:

time      =>  time_array
date      =>  date_array
datetime  =>  datetime_array

Besides that, these methods were misleading. Does time return a Time object? Now it is clearer that an Array is returned.

If you are using these methods… sorry! All I can say is s/\.(date|time|datetime)\b/.$1_array/

Other than that, what’s new?

Since the last announcement (v0.8.5), here’s the new stuff.

NSDate additions, especially delta, which was damn hard to write

If you’ve been keeping up with the git repo, you might have seen the NSDate/Time additions. I think the start_of_day and end_of_day methods are worth pointing out, because they make it easy to check for “today’s events”.

“`ruby now = Time.new fromtime = now.startofday untiltime = now.endofday

if fromtime <= event and event < untiltime dates << event end ”`

If you’re building a calendar, you might find the days_in_month and days_in_year methods helpful. Takes into account leap years. Speaking of which, date.leap_year? returns true if the year of the date is a leap year (e.g. any date in 2012 will return true).

What is delta and why aren’t you talking about it!?

I think NSDate#delta is really cool. It was awful to write, and I need to make tests for it (I need tests for ALL of sugarcube! For shame! I could use help on this, actually).

From the README:

“`ruby (main)> feb282012 = Time.at(1330473600) => 2012-02-28 17:00:00 -0700

add an hour or two

(main)> feb282012.delta(hours:1) => 2012-02-28 18:00:00 -0700 (main)> feb282012.delta(hours:2) => 2012-02-28 19:00:00 -0700

add some days

(main)> feb282012.delta(days:1) => 2012-02-29 17:00:00 -0700 (main)> feb282012.delta(days:2) => 2012-03-01 17:00:00 -0700

how about a month?

(main)> feb282012.delta(months:1) => 2012-03-28 17:00:00 -0600 # look, the time didn’t change, event though there was a DST change!

cool, but if you want a more literal "24 hours”, specify a time unit

(main)> feb282012.delta(months:1, hours:0) => 2012-03-28 18:00:00 -0600 # disable the DST fix by specifying hours, minutes, or seconds (a “precise” delta)

in one year, it will still be Feb 28th

(main)> feb282012.delta(years:1) => 2013-02-28 17:00:00 -0700

and we already know what adding a day looks like

(main)> feb282012.delta(days:1) => 2012-02-29 17:00:00 -0700

a year and a day is tricky, because do we add a day, then a year? or add a

year and then a day? well, i’ll tell you, I add a day and then a year,

which is feb 29th, which is no good, and the algorithm rolls back days to the

last day of the month, so we get the 28th.

(main)> feb282012.delta(days:1, years:1) => 2013-02-28 17:00:00 -0700

adding 2 days puts us into March, which then “looks right”, but it’s both

right AND wrong, depending on how you look at it. Another example is below,

where we add a month to January 30th. Really, though, think of this: how

often do you need to add a year AND a day!? Adding a year is more common, and

this is showing that adding a year to Feb 29th will give you Feb 28th, which I

think is better than March 1st.

(main)> feb282012.delta(days:2, years:1) => 2013-03-01 17:00:00 -0700

Crazier: add a day (fab 29th), then a month (march 29th), THEN a year.

(main)> feb282012.delta(days:1, years:1, months:1) => 2013-03-29 17:00:00 -0600

k, for the next examples, we need a new date, and this is a non-leap year.

(main)> jan292013 = feb282012.delta(days:1, months:11) => 2013-01-29 17:00:00 -0700

what is 1/29/2013 plus two months? easy! march 29, 2013

(main)> jan292013.delta(months:2) => 2013-03-29 17:00:00 -0600

Yeah, smart guy? Well then what is 1/29/2013 plus ONE month. It’s feb 28th.

When someone says “see you in a month!” they mean “next month”, not “in the

early part of two months in the future”, which is where the math will take you

if you don’t add a “day of month” correction.

(main)> jan292013.delta(months:1) => 2013-02-28 17:00:00 -0700

but last year was a leap year, so we should get Feb 29th, 2012:

(main)> jan292013.delta(months:1, years: -1) => 2012-02-29 17:00:00 -0700 # success!

do other deltas work in reverse? fuuuuuu…

(main)> jan292013.delta(months:-11) => 2012-02-29 17:00:00 -0700

…ck yeah! :-)

unfortunately you will, in the edge cases, end up with stuff like this:

(main)> feb282012 == feb282012.delta(days:1, months:12).delta(months:-12) => true “`

Numeric conversions might look odd to you, and that’s OK

Okay, no one EVER uses degrees, right? Always radians. So converting FROM radians is not as common, and when you do it you are doing some gnarly floating point division, so you won’t get nice whole numbers

> 91.0 / 180.0 * Math::PI / Math::PI * 180.0
=> 90.9999694824219

So, anyway, there’s my justification for this:

(main)> 90.degrees
=> 1.57079601287842
(main)> 91.degrees
=> 1.58824872970581

# bear with me...
(main)> Math::sin(90.degrees)
=> 1.0

# and that's not all!  How about those multiples of pi!?
(main)> Math::sin(0.5.pi)
=> 1.0
(main)> Math::sin(1.5.pi)
=> -1.0
# awww, yeah.

I’m sorry if that offends you, but I think it looks great! rotate 45.degrees just looks COOL to me! I hope you agree. Different strokes for different code monkies, I guess.

When would you not want to present a full screen modal? That’s crazy. I had a bug where my modal was appearing BENEATH my custom tabs. No bueƱo! So I wrote these bad boys to get it done right everytime:

”`ruby include SugarCube::Modal

presentmodal(mymodal_controller)

dismiss_modal “`

The presentViewController method expects a target, which is the ViewController presenting your modal. I’m using UIApplication.sharedApplication.keyWindow.rootViewController for this target, which means that no matter WHERE you call it from, your modal view controller will be on top of all the other views. BAM.

UITextView is not a UIControl but what-do-i-care!?

The things I do with UIControls are pretty simple. It’s change events. That’s what I listen for, and they make it easy to do, right? But UITextView just HAAAD to be different, and instead emits notifications using the NSNotficationCenter. I’ve add the on :event methods to UITextView so you don’t have to care, and can treat it like another (SugarCube-powered) UIControl.

EXCEPT! Notifications are observed, and you have to STOP observing them at some point, or something bad happens (think LOST and the weird timer from season 1).

”`ruby textcontrol = UITextField.new textview = UITextView.new

textcontrol.on :change { p textcontrol.text }

textview.on :change { p textview.text }

textcontrol.off textview.off “`

More NSNotfication additions

Notifications are strings, pretty much. I mean, they start as strings, and then you emit them and listen for them. But at their heart, they are strings.

so.

”`ruby

my_notification = i’m not telling, it’s a secret.

mynotification.postnotification mynotification.addobserver(self, :someeventmethod) mynotification.removeobserver(self)

p my_notification => “my Notification” # see? it was a darn string the whole time! “`

It’s important to unobserve. Again, think of that evil death timer. Scary stuff.

Where to now!?

I’m concerned about the cross-talk between BubbleWrap and SugarCube. I’m trying to keep the theme of SugarCube to be "getting work done”, and in that vein I’m never going to build the wrappers (HTTP, EventMachine) that BubbleWrap has, and I’m not gonna compete with the App and Device singletons that BubbleWrap offers. They do what they do perfectly, and I already use them extensively.

That said, I have a different vision of the gesture recognizers, and I think they have a place in SugarCube. We’re already offering competing versions of observation (actually, the two versions are complementary - BubbleWrap supports blocks, while SugarCube supports target/action), maybe it’s good to have these different wrappers.

Please tell me what you think.