pinpoint 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/pinpoint/address.rb +29 -23
- data/lib/pinpoint/composable.rb +48 -0
- data/lib/pinpoint/model_support.rb +157 -0
- data/lib/pinpoint/validations.rb +10 -7
- data/lib/pinpoint/version.rb +1 -1
- data/lib/pinpoint.rb +1 -43
- data/spec/address_spec.rb +0 -2
- data/spec/model_support_spec.rb +234 -0
- data/spec/validations_spec.rb +58 -57
- metadata +6 -4
- data/spec/pinpoint_spec.rb +0 -86
data/lib/pinpoint/address.rb
CHANGED
@@ -2,20 +2,28 @@ require 'pinpoint/formatter'
|
|
2
2
|
|
3
3
|
module Pinpoint
|
4
4
|
class Address
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
ATTRIBUTE_NAMES = [
|
6
|
+
:name,
|
7
|
+
:street_and_premises,
|
8
|
+
:city,
|
9
|
+
:state,
|
10
|
+
:county,
|
11
|
+
:postal_code,
|
12
|
+
:country,
|
13
|
+
:latitude,
|
14
|
+
:longitude
|
15
|
+
]
|
16
|
+
|
17
|
+
attr_accessor *ATTRIBUTE_NAMES
|
14
18
|
|
15
19
|
# City Aliases
|
16
20
|
alias :locality :city
|
17
21
|
alias :locality= :city=
|
18
22
|
|
23
|
+
# Street Aliases
|
24
|
+
alias :street :street_and_premises
|
25
|
+
alias :street= :street_and_premises=
|
26
|
+
|
19
27
|
# State Aliases
|
20
28
|
alias :region :state
|
21
29
|
alias :region= :state=
|
@@ -23,12 +31,10 @@ module Pinpoint
|
|
23
31
|
alias :province= :state=
|
24
32
|
|
25
33
|
# Zip Code Aliases
|
26
|
-
alias :postal_code
|
27
|
-
alias :
|
28
|
-
alias :
|
29
|
-
alias :
|
30
|
-
alias :zip :zip_code
|
31
|
-
alias :zip= :zip_code=
|
34
|
+
alias :zip_code :postal_code
|
35
|
+
alias :zip_code= :postal_code=
|
36
|
+
alias :zip :postal_code
|
37
|
+
alias :zip= :postal_code=
|
32
38
|
|
33
39
|
# County Aliases
|
34
40
|
alias :district :county
|
@@ -41,10 +47,10 @@ module Pinpoint
|
|
41
47
|
end
|
42
48
|
|
43
49
|
def complete?
|
44
|
-
present?(
|
45
|
-
present?(city)
|
46
|
-
present?(state)
|
47
|
-
present?(
|
50
|
+
present?(street_and_premises) &&
|
51
|
+
present?(city) &&
|
52
|
+
present?(state) &&
|
53
|
+
present?(postal_code)
|
48
54
|
end
|
49
55
|
|
50
56
|
def incomplete?
|
@@ -52,10 +58,10 @@ module Pinpoint
|
|
52
58
|
end
|
53
59
|
|
54
60
|
def empty?
|
55
|
-
blank?(
|
56
|
-
blank?(city)
|
57
|
-
blank?(state)
|
58
|
-
blank?(
|
61
|
+
blank?(street_and_premises) &&
|
62
|
+
blank?(city) &&
|
63
|
+
blank?(state) &&
|
64
|
+
blank?(postal_code)
|
59
65
|
end
|
60
66
|
|
61
67
|
def to_s(options = { :country => :us, :format => :one_line })
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'pinpoint/model_support'
|
2
|
+
require 'pinpoint/validations'
|
3
|
+
|
4
|
+
module Pinpoint
|
5
|
+
module Composable
|
6
|
+
##
|
7
|
+
# Public: Allows fields on a class to be composed/decomposed to
|
8
|
+
# Pinpoint::Addresses.
|
9
|
+
#
|
10
|
+
# field_name - The object to apply the accessors to
|
11
|
+
# options - A Hash of options to apply to the method call
|
12
|
+
#
|
13
|
+
# :validate - A Boolean describing whether to include
|
14
|
+
# ActiveModel::Validations into the class for the
|
15
|
+
# address fields.
|
16
|
+
# :prefix - If set, then all fields which make up the Address
|
17
|
+
# will be prefixed with that string (eg: #venue_city
|
18
|
+
# instead of just #city) If you do not want field
|
19
|
+
# names to be prefixed simply set it to false.
|
20
|
+
# (defaults to the field name).
|
21
|
+
#
|
22
|
+
# Example
|
23
|
+
#
|
24
|
+
# # Implicit Prefix (will look for fields such as 'address_city')
|
25
|
+
# pinpoint :address
|
26
|
+
#
|
27
|
+
# # Explicit Prefix (will look for fields such as 'venue_city')
|
28
|
+
# pinpoint :location, :prefix => :venue
|
29
|
+
#
|
30
|
+
# # No Prefix (will look for fields such as 'city')
|
31
|
+
# pinpoint :location, :prefix => false
|
32
|
+
#
|
33
|
+
# # Include field validations
|
34
|
+
# pinpoint :address, :validate => true
|
35
|
+
#
|
36
|
+
# Returns nothing
|
37
|
+
#
|
38
|
+
def pinpoint(field_name, options = {})
|
39
|
+
options[:field_field_name] = field_name
|
40
|
+
|
41
|
+
if options[:validate]
|
42
|
+
Pinpoint::Validations.define(self, options)
|
43
|
+
end
|
44
|
+
|
45
|
+
Pinpoint::ModelSupport.define_address_accessors(self, options)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'pinpoint/address'
|
3
|
+
|
4
|
+
module Pinpoint
|
5
|
+
module ModelSupport
|
6
|
+
##
|
7
|
+
# Private: Defines address accessors for a given object.
|
8
|
+
#
|
9
|
+
# object - The object to apply the accessors to
|
10
|
+
# options - A Hash of options to apply to the method call
|
11
|
+
#
|
12
|
+
# :field_name - The String or Symbol representing the name of the
|
13
|
+
# field pair to create (eg: #venue and #venue=).
|
14
|
+
# :prefix - If set, then all fields which make up the Address
|
15
|
+
# will be prefixed with that string (eg: #venue_city
|
16
|
+
# instead of just #city) If you do not want field
|
17
|
+
# names to be prefixed simply set it to false.
|
18
|
+
# (defaults to the field name).
|
19
|
+
#
|
20
|
+
# Example
|
21
|
+
#
|
22
|
+
# # Without a Prefix
|
23
|
+
# define_address_accessors my_object, field_name: :venue
|
24
|
+
#
|
25
|
+
# # With a Prefix
|
26
|
+
# define_address_accessors my_object, field_name: :address,
|
27
|
+
# prefix: :venue
|
28
|
+
#
|
29
|
+
# Returns nothing
|
30
|
+
#
|
31
|
+
def self.define_address_accessors(object, options = {})
|
32
|
+
field_name = options.fetch(:field_name)
|
33
|
+
options[:prefix] = options.fetch(:prefix, field_name)
|
34
|
+
options[:address_fields] ||= find_address_fields(object, options)
|
35
|
+
|
36
|
+
define_address_reader(object, options)
|
37
|
+
define_address_setter(object, options)
|
38
|
+
define_address_predicates(object, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
##
|
44
|
+
# Private: Defines the reader for the composed Address field.
|
45
|
+
#
|
46
|
+
# The defined method will collect all of the disparate pieces of information
|
47
|
+
# from the model and create a Pinpoint::Address that represents that
|
48
|
+
# information.
|
49
|
+
#
|
50
|
+
# object - (See .define_address_accessors object parameter)
|
51
|
+
# options - (See .define_address_accessors options parameter)
|
52
|
+
#
|
53
|
+
# Returns nothing
|
54
|
+
#
|
55
|
+
def self.define_address_reader(object, options)
|
56
|
+
field_name = options.fetch(:field_name)
|
57
|
+
attribute_fields = options.fetch(:address_fields).fetch(:base_fields)
|
58
|
+
reader_fields = options.fetch(:address_fields).fetch(:reader_fields)
|
59
|
+
field_pairs = attribute_fields.zip reader_fields
|
60
|
+
|
61
|
+
# TODO: Memoize
|
62
|
+
object.send(:define_method, field_name) do
|
63
|
+
address_fields_and_values = field_pairs.map do |field_pair|
|
64
|
+
[field_pair[0], send(field_pair[1])]
|
65
|
+
end
|
66
|
+
address_fields_and_values = Hash[address_fields_and_values]
|
67
|
+
|
68
|
+
Pinpoint::Address.new(address_fields_and_values)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Private: Defines the writer for the composed Address field.
|
74
|
+
#
|
75
|
+
# The defined method will read all of the pieces of the address from the
|
76
|
+
# Address that is passed in and set that information on the model's address
|
77
|
+
# fields.
|
78
|
+
#
|
79
|
+
# object - (See .define_address_accessors object parameter)
|
80
|
+
# options - (See .define_address_accessors options parameter)
|
81
|
+
#
|
82
|
+
# Returns nothing
|
83
|
+
#
|
84
|
+
def self.define_address_setter(object, options)
|
85
|
+
name = options.fetch(:field_name)
|
86
|
+
reader_fields = options.fetch(:address_fields).fetch(:base_fields)
|
87
|
+
writer_fields = options.fetch(:address_fields).fetch(:writer_fields)
|
88
|
+
field_pairs = reader_fields.zip writer_fields
|
89
|
+
|
90
|
+
object.send(:define_method, "#{name}=") do |address|
|
91
|
+
field_pairs.each do |field_pair|
|
92
|
+
send(field_pair[1], address.send(field_pair[0]))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Private: Defines a method which checks to see if the composed address
|
99
|
+
# field is incomplete.
|
100
|
+
#
|
101
|
+
# This is used mainly to remove the need to 'reach into' the Address itself.
|
102
|
+
#
|
103
|
+
# object - (See .define_address_accessors object parameter)
|
104
|
+
# options - (See .define_address_accessors options parameter)
|
105
|
+
#
|
106
|
+
# Returns nothing
|
107
|
+
#
|
108
|
+
def self.define_address_predicates(object, options)
|
109
|
+
name = options.fetch(:field_name)
|
110
|
+
|
111
|
+
object.send(:define_method, "#{name}_incomplete?") do
|
112
|
+
send(:"#{name}").incomplete?
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# Private: Finds the writer and reader methods that will be used whenever
|
118
|
+
# the composed address field is read from or written to.
|
119
|
+
#
|
120
|
+
# Additionally it maps writer fields back to the field that must be read
|
121
|
+
# from on the Address to get the proper value.
|
122
|
+
#
|
123
|
+
# Returns a Hash of 'base', 'reader' and 'writer' fields.
|
124
|
+
#
|
125
|
+
def self.find_address_fields(object, options)
|
126
|
+
prefix = options[:prefix].blank? ? '' : "#{options[:prefix]}_"
|
127
|
+
address_attrs = Pinpoint::Address::ATTRIBUTE_NAMES
|
128
|
+
reader_field_names = address_attrs.map { |field| :"#{prefix}#{field}" }
|
129
|
+
writer_field_names = address_attrs.map { |field| :"#{prefix}#{field}=" }
|
130
|
+
|
131
|
+
reader_fields = find_existing_methods(object, reader_field_names)
|
132
|
+
writer_fields = find_existing_methods(object, writer_field_names)
|
133
|
+
base_fields = writer_fields.map do |field|
|
134
|
+
field.to_s.
|
135
|
+
gsub(/\A#{prefix}/, '').
|
136
|
+
gsub(/=\z/, '').
|
137
|
+
to_sym
|
138
|
+
end
|
139
|
+
|
140
|
+
{
|
141
|
+
base_fields: base_fields,
|
142
|
+
reader_fields: reader_fields,
|
143
|
+
writer_fields: writer_fields
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Private: Finds the items that are common between the fields being passed
|
149
|
+
# in and the public instance methods on the object.
|
150
|
+
#
|
151
|
+
# Returns an Array of common methods
|
152
|
+
#
|
153
|
+
def self.find_existing_methods(object, fields)
|
154
|
+
fields & object.public_instance_methods
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/lib/pinpoint/validations.rb
CHANGED
@@ -3,32 +3,35 @@ require 'pinpoint/config/patterns'
|
|
3
3
|
|
4
4
|
module Pinpoint
|
5
5
|
module Validations
|
6
|
-
def
|
7
|
-
|
8
|
-
|
6
|
+
def self.define(object, options)
|
7
|
+
name = options.fetch(:field_name)
|
8
|
+
prefix = options.fetch(:prefix, name).blank? ? '' : "#{options[:prefix]}_"
|
9
|
+
|
10
|
+
object.instance_eval <<-VALIDATIONIZATION
|
11
|
+
validates :#{prefix}name,
|
9
12
|
:length => {
|
10
13
|
:maximum => 140 }
|
11
14
|
|
12
|
-
validates :#{
|
15
|
+
validates :#{prefix}street_and_premises,
|
13
16
|
:presence => {
|
14
17
|
:if => :#{name}_incomplete? },
|
15
18
|
:length => {
|
16
19
|
:maximum => 255 }
|
17
20
|
|
18
|
-
validates :#{
|
21
|
+
validates :#{prefix}city,
|
19
22
|
:presence => {
|
20
23
|
:if => :#{name}_incomplete? },
|
21
24
|
:length => {
|
22
25
|
:maximum => 60 }
|
23
26
|
|
24
|
-
validates :#{
|
27
|
+
validates :#{prefix}state,
|
25
28
|
:presence => {
|
26
29
|
:if => :#{name}_incomplete? },
|
27
30
|
:inclusion => {
|
28
31
|
:in => Pinpoint::US_STATES,
|
29
32
|
:allow_blank => true }
|
30
33
|
|
31
|
-
validates :#{
|
34
|
+
validates :#{prefix}postal_code,
|
32
35
|
:presence => {
|
33
36
|
:if => :#{name}_incomplete? },
|
34
37
|
:format => {
|
data/lib/pinpoint/version.rb
CHANGED
data/lib/pinpoint.rb
CHANGED
@@ -1,48 +1,6 @@
|
|
1
1
|
require 'pinpoint/version'
|
2
|
-
require 'pinpoint/
|
2
|
+
require 'pinpoint/composable'
|
3
3
|
require 'pinpoint/address'
|
4
4
|
|
5
5
|
module Pinpoint
|
6
|
-
module ClassMethods
|
7
|
-
include Validations
|
8
|
-
|
9
|
-
def pinpoint(name, options = {})
|
10
|
-
if options[:validate]
|
11
|
-
install_validations(name)
|
12
|
-
end
|
13
|
-
|
14
|
-
define_method name do
|
15
|
-
# TODO: Memoize
|
16
|
-
|
17
|
-
Pinpoint::Address.new(
|
18
|
-
:name => respond_to?(:"#{name}_name") ? send(:"#{name}_name") : '',
|
19
|
-
:street => respond_to?(:"#{name}_street_and_premises") ? send(:"#{name}_street_and_premises") : '',
|
20
|
-
:city => respond_to?(:"#{name}_city") ? send(:"#{name}_city") : '',
|
21
|
-
:state => respond_to?(:"#{name}_state_or_province") ? send(:"#{name}_state_or_province") : '',
|
22
|
-
:county => respond_to?(:"#{name}_district_or_county") ? send(:"#{name}_district_or_county") : '',
|
23
|
-
:postal_code => respond_to?(:"#{name}_postal_code") ? send(:"#{name}_postal_code") : '',
|
24
|
-
:country => respond_to?(:"#{name}_country") ? send(:"#{name}_country") : '',
|
25
|
-
:latitude => respond_to?(:"#{name}_latitude") ? send(:"#{name}_latitude") : '',
|
26
|
-
:longitude => respond_to?(:"#{name}_longitude") ? send(:"#{name}_longitude") : ''
|
27
|
-
)
|
28
|
-
end
|
29
|
-
|
30
|
-
define_method "#{name}=" do |address|
|
31
|
-
send(:"#{name}_name=", address.name)
|
32
|
-
send(:"#{name}_street_and_premises=", address.street)
|
33
|
-
send(:"#{name}_city=", address.city)
|
34
|
-
send(:"#{name}_state_or_province=", address.state)
|
35
|
-
send(:"#{name}_postal_code=", address.zip_code)
|
36
|
-
send(:"#{name}_country=", address.country)
|
37
|
-
end
|
38
|
-
|
39
|
-
define_method "#{name}_incomplete?" do
|
40
|
-
send(:"#{name}").incomplete?
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.included(base)
|
46
|
-
base.extend ClassMethods
|
47
|
-
end
|
48
6
|
end
|
data/spec/address_spec.rb
CHANGED
@@ -12,8 +12,6 @@ describe 'Pinpoint::Address' do
|
|
12
12
|
it { address.respond_to?(:locality=).should be_true }
|
13
13
|
it { address.respond_to?(:postal_code).should be_true }
|
14
14
|
it { address.respond_to?(:postal_code=).should be_true }
|
15
|
-
it { address.respond_to?(:postalcode).should be_true }
|
16
|
-
it { address.respond_to?(:postalcode=).should be_true }
|
17
15
|
it { address.respond_to?(:zip).should be_true }
|
18
16
|
it { address.respond_to?(:zip=).should be_true }
|
19
17
|
it { address.respond_to?(:district).should be_true }
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'pinpoint/model_support'
|
3
|
+
|
4
|
+
class PinpointableAccessorClass
|
5
|
+
def initialize(options = {})
|
6
|
+
options.each do |key, value|
|
7
|
+
send("#{key}=", value)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class PinpointableAccessorClassWithNoPrefixedFields < PinpointableAccessorClass
|
13
|
+
attr_accessor :name,
|
14
|
+
:street_and_premises,
|
15
|
+
:city,
|
16
|
+
:state,
|
17
|
+
:county,
|
18
|
+
:postal_code,
|
19
|
+
:country,
|
20
|
+
:latitude,
|
21
|
+
:longitude
|
22
|
+
end
|
23
|
+
|
24
|
+
class PinpointableAccessorClassWithPrefixedFields < PinpointableAccessorClass
|
25
|
+
attr_accessor :my_address_name,
|
26
|
+
:my_address_street_and_premises,
|
27
|
+
:my_address_city,
|
28
|
+
:my_address_state,
|
29
|
+
:my_address_county,
|
30
|
+
:my_address_postal_code,
|
31
|
+
:my_address_country,
|
32
|
+
:my_address_latitude,
|
33
|
+
:my_address_longitude
|
34
|
+
end
|
35
|
+
|
36
|
+
class PinpointableAccessorClassWithIncompleteFields < PinpointableAccessorClass
|
37
|
+
attr_accessor :name,
|
38
|
+
:street_and_premises,
|
39
|
+
:city,
|
40
|
+
:state
|
41
|
+
end
|
42
|
+
|
43
|
+
describe Pinpoint::ModelSupport do
|
44
|
+
let(:model_support_module) { Pinpoint::ModelSupport }
|
45
|
+
|
46
|
+
context 'when only a composed name is passed in' do
|
47
|
+
let(:pinpointable_class) { PinpointableAccessorClassWithPrefixedFields }
|
48
|
+
let(:pinpointable) { pinpointable_class.new(
|
49
|
+
my_address_name: 'name',
|
50
|
+
my_address_street_and_premises: 'street',
|
51
|
+
my_address_city: 'city',
|
52
|
+
my_address_state: 'state',
|
53
|
+
my_address_county: 'county',
|
54
|
+
my_address_postal_code: 'postal_code',
|
55
|
+
my_address_country: 'country',
|
56
|
+
my_address_latitude: 'latitude',
|
57
|
+
my_address_longitude: 'longitude' ) }
|
58
|
+
|
59
|
+
it 'can add a reader to a class' do
|
60
|
+
model_support_module.define_address_accessors(pinpointable_class,
|
61
|
+
field_name: :my_address)
|
62
|
+
|
63
|
+
pinpointable.should respond_to :my_address
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'can compose an Address with the proper values from the model' do
|
67
|
+
model_support_module.define_address_accessors(pinpointable_class,
|
68
|
+
field_name: :my_address)
|
69
|
+
|
70
|
+
Pinpoint::Address.should_receive(:new).with(
|
71
|
+
name: 'name',
|
72
|
+
street_and_premises: 'street',
|
73
|
+
city: 'city',
|
74
|
+
state: 'state',
|
75
|
+
county: 'county',
|
76
|
+
postal_code: 'postal_code',
|
77
|
+
country: 'country',
|
78
|
+
latitude: 'latitude',
|
79
|
+
longitude: 'longitude'
|
80
|
+
)
|
81
|
+
|
82
|
+
pinpointable.my_address
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'can deconstruct an Address into its component pieces' do
|
86
|
+
model_support_module.define_address_accessors(pinpointable_class,
|
87
|
+
field_name: :my_address)
|
88
|
+
|
89
|
+
address = Pinpoint::Address.new(name: 'name',
|
90
|
+
street_and_premises: 'street',
|
91
|
+
city: 'city',
|
92
|
+
state: 'state',
|
93
|
+
county: 'county',
|
94
|
+
postal_code: 'postal_code',
|
95
|
+
country: 'country',
|
96
|
+
latitude: 'latitude',
|
97
|
+
longitude: 'longitude')
|
98
|
+
|
99
|
+
pinpointable.should_receive :my_address_name=, :with => 'name'
|
100
|
+
pinpointable.should_receive :my_address_street_and_premises=, :with => 'street'
|
101
|
+
pinpointable.should_receive :my_address_city=, :with => 'city'
|
102
|
+
pinpointable.should_receive :my_address_state=, :with => 'state'
|
103
|
+
pinpointable.should_receive :my_address_county=, :with => 'county'
|
104
|
+
pinpointable.should_receive :my_address_postal_code=, :with => 'postal_code'
|
105
|
+
pinpointable.should_receive :my_address_country=, :with => 'country'
|
106
|
+
pinpointable.should_receive :my_address_latitude=, :with => 'latitude'
|
107
|
+
pinpointable.should_receive :my_address_longitude=, :with => 'longitude'
|
108
|
+
|
109
|
+
pinpointable.my_address = address
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when a composed name is passed in along with a prefix' do
|
114
|
+
let(:pinpointable_class) { PinpointableAccessorClassWithPrefixedFields }
|
115
|
+
let(:pinpointable) { pinpointable_class.new( my_address_name: 'name',
|
116
|
+
my_address_street_and_premises: 'street',
|
117
|
+
my_address_city: 'city',
|
118
|
+
my_address_state: 'state',
|
119
|
+
my_address_county: 'county',
|
120
|
+
my_address_postal_code: 'postal_code',
|
121
|
+
my_address_country: 'country',
|
122
|
+
my_address_latitude: 'latitude',
|
123
|
+
my_address_longitude: 'longitude' ) }
|
124
|
+
|
125
|
+
it 'can add a reader to a class' do
|
126
|
+
model_support_module.define_address_accessors(pinpointable_class,
|
127
|
+
field_name: :address,
|
128
|
+
prefix: :my_address)
|
129
|
+
|
130
|
+
pinpointable.should respond_to :address
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'can compose an Address with the proper values from the model' do
|
134
|
+
model_support_module.define_address_accessors(pinpointable_class,
|
135
|
+
field_name: :address,
|
136
|
+
prefix: :my_address)
|
137
|
+
|
138
|
+
Pinpoint::Address.should_receive(:new).with(
|
139
|
+
name: 'name',
|
140
|
+
street_and_premises: 'street',
|
141
|
+
city: 'city',
|
142
|
+
state: 'state',
|
143
|
+
county: 'county',
|
144
|
+
postal_code: 'postal_code',
|
145
|
+
country: 'country',
|
146
|
+
latitude: 'latitude',
|
147
|
+
longitude: 'longitude'
|
148
|
+
)
|
149
|
+
|
150
|
+
pinpointable.address
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'can deconstruct an Address into its component pieces' do
|
154
|
+
model_support_module.define_address_accessors(pinpointable_class,
|
155
|
+
field_name: :address,
|
156
|
+
prefix: :my_address)
|
157
|
+
|
158
|
+
address = Pinpoint::Address.new(name: 'name',
|
159
|
+
street_and_premises: 'street',
|
160
|
+
city: 'city',
|
161
|
+
state: 'state',
|
162
|
+
county: 'county',
|
163
|
+
postal_code: 'postal_code',
|
164
|
+
country: 'country',
|
165
|
+
latitude: 'latitude',
|
166
|
+
longitude: 'longitude')
|
167
|
+
|
168
|
+
pinpointable.should_receive :my_address_name=, :with => 'name'
|
169
|
+
pinpointable.should_receive :my_address_street_and_premises=, :with => 'street'
|
170
|
+
pinpointable.should_receive :my_address_city=, :with => 'city'
|
171
|
+
pinpointable.should_receive :my_address_state=, :with => 'state'
|
172
|
+
pinpointable.should_receive :my_address_county=, :with => 'county'
|
173
|
+
pinpointable.should_receive :my_address_postal_code=, :with => 'postal_code'
|
174
|
+
pinpointable.should_receive :my_address_country=, :with => 'country'
|
175
|
+
pinpointable.should_receive :my_address_latitude=, :with => 'latitude'
|
176
|
+
pinpointable.should_receive :my_address_longitude=, :with => 'longitude'
|
177
|
+
|
178
|
+
pinpointable.address = address
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'when an object is referenced which does not implement all of the fields' do
|
183
|
+
let(:pinpointable_class) { PinpointableAccessorClassWithIncompleteFields }
|
184
|
+
let(:pinpointable) { pinpointable_class.new( name: 'name',
|
185
|
+
street_and_premises: 'street',
|
186
|
+
city: 'city',
|
187
|
+
state: 'state' ) }
|
188
|
+
|
189
|
+
it 'can compose an Address with the proper values from the model' do
|
190
|
+
model_support_module.define_address_accessors(pinpointable_class,
|
191
|
+
field_name: :address,
|
192
|
+
prefix: false)
|
193
|
+
|
194
|
+
Pinpoint::Address.should_receive(:new).with(
|
195
|
+
name: 'name',
|
196
|
+
street_and_premises: 'street',
|
197
|
+
city: 'city',
|
198
|
+
state: 'state'
|
199
|
+
)
|
200
|
+
|
201
|
+
pinpointable.address
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'can deconstruct an Address into its component pieces' do
|
205
|
+
model_support_module.define_address_accessors(pinpointable_class,
|
206
|
+
field_name: :address,
|
207
|
+
prefix: false)
|
208
|
+
|
209
|
+
address = Pinpoint::Address.new(name: 'name',
|
210
|
+
street_and_premises: 'street',
|
211
|
+
city: 'city',
|
212
|
+
state: 'state',
|
213
|
+
county: 'county',
|
214
|
+
postal_code: 'postal_code',
|
215
|
+
country: 'country',
|
216
|
+
latitude: 'latitude',
|
217
|
+
longitude: 'longitude')
|
218
|
+
|
219
|
+
pinpointable.should_receive :name=, :with => 'name'
|
220
|
+
pinpointable.should_receive :street_and_premises=, :with => 'street'
|
221
|
+
pinpointable.should_receive :city=, :with => 'city'
|
222
|
+
pinpointable.should_receive :state=, :with => 'state'
|
223
|
+
|
224
|
+
pinpointable.address = address
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
context 'when a composed name is not passed in' do
|
229
|
+
it 'throws an error' do
|
230
|
+
expect { model_support_module.define_address_accessors(pinpointable_class) }.to(
|
231
|
+
raise_error)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
data/spec/validations_spec.rb
CHANGED
@@ -5,30 +5,31 @@ require 'pinpoint'
|
|
5
5
|
|
6
6
|
class ValidatablePinpointable
|
7
7
|
include ActiveModel::Validations
|
8
|
-
|
8
|
+
extend Pinpoint::Composable
|
9
9
|
|
10
|
-
attr_accessor :
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
10
|
+
attr_accessor :name,
|
11
|
+
:street_and_premises,
|
12
|
+
:city,
|
13
|
+
:state,
|
14
|
+
:postal_code,
|
15
|
+
:country
|
16
16
|
|
17
|
-
pinpoint :address, :
|
17
|
+
pinpoint :address, prefix: false,
|
18
|
+
validate: true
|
18
19
|
end
|
19
20
|
|
20
21
|
class UnvalidatablePinpointable
|
21
22
|
include ActiveModel::Validations
|
22
|
-
|
23
|
+
extend Pinpoint::Composable
|
23
24
|
|
24
25
|
attr_accessor :address_name,
|
25
26
|
:address_street_and_premises,
|
26
27
|
:address_city,
|
27
|
-
:
|
28
|
+
:address_state,
|
28
29
|
:address_postal_code,
|
29
30
|
:address_country
|
30
31
|
|
31
|
-
pinpoint :address
|
32
|
+
pinpoint :address, :prefix => :address
|
32
33
|
end
|
33
34
|
|
34
35
|
describe 'Pinpoint::Validations' do
|
@@ -36,60 +37,60 @@ describe 'Pinpoint::Validations' do
|
|
36
37
|
subject { ValidatablePinpointable.new }
|
37
38
|
|
38
39
|
describe '#address_name' do
|
39
|
-
it { should have_valid(:
|
40
|
-
it { should_not have_valid(:
|
40
|
+
it { should have_valid(:name).when nil, ('*' * 140) }
|
41
|
+
it { should_not have_valid(:name).when ('*' * 141) }
|
41
42
|
end
|
42
43
|
|
43
|
-
describe '#
|
44
|
-
it { should have_valid(:
|
45
|
-
it { should_not have_valid(:
|
44
|
+
describe '#address_state' do
|
45
|
+
it { should have_valid(:street_and_premises).when nil, ('*' * 255) }
|
46
|
+
it { should_not have_valid(:street_and_premises).when ('*' * 256) }
|
46
47
|
end
|
47
48
|
|
48
49
|
describe '#address_city' do
|
49
|
-
it { should have_valid(:
|
50
|
-
it { should_not have_valid(:
|
50
|
+
it { should have_valid(:city).when nil, ('*' * 60) }
|
51
|
+
it { should_not have_valid(:city).when ('*' * 61) }
|
51
52
|
end
|
52
53
|
|
53
54
|
describe '#address_street_and_premises' do
|
54
|
-
it { should have_valid(:
|
55
|
-
it { should_not have_valid(:
|
55
|
+
it { should have_valid(:state).when nil, '', 'NY', 'MO' }
|
56
|
+
it { should_not have_valid(:state).when 'WP', 'NI', 'PO' }
|
56
57
|
end
|
57
58
|
|
58
59
|
describe '#address_postal_code' do
|
59
|
-
it { should have_valid(:
|
60
|
-
it { should_not have_valid(:
|
60
|
+
it { should have_valid(:postal_code).when nil, '', 12345, 12345-1234 }
|
61
|
+
it { should_not have_valid(:postal_code).when 'asdf', '123456' }
|
61
62
|
end
|
62
63
|
|
63
64
|
context "when the street address is set" do
|
64
|
-
before { subject.
|
65
|
+
before { subject.street_and_premises = "foo" }
|
65
66
|
|
66
|
-
it('the city should not be valid when blank') { should_not have_valid(:
|
67
|
-
it('the state should not be valid when blank') { should_not have_valid(:
|
68
|
-
it('the zip code should not be valid when blank') { should_not have_valid(:
|
67
|
+
it('the city should not be valid when blank') { should_not have_valid(:city).when nil, '' }
|
68
|
+
it('the state should not be valid when blank') { should_not have_valid(:state).when nil, '' }
|
69
|
+
it('the zip code should not be valid when blank') { should_not have_valid(:postal_code).when nil, '' }
|
69
70
|
end
|
70
71
|
|
71
72
|
context "when the city is set" do
|
72
|
-
before { subject.
|
73
|
+
before { subject.city = "foo" }
|
73
74
|
|
74
|
-
it('the street should not be valid when blank') { should_not have_valid(:
|
75
|
-
it('the state should not be valid when blank') { should_not have_valid(:
|
76
|
-
it('the zip code should not be valid when blank') { should_not have_valid(:
|
75
|
+
it('the street should not be valid when blank') { should_not have_valid(:street_and_premises).when nil, '' }
|
76
|
+
it('the state should not be valid when blank') { should_not have_valid(:state).when nil, '' }
|
77
|
+
it('the zip code should not be valid when blank') { should_not have_valid(:postal_code).when nil, '' }
|
77
78
|
end
|
78
79
|
|
79
80
|
context "when the state is set" do
|
80
|
-
before { subject.
|
81
|
+
before { subject.state = "FO" }
|
81
82
|
|
82
|
-
it('the street should not be valid when blank') { should_not have_valid(:
|
83
|
-
it('the city should not be valid when blank') { should_not have_valid(:
|
84
|
-
it('the zip code should not be valid when blank') { should_not have_valid(:
|
83
|
+
it('the street should not be valid when blank') { should_not have_valid(:street_and_premises).when nil, '' }
|
84
|
+
it('the city should not be valid when blank') { should_not have_valid(:city).when nil, '' }
|
85
|
+
it('the zip code should not be valid when blank') { should_not have_valid(:postal_code).when nil, '' }
|
85
86
|
end
|
86
87
|
|
87
88
|
context "when the zip is set" do
|
88
|
-
before { subject.
|
89
|
+
before { subject.postal_code = "12345" }
|
89
90
|
|
90
|
-
it('the street should not be valid when blank') { should_not have_valid(:
|
91
|
-
it('the city should not be valid when blank') { should_not have_valid(:
|
92
|
-
it('the state should not be valid when blank') { should_not have_valid(:
|
91
|
+
it('the street should not be valid when blank') { should_not have_valid(:street_and_premises).when nil, '' }
|
92
|
+
it('the city should not be valid when blank') { should_not have_valid(:city).when nil, '' }
|
93
|
+
it('the state should not be valid when blank') { should_not have_valid(:state).when nil, '' }
|
93
94
|
end
|
94
95
|
end
|
95
96
|
|
@@ -97,55 +98,55 @@ describe 'Pinpoint::Validations' do
|
|
97
98
|
subject { UnvalidatablePinpointable.new }
|
98
99
|
|
99
100
|
describe '#address_name' do
|
100
|
-
it { should have_valid(:address_name).when
|
101
|
+
it { should have_valid(:address_name).when ('*' * 141) }
|
101
102
|
end
|
102
103
|
|
103
|
-
describe '#
|
104
|
-
it { should have_valid(:address_street_and_premises).when
|
104
|
+
describe '#address_state' do
|
105
|
+
it { should have_valid(:address_street_and_premises).when ('*' * 256) }
|
105
106
|
end
|
106
107
|
|
107
108
|
describe '#address_city' do
|
108
|
-
it { should have_valid(:address_city).when
|
109
|
+
it { should have_valid(:address_city).when ('*' * 61) }
|
109
110
|
end
|
110
111
|
|
111
112
|
describe '#address_street_and_premises' do
|
112
|
-
it { should have_valid(:
|
113
|
+
it { should have_valid(:address_state).when 'WP', 'NI', 'PO' }
|
113
114
|
end
|
114
115
|
|
115
116
|
describe '#address_postal_code' do
|
116
|
-
it { should have_valid(:address_postal_code).when
|
117
|
+
it { should have_valid(:address_postal_code).when 'asdf', '123456' }
|
117
118
|
end
|
118
119
|
|
119
120
|
context "when the street address is set" do
|
120
121
|
before { subject.address_street_and_premises = "foo" }
|
121
122
|
|
122
|
-
it('the city should not be valid when blank') { should have_valid(:address_city).when
|
123
|
-
it('the state should not be valid when blank') { should have_valid(:
|
124
|
-
it('the zip code should not be valid when blank') { should have_valid(:address_postal_code).when
|
123
|
+
it('the city should not be valid when blank') { should have_valid(:address_city).when nil, '' }
|
124
|
+
it('the state should not be valid when blank') { should have_valid(:address_state).when nil, '' }
|
125
|
+
it('the zip code should not be valid when blank') { should have_valid(:address_postal_code).when nil, '' }
|
125
126
|
end
|
126
127
|
|
127
128
|
context "when the city is set" do
|
128
129
|
before { subject.address_city = "foo" }
|
129
130
|
|
130
|
-
it('the street should not be valid when blank') { should have_valid(:address_street_and_premises).when
|
131
|
-
it('the state should not be valid when blank') { should have_valid(:
|
132
|
-
it('the zip code should not be valid when blank') { should have_valid(:address_postal_code).when
|
131
|
+
it('the street should not be valid when blank') { should have_valid(:address_street_and_premises).when nil, '' }
|
132
|
+
it('the state should not be valid when blank') { should have_valid(:address_state).when nil, '' }
|
133
|
+
it('the zip code should not be valid when blank') { should have_valid(:address_postal_code).when nil, '' }
|
133
134
|
end
|
134
135
|
|
135
136
|
context "when the state is set" do
|
136
|
-
before { subject.
|
137
|
+
before { subject.address_state = "FO" }
|
137
138
|
|
138
|
-
it('the street should not be valid when blank') { should have_valid(:address_street_and_premises).when
|
139
|
-
it('the city should not be valid when blank') { should have_valid(:address_city).when
|
140
|
-
it('the zip code should not be valid when blank') { should have_valid(:address_postal_code).when
|
139
|
+
it('the street should not be valid when blank') { should have_valid(:address_street_and_premises).when nil, '' }
|
140
|
+
it('the city should not be valid when blank') { should have_valid(:address_city).when nil, '' }
|
141
|
+
it('the zip code should not be valid when blank') { should have_valid(:address_postal_code).when nil, '' }
|
141
142
|
end
|
142
143
|
|
143
144
|
context "when the zip is set" do
|
144
145
|
before { subject.address_postal_code = "12345" }
|
145
146
|
|
146
|
-
it('the street should not be valid when blank') { should have_valid(:address_street_and_premises).when
|
147
|
-
it('the city should not be valid when blank') { should have_valid(:address_city).when
|
148
|
-
it('the state should not be valid when blank') { should have_valid(:
|
147
|
+
it('the street should not be valid when blank') { should have_valid(:address_street_and_premises).when nil, '' }
|
148
|
+
it('the city should not be valid when blank') { should have_valid(:address_city).when nil, '' }
|
149
|
+
it('the state should not be valid when blank') { should have_valid(:address_state).when nil, '' }
|
149
150
|
end
|
150
151
|
end
|
151
152
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pinpoint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-02-
|
13
|
+
date: 2013-02-19 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -86,6 +86,7 @@ extra_rdoc_files:
|
|
86
86
|
- LICENSE
|
87
87
|
files:
|
88
88
|
- lib/pinpoint/address.rb
|
89
|
+
- lib/pinpoint/composable.rb
|
89
90
|
- lib/pinpoint/config/formats/us.yml
|
90
91
|
- lib/pinpoint/config/patterns.rb
|
91
92
|
- lib/pinpoint/config/us_states.rb
|
@@ -99,6 +100,7 @@ files:
|
|
99
100
|
- lib/pinpoint/format/tokenizer.rb
|
100
101
|
- lib/pinpoint/format.rb
|
101
102
|
- lib/pinpoint/formatter.rb
|
103
|
+
- lib/pinpoint/model_support.rb
|
102
104
|
- lib/pinpoint/validations.rb
|
103
105
|
- lib/pinpoint/version.rb
|
104
106
|
- lib/pinpoint.rb
|
@@ -115,7 +117,7 @@ files:
|
|
115
117
|
- spec/format/tokenizer_spec.rb
|
116
118
|
- spec/format_spec.rb
|
117
119
|
- spec/formatter_spec.rb
|
118
|
-
- spec/
|
120
|
+
- spec/model_support_spec.rb
|
119
121
|
- spec/spec_helper.rb
|
120
122
|
- spec/support/focused.rb
|
121
123
|
- spec/support/pending.rb
|
@@ -156,7 +158,7 @@ test_files:
|
|
156
158
|
- spec/format/tokenizer_spec.rb
|
157
159
|
- spec/format_spec.rb
|
158
160
|
- spec/formatter_spec.rb
|
159
|
-
- spec/
|
161
|
+
- spec/model_support_spec.rb
|
160
162
|
- spec/spec_helper.rb
|
161
163
|
- spec/support/focused.rb
|
162
164
|
- spec/support/pending.rb
|
data/spec/pinpoint_spec.rb
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
require 'rspectacular'
|
2
|
-
require 'pinpoint'
|
3
|
-
|
4
|
-
class Pinpointable
|
5
|
-
include Pinpoint
|
6
|
-
|
7
|
-
attr_accessor :address_name,
|
8
|
-
:address_street_and_premises,
|
9
|
-
:address_city,
|
10
|
-
:address_state_or_province,
|
11
|
-
:address_postal_code,
|
12
|
-
:address_country
|
13
|
-
|
14
|
-
pinpoint :address
|
15
|
-
end
|
16
|
-
|
17
|
-
describe 'Pinpoint' do
|
18
|
-
let(:pinpointable) { Pinpointable.new }
|
19
|
-
|
20
|
-
describe '.pinpoint' do
|
21
|
-
it 'creates a method to access the address' do
|
22
|
-
pinpointable.respond_to?(:address).should be_true
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'creates a method to set the address' do
|
26
|
-
pinpointable.respond_to?(:address=).should be_true
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe '#address' do
|
31
|
-
it 'is a Pinpoint::Address' do
|
32
|
-
pinpointable.address.should be_a Pinpoint::Address
|
33
|
-
end
|
34
|
-
|
35
|
-
context 'when the object has address information set' do
|
36
|
-
before do
|
37
|
-
pinpointable.address_name = 'The TARDIS'
|
38
|
-
pinpointable.address_street_and_premises = '413 Citadel Drive'
|
39
|
-
pinpointable.address_city = 'Panopticon'
|
40
|
-
pinpointable.address_state_or_province = 'Eye of Harmony'
|
41
|
-
pinpointable.address_postal_code = '12345'
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'has the correct address components' do
|
45
|
-
pinpointable.address.name.should eql 'The TARDIS'
|
46
|
-
pinpointable.address.street.should eql '413 Citadel Drive'
|
47
|
-
pinpointable.address.city.should eql 'Panopticon'
|
48
|
-
pinpointable.address.state.should eql 'Eye of Harmony'
|
49
|
-
pinpointable.address.county.should eql ''
|
50
|
-
pinpointable.address.zip_code.should eql '12345'
|
51
|
-
pinpointable.address.latitude.should eql ''
|
52
|
-
pinpointable.address.longitude.should eql ''
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe '#address=' do
|
58
|
-
context 'when the object has address information set' do
|
59
|
-
before do
|
60
|
-
pinpointable.address_name = 'The TARDIS'
|
61
|
-
pinpointable.address_street_and_premises = '413 Citadel Drive'
|
62
|
-
pinpointable.address_city = 'Panopticon'
|
63
|
-
pinpointable.address_state_or_province = 'Eye of Harmony'
|
64
|
-
pinpointable.address_postal_code = '12345'
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'overrides the current address information with that stored in the Pinpoint::Address' do
|
68
|
-
pinpointable.address = Pinpoint::Address.new(
|
69
|
-
name: 'Buckingham Palace',
|
70
|
-
street: '',
|
71
|
-
city: 'City of Westminster',
|
72
|
-
state: 'London',
|
73
|
-
zip_code: 'SW1A',
|
74
|
-
country: 'United Kingdom'
|
75
|
-
)
|
76
|
-
|
77
|
-
pinpointable.address_name.should eql 'Buckingham Palace'
|
78
|
-
pinpointable.address_street_and_premises.should eql ''
|
79
|
-
pinpointable.address_city.should eql 'City of Westminster'
|
80
|
-
pinpointable.address_state_or_province.should eql 'London'
|
81
|
-
pinpointable.address_postal_code.should eql 'SW1A'
|
82
|
-
pinpointable.address_country.should eql 'United Kingdom'
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|