pinpoint 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,4 +1,182 @@
1
- pinpoint
2
- ========
1
+ <img align="right" width="400" src="http://cache.jezebel.com/assets/images/7/2009/07/custom_1245192016735_wargames_missle_locations.jpg" />
3
2
 
4
- (Un)conventional Address Composition for Ruby (and Rails)
3
+ Pinpoint
4
+ ===============================================================================
5
+
6
+ _(Un)conventional Address Composition for Ruby (and Rails)_
7
+
8
+ Supported Rubies
9
+ --------------------------------
10
+ * MRI Ruby 1.9.2
11
+ * MRI Ruby 1.9.3
12
+ * JRuby (in 1.9 compat mode)
13
+
14
+ <br/>
15
+ <br/>
16
+
17
+ Installation
18
+ -------------------------------------------------------------------------------
19
+
20
+ First:
21
+
22
+ ```ruby
23
+ gem install pinpoint
24
+ ```
25
+
26
+ Then in your script:
27
+
28
+ ```ruby
29
+ require 'pinpoint'
30
+ ```
31
+
32
+ or in your Gemfile
33
+
34
+ ```ruby
35
+ gem 'pinpoint'
36
+ ```
37
+
38
+ or from IRB
39
+
40
+ ```ruby
41
+ irb -r 'pinpoint'
42
+ ```
43
+
44
+ Usage
45
+ -------------------------------------------------------------------------------
46
+
47
+ ### Direct ####################################################################
48
+
49
+ Typically Pinpoint is used by requiring it and using the `Pinpoint::Address`
50
+ class directly. Like so:
51
+
52
+ ```ruby
53
+ class ThingWithAddress
54
+ attr_accessor :address
55
+ end
56
+
57
+ thing = ThingWithAddress.new
58
+ thing.address = Pinpoint::Address.new(:foo => :bar)
59
+ ```
60
+
61
+ ### Composable ################################################################
62
+
63
+ However it can also be included in the class when the class already has
64
+ accessors for all of the different address attributes to generate a composed
65
+ address.
66
+
67
+ ```ruby
68
+ class ThingWithAddress
69
+ include Pinpoint::Composable
70
+
71
+ attr_accessor :street
72
+ attr_accessor :city
73
+ attr_accessor :state
74
+ attr_accessor :zip_code
75
+ attr_accessor :country
76
+
77
+ pinpoint :address
78
+ end
79
+
80
+ thing = ThingWithAddress.new
81
+
82
+ # Set all address fields on `thing`
83
+
84
+ thing.address # => Pinpoint::Address
85
+ ```
86
+
87
+ #### Field Prefixes
88
+
89
+ If your class has fields that are prefixed by a string (eg venue_city,
90
+ venue_state, etc) you can pass `field_prefix` to the compose class method like
91
+ so:
92
+
93
+ ```ruby
94
+ class ThingWithAddress
95
+ # attr_accessors like:
96
+ attr_accessor :venue_street
97
+ attr_accessor :venue_city
98
+
99
+ pinpoint :venue, :field_prefix => 'venue'
100
+ end
101
+ ```
102
+
103
+ ### Address Formatting ########################################################
104
+
105
+ Pinpoint has an advanced address formatter which can output an address in
106
+ multiple country formats.
107
+
108
+ ```ruby
109
+ pinpoint_address.to_s(country: :us,
110
+ style: :one_line)
111
+ # => 'Kwik-E-Mart, 123 Apu Lane, Springfield, NW 12345, United States'
112
+
113
+ pinpoint\_address.to\_s(country: :ru,
114
+ style: :one_line)
115
+ # => 'Kwik-E-Mart, ul. Apu Lane d. 123, pos. Springfield, NW obl, 12345, United States'
116
+ ```
117
+
118
+ _Note: By default, Pinpoint will format addresses for the country that the
119
+ address is located in._
120
+
121
+ ### Retrieving Address Information ############################################
122
+
123
+ * `:name` - The name of the person or business this address is associated with
124
+ * `:location_details` - Free form but typically describes the department, mail
125
+ stop, etc).
126
+ * `:street` - The name of the street
127
+ * `:street_number` - The number that corresponds to the place that the address
128
+ is associated with.
129
+ * `:suite_number` - The number associated with the aparment, office or suite.
130
+ * `:zip_code` - The postal code associated with the address
131
+ * `:city` - The town/villiage/city associated with the address
132
+ * `:state` - The state/department/province associated with the address
133
+ * `:country` - The country associated with the address
134
+
135
+ #### Aliases ####
136
+
137
+ Some of the above attributes are aliased to some other common names:
138
+
139
+ * `:name` => `:recipient`
140
+ * `:name` => `:building_name`
141
+ * `:location_details` => `:mail_stop`
142
+ * `:zip_code` => `:postal_code`
143
+ * `:city` => `:locality`
144
+ * `:state` => `:province`
145
+
146
+ ### Geocoding #################################################################
147
+
148
+ By default, Pinpoint uses [Geocoder]() to add latitude and longitude information
149
+ to addresses.
150
+
151
+ If a Pinpoint address is composed into your class, you can add a `:latitude` and
152
+ `:longitude` attribute to it and it will be set/modified when the address is
153
+ changed.
154
+
155
+ Road Map
156
+ --------------------------------
157
+
158
+ * Advanced parsing support
159
+ * SimpleForm inputs
160
+
161
+ Issues
162
+ --------------------------------
163
+
164
+ If you have problems, please create a [Github issue](https://github.com/chirrpy/pinpoint/issues).
165
+
166
+ Credits
167
+ --------------------------------
168
+
169
+ ![](https://dl.dropbox.com/s/f9s2qd0kmbc8nwl/github_logo.png?dl=1)
170
+
171
+ pinpoint is maintained by [Chrrpy, LLC](http://chirrpy.com)
172
+
173
+ The names and logos for Chirrpy are trademarks of Chrrpy, LLC
174
+
175
+ Contributors
176
+ --------------------------------
177
+ * [Jeff Felchner](https://github.com/jfelchner)
178
+
179
+ License
180
+ --------------------------------
181
+
182
+ pinpoint is Copyright &copy; 2012 Chirrpy. It is free software, and may be redistributed under the terms specified in the LICENSE file.
@@ -1,3 +1,5 @@
1
+ require 'pinpoint/formatter'
2
+
1
3
  module Pinpoint
2
4
  class Address
3
5
  attr_accessor :name,
@@ -10,6 +12,10 @@ module Pinpoint
10
12
  :latitude,
11
13
  :longitude
12
14
 
15
+ # City Aliases
16
+ alias :locality :city
17
+ alias :locality= :city=
18
+
13
19
  # State Aliases
14
20
  alias :region :state
15
21
  alias :region= :state=
@@ -52,6 +58,10 @@ module Pinpoint
52
58
  blank?(zip_code)
53
59
  end
54
60
 
61
+ def to_s(options = { :country => :us, :format => :one_line })
62
+ Formatter.format(self, options)
63
+ end
64
+
55
65
  private
56
66
  def present?(value)
57
67
  !blank?(value)
@@ -0,0 +1,21 @@
1
+ one_line_with_name: '(%n, )(%s, )(%l, )(%p )%z(, %c)'
2
+ one_line: '(%s, )(%l, )(%p )%z(, %c)'
3
+ multi_line: "(%s\n)((%l, )(%p )%z\n)(%c\n)"
4
+ multi_line_with_name: "(%n\n)(%s\n)((%l, )(%p )%z\n)(%c\n)"
5
+ html: |
6
+ <address>
7
+ <span class="section">
8
+ <span class="name">%n</span>
9
+ </span>
10
+ <span class="section">
11
+ <span class="street">%s</span>
12
+ </span>
13
+ <span class="section">
14
+ <span class="locality">%l</span>
15
+ <span class="province">%p</span>
16
+ <span class="postal_code">%z</span>
17
+ </span>
18
+ <span class="section">
19
+ <span class="country">%c</span>
20
+ </span>
21
+ </address>
File without changes
File without changes
@@ -0,0 +1,61 @@
1
+ require 'pinpoint/format/file'
2
+
3
+ module Pinpoint
4
+ class Format
5
+ attr_accessor :styles
6
+
7
+ ##
8
+ # Public: Initialize an empty Format with no styles.
9
+ #
10
+ def initialize
11
+ styles = {}
12
+ end
13
+
14
+ ##
15
+ # Public: Attempts to find a format for a given country.
16
+ #
17
+ # country - A Symbol representing the lowercased two-character [ISO
18
+ # 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) country code for
19
+ # the country you are trying to load a format for.
20
+ #
21
+ # Example
22
+ #
23
+ # lookup_by_country(:us)
24
+ # # => <Format styles: {one_line: <Format::Style>, ....}>
25
+ #
26
+ # Returns a Format loaded with all of the styles that it is capable of.
27
+ #
28
+ def self.lookup_by_country(country)
29
+ format = self.new
30
+ format.styles = Pinpoint::Format::File.styles_for(country)
31
+ format
32
+ end
33
+
34
+ ##
35
+ # Public: Will output any given address for the country defined in the
36
+ # Format.
37
+ #
38
+ # By default it will output in a 'one-line' style.
39
+ #
40
+ # address - The address that will be formatted (typically
41
+ # a Pinpoint::Address).
42
+ #
43
+ # options - A Hash of options for the method
44
+ #
45
+ # :style - The style to be applied to the address output
46
+ # (defaults to :one_line).
47
+ #
48
+ # Example
49
+ #
50
+ # output my_address, :style => :one_line
51
+ # # => 'Kwik-E-Mart, 123 Apu Lane, Springfield, NW 12345, United States'
52
+ #
53
+ # Returns a String representing the address in the specified style.
54
+ #
55
+ def output(address, options = {})
56
+ requested_style = options.fetch(:style, :one_line).to_sym
57
+
58
+ styles[requested_style].output(address)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,45 @@
1
+ require 'yaml'
2
+ require 'pinpoint/format/style'
3
+
4
+ module Pinpoint
5
+ class Format
6
+ class File
7
+
8
+ ##
9
+ # Public: Loads the format for the given country from the appropriate
10
+ # YAML file.
11
+ #
12
+ # It then converts the parsed YAML into Pinpoint::Format::Style objects which
13
+ # can be used to style something that quaks like an Address.
14
+ #
15
+ # country - A Symbol representing the lowercased two-character [ISO
16
+ # 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) country code for
17
+ # the country you are trying to load a format for.
18
+ #
19
+ # Returns a Hash containing symbolized keys for the style names and values
20
+ # containing Styles.
21
+ #
22
+ def self.styles_for(country)
23
+ raw_style_data(country).each_with_object({}) do |style_definition, hash|
24
+ style_name = style_definition[0]
25
+ style = style_definition[1]
26
+
27
+ hash[style_name.to_sym] = Format::Style.from_yaml(style)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def self.format_yaml_contents(country)
34
+ relative_path = "../../config/formats/#{country}.yml"
35
+ filename = ::File.expand_path(relative_path, __FILE__)
36
+
37
+ ::File.read(filename)
38
+ end
39
+
40
+ def self.raw_style_data(country)
41
+ YAML::load(format_yaml_contents(country))
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,53 @@
1
+ require 'pinpoint/format'
2
+
3
+ module Pinpoint
4
+ class Format
5
+ class List
6
+ include Enumerable
7
+
8
+ ##
9
+ # Public: Initializes a new empty List
10
+ #
11
+ def initialize
12
+ self.formats = Hash.new
13
+ end
14
+
15
+ ##
16
+ # Public: Retrieves a Format for the given country.
17
+ #
18
+ # If the country's Format has already been retrieved, it is returned,
19
+ # otherwise it is looked up.
20
+ #
21
+ # country - The two letter ISO_3166-1 code for the country you're looking
22
+ # up the format for.
23
+ #
24
+ # Example
25
+ #
26
+ # [:us]
27
+ # # => <Format>
28
+ #
29
+ # Returns a Format which corresponds to the given country.
30
+ #
31
+ def [](country)
32
+ country = country.to_sym
33
+
34
+ get(country) ||
35
+ set(country, Pinpoint::Format.lookup_by_country(country))
36
+ end
37
+
38
+ protected
39
+
40
+ attr_accessor :formats
41
+
42
+ private
43
+
44
+ def get(country)
45
+ self.formats[country]
46
+ end
47
+
48
+ def set(country, format)
49
+ self.formats[country] = format
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,6 @@
1
+ module Pinpoint
2
+ class Format
3
+ class ParseError < StandardError; end
4
+ class UnevenNestingError < ParseError; end
5
+ end
6
+ end
@@ -0,0 +1,94 @@
1
+ require 'pinpoint/format/tokenizer'
2
+
3
+ ###
4
+ # Private: Parses a set of Tokens into a parse tree that can be navigated by
5
+ # a Style to render some output.
6
+ #
7
+ module Pinpoint
8
+ class Format
9
+ class Parser
10
+
11
+ ##
12
+ # Public: Initializes a Parser and converts the passed String into
13
+ # a TokenList that will be utilized when it is parsed.
14
+ #
15
+ # Raises a ParseError if the Tokenizer cannot tokenize the String
16
+ # Returns a Parser
17
+ #
18
+ def initialize(string)
19
+ self.tokens = Pinpoint::Format::Tokenizer.new(string).to_token_list
20
+ end
21
+
22
+ ##
23
+ # Public: Provides a way to convert a TokenList into a tree repersenting
24
+ # groups, message names, and String literals.
25
+ #
26
+ # If the TokenList is not valid, a form of ParseError is raised.
27
+ #
28
+ # Example
29
+ #
30
+ # Parser.new('(%s, )((%l, )(%p )%z)(, %c)').parse
31
+ # # => [
32
+ # # [
33
+ # # :street,
34
+ # # ', ',
35
+ # # ],
36
+ # # [
37
+ # # [
38
+ # # :locality,
39
+ # # ', '
40
+ # # ],
41
+ # # [
42
+ # # :province,
43
+ # # ' '
44
+ # # ],
45
+ # # :postal_code
46
+ # # ],
47
+ # # [
48
+ # # ', ',
49
+ # # :country
50
+ # # ]
51
+ # # ]
52
+ #
53
+ # Returns an Array containing the parse tree structure
54
+ #
55
+ def parse
56
+ process(tokens) if tokens.valid?
57
+ end
58
+
59
+ protected
60
+
61
+ ##
62
+ # Protected: Reads the TokenList associated with the Parser
63
+ #
64
+ attr_accessor :tokens
65
+
66
+ ##
67
+ # Protected: Can recursively process the TokenList into an Array containing
68
+ # the parse tree.
69
+ #
70
+ # See also Parser#parse
71
+ #
72
+ # Returns an Array containing the parse tree structure
73
+ #
74
+ def process(token_list)
75
+ result = []
76
+
77
+ token_list.process_each! do |token|
78
+ case token.processed_value
79
+ when :group_start
80
+ token_list, intermediate_result = process(token_list)
81
+
82
+ result << intermediate_result
83
+ when :group_end
84
+ return [token_list, result]
85
+ else
86
+ result << token.processed_value
87
+ end
88
+ end
89
+
90
+ result
91
+ end
92
+ end
93
+ end
94
+ end