pinpoint 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|