Permalink
Browse files

decouple from strftime

  • Loading branch information...
1 parent 2bf058c commit 7507ffbd1382b6bf63bea37c9f11d1621b80d1f1 @kbrock kbrock committed with Dec 9, 2012
View
@@ -0,0 +1,11 @@
+## Contributing to stamp
+
+* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
+* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
+* Fork the project
+* Run `bundle install`
+* Run `rake` to execute the cucumber specs and make sure they all pass
+* Start a feature/bugfix branch
+* Commit and push until you are happy with your contribution
+* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
+* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
View
@@ -1,7 +1,7 @@
# stamp
Format dates and times based on human-friendly examples, not arcane
-strftime directives.
+[strftime](http://strfti.me) directives.
[![Build Status](https://secure.travis-ci.org/jeremyw/stamp.png)](http://travis-ci.org/jeremyw/stamp)
@@ -15,7 +15,7 @@ Your Ruby dates and times get a powerful new method: `stamp`.
You might be concerned that "stamp" isn't descriptive enough for developers
reading your code who aren't familiar with this gem. If that's the case, the
-following aliases are available:
+following aliases are provided:
* `stamp_like`
* `format_like`
@@ -27,11 +27,11 @@ and weekday parts you'd like, and your date will be formatted accordingly:
```ruby
date = Date.new(2011, 6, 9)
-date.stamp("March 1, 1999") #=> "June 9, 2011"
-date.stamp("Jan 1, 1999") #=> "Jun 9, 2011"
+date.stamp("March 1, 1999") #=> "June 9, 2011"
+date.stamp("Jan 1, 1999") #=> "Jun 9, 2011"
date.stamp("Jan 01") #=> "Jun 09"
-date.stamp("Sunday, May 1, 2000") #=> "Thursday, June 9, 2011"
-date.stamp("Sun Aug 5") #=> "Thu Jun 9"
+date.stamp("Sunday, May 1, 2000") #=> "Thursday, June 9, 2011"
+date.stamp("Sun Aug 5") #=> "Thu Jun 9"
date.stamp("12/31/99") #=> "06/09/11"
date.stamp("DOB: 12/31/2000") #=> "DOB: 06/09/2011"
```
@@ -50,11 +50,11 @@ hours, minutes, and seconds when it sees colon-separated values.
```ruby
time = Time.utc(2011, 6, 9, 20, 52, 30)
-time.stamp("3:00 AM") #=> " 8:52 PM"
+time.stamp("3:00 AM") #=> "8:52 PM"
time.stamp("01:00:00 AM") #=> "08:52:30 PM"
time.stamp("23:59") #=> "20:52"
time.stamp("23:59:59") #=> "20:52:30"
-time.stamp("Jan 1 at 01:00 AM") #=> "Jun 9 at 08:52 PM"
+time.stamp("Jan 1 at 01:00 AM") #=> "Jun 9 at 08:52 PM"
time.stamp("23:59 UTC") #=> "20:52 PST"
```
@@ -76,53 +76,29 @@ For example, "01/09" could refer to January 9, September 1, or
January 2009. More explicit examples include "12/31", "31/12", and "12/99".
Using unambiguous values will also help people who read the code in the
-future understand your intent.
+future, including yourself, understand your intent.
### Rails Integration
-Stamp makes it easy to configure your application's common date and time
-formats in a more self-documenting way with the `strftime_format` method:
+Stamp makes it easy to configure your Rails application's common date and time
+formats in a more self-documenting way with `DATE_FORMATS`:
```ruby
-# config/initializers/time_formats.rb
-Date::DATE_FORMATS[:short] = Stamp.strftime_format("Mon Jan 1")
-Time::DATE_FORMATS[:military] = Stamp.strftime_format("23:59")
+# config/initializers/date_formats.rb
+Date::DATE_FORMATS[:short] = Proc.new { |date| date.stamp("Sun Jan 5") }
+Time::DATE_FORMATS[:military] = Proc.new { |time| time.stamp("5 January 23:59") }
```
To use your formats:
```ruby
Date.today.to_s(:short) #=> "Sat Jul 16"
-Time.now.to_s(:military) #=> "15:35"
+Time.now.to_s(:military) #=> "16 July 15:35"
```
### Limitations
-* DateTime should inherit stamp behavior from Date, but it hasn't been thoroughly tested. Patches welcome!
-
-### Advanced Usage
-
-If you need more obscure formatting options, you can include any valid
-[strftime](http://strfti.me) directives in your example string, and they'll
-just be passed along:
-
-```ruby
-Date.today.stamp("Week #%U, 1999") #=> "Week #23, 2011"
-```
-
-Check out [http://strfti.me](http://strfti.me) for more ideas.
-
-## Contributing to stamp
-
-* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
-* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
-* Fork the project
-* Run `bundle install`
-* Run `rake` to execute the cucumber specs and make sure they all pass
-* Start a feature/bugfix branch
-* Commit and push until you are happy with your contribution
-* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
-* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
+* `DateTime` should inherit stamp behavior from `Date`, but it hasn't been thoroughly tested. Patches welcome!
## Copyright
View
@@ -14,15 +14,15 @@ Feature: Stamping a date
| example | output |
| January | September |
| Jan | Sep |
- | Jan 1 | Sep 8 |
+ | Jan 1 | Sep 8 |
| Jan 01 | Sep 08 |
| Jan 10 | Sep 08 |
- | Jan 1, 1999 | Sep 8, 2011 |
+ | Jan 1, 1999 | Sep 8, 2011 |
| Jan 12, 1999 | Sep 08, 2011 |
| 13 January 1999 | 08 September 2011 |
| Monday | Thursday |
- | Tue, Jan 1 | Thu, Sep 8 |
- | Tuesday, January 1, 1999 | Thursday, September 8, 2011 |
+ | Tue, Jan 1 | Thu, Sep 8 |
+ | Tuesday, January 1, 1999 | Thursday, September 8, 2011 |
| 01/1999 | 09/2011 |
| 01/01 | 09/08 |
| 01/31 | 09/08 |
@@ -93,19 +93,19 @@ Feature: Stamping a date
Examples:
| example | output |
- | Jan 1, 1999 8:59 am | Sep 8, 2011 1:31 pm |
+ | Jan 1, 1999 8:59 am | Sep 8, 2011 1:31 pm |
| 08:59 AM 1999-12-31 | 01:31 PM 2011-09-08 |
- | Date: Jan 1, 1999 Time: 8:59 am | Date: Sep 8, 2011 Time: 1:31 pm |
+ | Date: Jan 1, 1999 Time: 8:59 am | Date: Sep 8, 2011 Time: 1:31 pm |
Scenario: strftime directives just get passed through
Given the date December 21, 2012
- When I stamp the example "John Cusack was in a movie about %b %d, %Y, but it wasn't very good."
- Then I produce "John Cusack was in a movie about Dec 21, 2012, but it wasn't very good."
+ When I stamp the example "John Cusack was in a movie about Jan (%-m) %e, %Y, but it wasn't very good."
+ Then I produce "John Cusack was in a movie about Dec (%-m) %e, %Y, but it wasn't very good."
Scenario: Plain text just gets passed through
Given the date June 1, 1926
- When I stamp the example "Marilyn Monroe was born on January 1, 1999."
- Then I produce "Marilyn Monroe was born on June 1, 1926."
+ When I stamp the example "Marilyn Monroe was born on January 9, 1999."
+ Then I produce "Marilyn Monroe was born on June 1, 1926."
Scenario Outline: Aliases for the stamp method
Given the date December 9, 2011
View
@@ -1,25 +1,19 @@
require "date"
require "time"
+require "stamp/emitters/modifiable"
+require "stamp/emitters/am_pm"
+require "stamp/emitters/composite"
+require "stamp/emitters/delegate"
+require "stamp/emitters/lookup"
+require "stamp/emitters/ordinal"
+require "stamp/emitters/string"
+require "stamp/emitters/two_digit"
require "stamp/translator"
require "stamp/version"
module Stamp
- # Transforms the given example dates/time format to a format string
- # suitable for strftime.
- #
- # @param [String] example a human-friendly date/time example
- # @param [#strftime] the Date or Time to be formatted. Optional, but may
- # be used to support certain edge cases
- # @return [String] a strftime-friendly format
- #
- # @example
- # Stamp.strftime_format("Jan 1, 1999") #=> "%b %e, %Y"
- def self.strftime_format(example, target=nil)
- Stamp::StrftimeTranslator.new(target).translate(example)
- end
-
# Formats a date/time using a human-friendly example as a template.
#
# @param [String] example a human-friendly date/time example
@@ -28,26 +22,11 @@ def self.strftime_format(example, target=nil)
# @example
# Date.new(2012, 12, 21).stamp("Jan 1, 1999") #=> "Dec 21, 2012"
def stamp(example)
- strftime(strftime_format(example))
+ Translator.new.translate(example).format(self)
end
alias :stamp_like :stamp
alias :format_like :stamp
-
- # Transforms the given example date/time format to a format string
- # suitable for strftime.
- #
- # @param [String] example a human-friendly date/time example
- # @return [String] a strftime-friendly format
- #
- # @example
- # Date.today.strftime_format("Jan 1, 1999") #=> "%b %e, %Y"
- def strftime_format(example)
- # delegate to the class method, providing self as a target value to
- # support certain edge cases
- Stamp.strftime_format(example, self)
- end
-
end
Date.send(:include, ::Stamp)
-Time.send(:include, ::Stamp)
+Time.send(:include, ::Stamp)
@@ -0,0 +1,22 @@
+module Stamp
+ module Emitters
+ class AmPm
+ include Modifiable
+
+ AM = 'am'
+ PM = 'pm'
+
+ def initialize(&block)
+ @modifier = block
+ end
+
+ def format(target)
+ modify(target.hour < 12 ? AM : PM)
+ end
+
+ def field
+ nil
+ end
+ end
+ end
+end
@@ -0,0 +1,29 @@
+module Stamp
+ module Emitters
+ class Composite
+ include Enumerable
+
+ EMPTY_STRING = ''
+
+ def initialize
+ @emitters = []
+ end
+
+ def format(target)
+ @emitters.map { |e| e.format(target) }.join(EMPTY_STRING)
+ end
+
+ def <<(emitter)
+ if emitter.is_a?(Enumerable)
+ emitter.each { |e| @emitters << e }
+ else
+ @emitters << emitter
+ end
+ end
+
+ def each(&block)
+ @emitters.each(&block)
+ end
+ end
+ end
+end
@@ -0,0 +1,19 @@
+module Stamp
+ module Emitters
+ class Delegate
+ include Modifiable
+
+ attr_reader :field
+
+ # @param [field] the field to be formatted (e.g. +:month+, +:year+)
+ def initialize(field, &block)
+ @field = field
+ @modifier = block
+ end
+
+ def format(target)
+ modify(target.send(field))
+ end
+ end
+ end
+end
@@ -0,0 +1,27 @@
+module Stamp
+ module Emitters
+ class Lookup
+ attr_reader :field
+
+ # @param [field] the field to be formatted (e.g. +:month+, +:year+)
+ # @param [lookup] an array of the string values to be formatted (e.g. +Date::DAYNAMES+)
+ # or a +call+able that returns the formatted value
+ def initialize(field, lookup=nil)
+ @field = field
+ @lookup = lookup
+ end
+
+ def format(target)
+ lookup(target.send(field))
@kbrock

kbrock Dec 12, 2012

Contributor
  modify(target.send(field))
+ end
+
+ def lookup(value)
+ if @lookup.respond_to?(:call)
+ @lookup.call(value)
+ else
+ @lookup[value]
+ end
+ end
@kbrock

kbrock Dec 12, 2012

Contributor
  def modify(value)
    @lookup[value]
  end

if they wanted to pass a block, just use Delegate
(or merge delegate into here)

+ end
+ end
+end
@@ -0,0 +1,9 @@
+module Modifiable
+ def modify(value)
+ if @modifier
+ @modifier.call(value)
+ else
+ value
+ end
+ end
+end
@@ -0,0 +1,16 @@
+module Stamp
+ module Emitters
+ class NumericEmitter
+ attr_reader :field
+
+ # @param [field] the field to be formatted (e.g. +:month+, +:year+)
+ def initialize(field)
+ @field = field
+ end
+
+ def format(target)
+ target.send(field).to_s
+ end
+ end
+ end
+end
@@ -0,0 +1,31 @@
+module Stamp
+ module Emitters
+ class Ordinal
+ attr_reader :field
+
+ # @param [field] the field to be formatted (e.g. +:month+, +:year+)
+ def initialize(field)
+ @field = field
+ end
+
+ def format(target)
+ ordinalize(target.send(field))
+ end
+
+ # Cribbed from ActiveSupport::Inflector
+ # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb
+ def ordinalize(number)
+ number.to_s + if (11..13).include?(number % 100)
+ 'th'
+ else
+ case number % 10
+ when 1; 'st'
+ when 2; 'nd'
+ when 3; 'rd'
+ else 'th'
+ end
+ end
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 7507ffb

Please sign in to comment.