campfire_logic 1.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. data/Capfile +4 -0
  2. data/Gemfile +29 -0
  3. data/Gemfile.local +27 -0
  4. data/README.rdoc +17 -0
  5. data/Rakefile +43 -0
  6. data/VERSION +1 -0
  7. data/app/controllers/application_controller.rb +5 -0
  8. data/app/controllers/directory_controller.rb +65 -0
  9. data/app/controllers/locations_controller.rb +65 -0
  10. data/app/controllers/services_controller.rb +46 -0
  11. data/app/helpers/application_helper.rb +27 -0
  12. data/app/models/google_maps_geocoder.rb +108 -0
  13. data/app/models/locale.rb +204 -0
  14. data/app/models/location.rb +216 -0
  15. data/app/models/location_import.rb +48 -0
  16. data/app/models/service.rb +27 -0
  17. data/app/models/zip_code.rb +48 -0
  18. data/app/models/zip_code_import.rb +19 -0
  19. data/app/views/directory/_search_form.html.erb +4 -0
  20. data/app/views/directory/_show_children.html.erb +29 -0
  21. data/app/views/directory/_show_location.html.erb +14 -0
  22. data/app/views/directory/search.html.erb +31 -0
  23. data/app/views/directory/show.html.erb +32 -0
  24. data/app/views/layouts/application.html.erb +49 -0
  25. data/app/views/locations/_form.html.erb +69 -0
  26. data/app/views/locations/edit.html.erb +3 -0
  27. data/app/views/locations/export.erb +4 -0
  28. data/app/views/locations/import.html.erb +13 -0
  29. data/app/views/locations/index.html.erb +37 -0
  30. data/app/views/locations/new.html.erb +3 -0
  31. data/app/views/locations/show.html.erb +70 -0
  32. data/app/views/services/_form.html.erb +29 -0
  33. data/app/views/services/edit.html.erb +3 -0
  34. data/app/views/services/index.html.erb +25 -0
  35. data/app/views/services/new.html.erb +3 -0
  36. data/app/views/services/show.html.erb +15 -0
  37. data/app/views/shared/_location.html.erb +18 -0
  38. data/app/views/shared/_map.html.erb +2 -0
  39. data/app/views/shared/_nav_tabs.html.erb +5 -0
  40. data/autotest/discover.rb +1 -0
  41. data/campfire_logic.gemspec +208 -0
  42. data/config/application.rb +44 -0
  43. data/config/boot.rb +13 -0
  44. data/config/cucumber.yml +10 -0
  45. data/config/deploy.rb +40 -0
  46. data/config/environment.rb +6 -0
  47. data/config/environments/development.rb +28 -0
  48. data/config/environments/production.rb +49 -0
  49. data/config/environments/test.rb +35 -0
  50. data/config/initializers/campfire_logic.rb +3 -0
  51. data/config/initializers/metric_fu.rb +9 -0
  52. data/config/initializers/secret_token.rb +7 -0
  53. data/config/initializers/session_store.rb +8 -0
  54. data/config/locales/en.yml +5 -0
  55. data/config/mongoid.yml +25 -0
  56. data/config/routes.rb +96 -0
  57. data/config.ru +4 -0
  58. data/db/seeds.rb +5 -0
  59. data/db/zip_codes.txt +42742 -0
  60. data/doc/google_maps_response.rb +56 -0
  61. data/features/admin_manages_locations.feature +10 -0
  62. data/features/customer_browses_directory.feature +25 -0
  63. data/features/customer_searches_directory.feature +29 -0
  64. data/features/step_definitions/directory_steps.rb +22 -0
  65. data/features/step_definitions/location_steps.rb +10 -0
  66. data/features/step_definitions/web_steps.rb +211 -0
  67. data/features/support/env.rb +31 -0
  68. data/features/support/paths.rb +33 -0
  69. data/features/support/selectors.rb +39 -0
  70. data/init.rb +1 -0
  71. data/lib/campfire_logic/engine.rb +7 -0
  72. data/lib/campfire_logic/railtie.rb +10 -0
  73. data/lib/campfire_logic.rb +31 -0
  74. data/lib/tasks/campfire_logic.rake +7 -0
  75. data/lib/tasks/cucumber.rake +71 -0
  76. data/public/404.html +26 -0
  77. data/public/422.html +26 -0
  78. data/public/500.html +26 -0
  79. data/public/favicon.ico +0 -0
  80. data/public/images/icons/collapsed.gif +0 -0
  81. data/public/images/icons/delete.png +0 -0
  82. data/public/images/icons/drag.png +0 -0
  83. data/public/images/icons/edit.png +0 -0
  84. data/public/images/icons/expanded.gif +0 -0
  85. data/public/images/icons/help_icon.png +0 -0
  86. data/public/images/icons/link_icon.png +0 -0
  87. data/public/images/icons/move.png +0 -0
  88. data/public/images/icons/move_white.png +0 -0
  89. data/public/images/icons/note.png +0 -0
  90. data/public/images/icons/note_white.png +0 -0
  91. data/public/images/icons/notification_icon_sprite.png +0 -0
  92. data/public/images/icons/spinner.gif +0 -0
  93. data/public/images/icons/view.png +0 -0
  94. data/public/images/icons/warning.png +0 -0
  95. data/public/images/icons/warning_2.png +0 -0
  96. data/public/images/icons/warning_box.png +0 -0
  97. data/public/images/icons/warning_icon.png +0 -0
  98. data/public/images/icons/warning_white.png +0 -0
  99. data/public/images/layout/arrow_asc.png +0 -0
  100. data/public/images/layout/arrow_desc.png +0 -0
  101. data/public/images/layout/black_bar.png +0 -0
  102. data/public/images/layout/branding.png +0 -0
  103. data/public/images/layout/button_bg.png +0 -0
  104. data/public/images/layout/content_left_bg.png +0 -0
  105. data/public/images/layout/content_right_bg.png +0 -0
  106. data/public/images/layout/footer_bg.png +0 -0
  107. data/public/images/layout/h2_bg.png +0 -0
  108. data/public/images/layout/h2_bg_for_table.png +0 -0
  109. data/public/images/layout/header_bg_grey.png +0 -0
  110. data/public/images/layout/header_bg_purple.png +0 -0
  111. data/public/images/layout/legend_bg.png +0 -0
  112. data/public/images/layout/text_field_bg.jpg +0 -0
  113. data/public/images/layout/text_field_error_bg.png +0 -0
  114. data/public/images/layout/th_bg.png +0 -0
  115. data/public/images/layout/th_bg_selected.png +0 -0
  116. data/public/images/rails.png +0 -0
  117. data/public/index.html +9 -0
  118. data/public/javascripts/application.js +2 -0
  119. data/public/javascripts/controls.js +965 -0
  120. data/public/javascripts/dragdrop.js +974 -0
  121. data/public/javascripts/effects.js +1123 -0
  122. data/public/javascripts/prototype.js +6001 -0
  123. data/public/javascripts/rails.js +175 -0
  124. data/public/robots.txt +6 -0
  125. data/public/sample-locations.xls +1 -0
  126. data/public/stylesheets/.gitkeep +0 -0
  127. data/public/stylesheets/application.css +682 -0
  128. data/public/stylesheets/core.css +1147 -0
  129. data/public/stylesheets/core_ie.css +52 -0
  130. data/public/stylesheets/csshover3.htc +14 -0
  131. data/script/cucumber +10 -0
  132. data/script/rails +6 -0
  133. data/spec/controllers/directory_controller_spec.rb +11 -0
  134. data/spec/controllers/services_controller_spec.rb +62 -0
  135. data/spec/models/google_maps_geocoder_spec.rb +62 -0
  136. data/spec/models/locale_spec.rb +64 -0
  137. data/spec/models/location_import_spec.rb +41 -0
  138. data/spec/models/location_spec.rb +195 -0
  139. data/spec/rcov.opts +2 -0
  140. data/spec/spec_helper.rb +12 -0
  141. data/spec/test-locations.xls +1 -0
  142. metadata +361 -0
@@ -0,0 +1,52 @@
1
+ /* @group General IE Workarounds */
2
+
3
+ fieldset.form_container {
4
+ clear: both;
5
+ width: 96%;
6
+ z-index: 0;
7
+ }
8
+
9
+ legend {
10
+ display: none;
11
+ }
12
+
13
+ .faux_legend {
14
+ border: 1px solid #999999;
15
+ padding: .5em;
16
+ background-color: #79984c;
17
+ color: #ffffff;
18
+ margin-top: -2.5em;
19
+ margin-left: -1.4em;
20
+ width: 26.5em;
21
+ font-weight: bold;
22
+ font-size: .8em;
23
+ text-transform: uppercase;
24
+ padding-left: 1em;
25
+ }
26
+
27
+ /* @end */
28
+
29
+ /* @group IE6 Workarounds */
30
+
31
+ /* Add support for hover, for nav menu */
32
+ *html body {
33
+ behavior:url("/stylesheets/csshover3.htc");
34
+ }
35
+
36
+ * html fieldset.form_container {
37
+ width: 92%;
38
+ margin-top: 20px !important;
39
+ z-index: 0;
40
+ }
41
+
42
+ * html p {
43
+ margin-bottom: 1.5em;
44
+ }
45
+
46
+ * html .faux_legend {
47
+ margin-top: -12px;
48
+ }
49
+
50
+
51
+ /* @end */
52
+
@@ -0,0 +1,14 @@
1
+ <public:attach event="ondocumentready" onevent="CSSHover()" />
2
+ <script>
3
+ /**
4
+ * Whatever:hover - V3.00.081222
5
+ * --------------------------------------------------------
6
+ * Author - Peter Nederlof, http://www.xs4all.nl/~peterned
7
+ * License - http://creativecommons.org/licenses/LGPL/2.1
8
+ * Packed - http://dean.edwards.name/packer
9
+ *
10
+ * howto: body { behavior:url("csshover3.htc"); }
11
+ */
12
+ eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('r.R=(8(){4 f=/(^|\\s)((([^a]([^ ]+)?)|(a([^#.][^ ]+)+)):(C|D|E))/i,S=/(.*?)\\:(C|D|E)/i,T=/[^:]+:([a-z-]+).*/i,U=/(\\.([a-V-W-]+):[a-z]+)|(:[a-z]+)/1c,X=/\\.([a-V-W-]*Y(C|D|E))/i,Z=/1d (5|6|7)/i,10=/1e/i;4 g=\'1f-\';4 h={p:[],t:{},11:8(){n(!Z.F(1g.1h)&&!10.F(r.12.1i))u;4 a=r.12.1j,l=a.v;w(4 i=0;i<l;i++){3.G(a[i])}},G:8(a){n(a.H){I{4 b=a.H,l=b.v;w(4 i=0;i<l;i++){3.G(a.H[i])}}J(13){}}I{4 c=a.1k,l=c.v;w(4 j=0;j<l;j++){3.14(c[j],a)}}J(13){}},14:8(a,b){4 c=a.1l;n(f.F(c)){4 d=a.K.1m,L=S.15(c)[1],M=c.N(T,\'Y$1\'),O=c.N(U,\'.$2\'+M),o=X.15(O)[1];4 e=L+o;n(!3.t[e]){b.16(L,g+o+\':1n(R(3, "\'+M+\'", "\'+o+\'"))\');3.t[e]=17}b.16(O,d)}},18:8(a,b,c){4 d=g+c;n(a.K[d]){a.K[d]=q}n(!a.x)a.x=[];n(!a.x[c]){a.x[c]=17;4 e=19 P(a,b,c);3.p.1o(e)}u b},y:8(){I{4 l=3.p.v;w(4 i=0;i<l;i++){3.p[i].y()}3.p=[];3.t={}}J(e){}}};r.Q(\'1p\',8(){h.y()});4 k={1q:{9:\'1r\',m:\'1s\'},1t:{9:\'1u\',m:\'1v\'},1a:{9:\'1a\',m:\'1w\'}};8 P(a,b,c){3.A=a;3.B=b;4 d=19 1x(\'(^|\\\\s)\'+c+\'(\\\\s|$)\',\'g\');3.9=8(){a.o+=\' \'+c};3.m=8(){a.o=a.o.N(d,\' \')};a.Q(k[b].9,3.9);a.Q(k[b].m,3.m)}P.1y={y:8(){3.A.1b(k[3.B].9,3.9);3.A.1b(k[3.B].m,3.m);3.9=q;3.m=q;3.A=q;3.B=q}};u 8(a,b,c){n(a){u h.18(a,b,c)}1z{h.11()}}})();',62,98,'|||this|var||||function|activator|||||||||||||deactivator|if|className|elements|null|window||callbacks|return|length|for|csshover|unload||node|type|hover|active|focus|test|parseStylesheet|imports|try|catch|style|affected|pseudo|replace|newSelect|CSSHoverElement|attachEvent|CSSHover|REG_AFFECTED|REG_PSEUDO|REG_SELECT|z0|9_|REG_CLASS|on|REG_MSIE|REG_COMPAT|init|document|securityException|parseCSSRule|exec|addRule|true|patch|new|onfocus|detachEvent|gi|msie|backcompat|csh|navigator|userAgent|compatMode|styleSheets|rules|selectorText|cssText|expression|push|onbeforeunload|onhover|onmouseenter|onmouseleave|onactive|onmousedown|onmouseup|onblur|RegExp|prototype|else'.split('|'),0,{}));
13
+
14
+ </script>
data/script/cucumber ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
4
+ if vendored_cucumber_bin
5
+ load File.expand_path(vendored_cucumber_bin)
6
+ else
7
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
8
+ require 'cucumber'
9
+ load Cucumber::BINARY
10
+ end
data/script/rails ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe DirectoryController do
4
+ it 'show action should render show template' do
5
+ Locale.stubs(:find).returns(Locale.new :lat_lng => [], :slug => 'foo')
6
+ Locale.stubs(:us).returns([Locale.find])
7
+ Locale.any_instance.stubs(:children).returns([])
8
+ get :show, :slug => 'foo'
9
+ response.should render_template(:show)
10
+ end
11
+ end
@@ -0,0 +1,62 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe ServicesController do
4
+ render_views
5
+
6
+ before :each do
7
+ @service = Service.new
8
+ Service.stubs(:find).returns(@service)
9
+ @service.stubs(:save).returns(true)
10
+ @service.stubs(:to_param).returns('1')
11
+ end
12
+
13
+ it 'gets index action' do
14
+ Service.stubs(:find).returns([@service])
15
+ get :index
16
+ response.should render_template(:index)
17
+ end
18
+
19
+ it 'gets show action' do
20
+ get :show, :id => 1
21
+ response.should render_template(:show)
22
+ end
23
+
24
+ it 'gets new action' do
25
+ get :new
26
+ response.should render_template(:new)
27
+ end
28
+
29
+ it 'posts create action and renders new template when model is invalid' do
30
+ Service.any_instance.stubs(:save).returns(false)
31
+ post :create
32
+ response.should render_template(:new)
33
+ end
34
+
35
+ it 'posts create action and redirects' do
36
+ post :create
37
+ response.should be_redirect
38
+ end
39
+
40
+ it 'gets edit action' do
41
+ get :edit, :id => 1
42
+ response.should render_template(:edit)
43
+ end
44
+
45
+ it 'puts update action and renders edit template when model is invalid' do
46
+ @service.stubs(:save).returns(false)
47
+ put :update, :id => 1
48
+ response.should render_template(:edit)
49
+ end
50
+
51
+ it 'puts update action and redirects' do
52
+ @service.stubs(:valid?).returns(true)
53
+ put :update, :id => 1
54
+ response.should be_redirect
55
+ end
56
+
57
+ it 'deletes destroy action and redirects' do
58
+ @service.expects(:destroy)
59
+ delete :destroy, :id => 1
60
+ response.should be_redirect
61
+ end
62
+ end
@@ -0,0 +1,62 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe GoogleMapsGeocoder do
4
+ before(:all) do
5
+ begin
6
+ @exact_match = GoogleMapsGeocoder.new('837 Union St Brooklyn NY')
7
+ @partial_match = GoogleMapsGeocoder.new('1600 Pennsylvania Washington')
8
+ rescue SocketError
9
+ @no_network = true
10
+ rescue RuntimeError
11
+ @query_limit = true
12
+ end
13
+ end
14
+
15
+ before(:each) do
16
+ pending 'waiting for a network connection', :if => @no_network
17
+ pending 'waiting for query limit to pass', :if => @query_limit
18
+ end
19
+
20
+ context 'with "837 Union St Brooklyn NY"' do
21
+ subject { @exact_match }
22
+ specify { should be_exact_match }
23
+
24
+ context 'address' do
25
+ specify { subject.formatted_street_address.should == '837 Union St' }
26
+ specify { subject.city.should == 'Brooklyn' }
27
+ specify { subject.county.should == 'Kings' }
28
+ specify { subject.state_long_name.should == 'New York' }
29
+ specify { subject.state_short_name.should == 'NY' }
30
+ specify { subject.postal_code.should == '11215' }
31
+ specify { subject.country_short_name.should == 'US' }
32
+ specify { subject.country_long_name.should == 'United States' }
33
+ specify { subject.formatted_address.should == '837 Union St, Brooklyn, NY 11215, USA' }
34
+ end
35
+
36
+ context 'coordinates' do
37
+ specify { subject.lat.should be_within(0.005).of(40.6748151) }
38
+ specify { subject.lng.should be_within(0.005).of(-73.9760302) }
39
+ end
40
+ end
41
+
42
+ context 'with "1600 Pennsylvania Washington"' do
43
+ subject { @partial_match }
44
+ specify { should be_partial_match }
45
+
46
+ context 'address' do
47
+ specify { subject.formatted_street_address.should == '1600 Pennsylvania Ave NW' }
48
+ specify { subject.city.should == 'Washington D.C.' }
49
+ specify { subject.state_long_name.should == 'District of Columbia' }
50
+ specify { subject.state_short_name.should == 'DC' }
51
+ specify { subject.postal_code.should == '20500' }
52
+ specify { subject.country_short_name.should == 'US' }
53
+ specify { subject.country_long_name.should == 'United States' }
54
+ specify { subject.formatted_address.should == '1600 Pennsylvania Ave NW, Washington D.C., DC 20500, USA' }
55
+ end
56
+
57
+ context 'coordinates' do
58
+ specify { subject.lat.should be_within(0.005).of(38.8976964) }
59
+ specify { subject.lng.should be_within(0.005).of(-77.0365191) }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,64 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Locale do
4
+ before(:all) do
5
+ Locale.delete_all
6
+ Locale.init_root
7
+ end
8
+
9
+ before(:each) do
10
+ GoogleMapsGeocoder.stubs(:new).returns(stub('prospect park zoo', :city => 'Brooklyn', :country_long_name => 'United States', :country_short_name => 'US', :formatted_address => '450 Flatbush Avenue', :formatted_street_address => '450 Flatbush Avenue', :lat => 1, :lng => 1, :partial_match? => false, :postal_code => '11217', :state_long_name => 'New York', :state_short_name => 'NY'))
11
+ location = Location.new(:address1 => '450 Flatbush Avenue', :city => 'Brooklyn', :name => 'Prospect Park Zoo', :state => 'NY')
12
+ location.validate_address!
13
+ location.localize
14
+ @locale = location.locale
15
+ end
16
+
17
+ it 'inits the root locale' do
18
+ Locale.root.name.should == 'Earth'
19
+ end
20
+
21
+ it 'inits a country locale' do
22
+ Locale.us.first.name.should == 'United States'
23
+ end
24
+
25
+ it 'sets its slug' do
26
+ @locale.to_url.include?('new-york/brooklyn/prospect-park-zoo').should be_true
27
+ end
28
+
29
+ it 'returns an address for geocoding' do
30
+ @locale.location.state_locale.parent.geocoding_address.should == 'United States'
31
+ @locale.location.state_locale.geocoding_address.should == 'New York'
32
+ @locale.location.city_locale.geocoding_address.should == 'Brooklyn New York'
33
+ @locale.geocoding_address.should be_nil
34
+ end
35
+
36
+ it 'returns its geographical kind' do
37
+ @locale.kind.should == 'location'
38
+ @locale.parent.kind.should == 'city'
39
+ @locale.parent.parent.kind.should == 'state'
40
+ end
41
+
42
+ it 'can skip geocoding on creation' do
43
+ @locale = Locale.new :name => 'White House', :skip_geocoding => true
44
+ @locale.stubs(:save)
45
+ @locale.save
46
+ @locale.geocoded?.should be_false
47
+ end
48
+
49
+ describe "friendly names" do
50
+
51
+ it 'returns a friendly name for a city' do
52
+ city = Locale.cities.first
53
+ city.friendly_name.should =~ /#{city.name}/
54
+ city.friendly_name.should =~ /#{city.parent.name}/
55
+ end
56
+
57
+ it 'returns a friendly name for a state' do
58
+ state = Locale.states.first
59
+ state.friendly_name.should == state.name
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,41 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe LocationImport do
4
+ before(:all) do
5
+ Locale.delete_all
6
+ Location.delete_all
7
+ end
8
+
9
+ before(:each) do
10
+ Locale.any_instance.stubs(:geocode)
11
+ Location.any_instance.stubs(:country_long_name).returns('United States')
12
+ Location.any_instance.stubs(:geocode).returns(:validated)
13
+ Location.any_instance.stubs(:state_long_name).returns('New York')
14
+ LocationImport.test('spec/test-locations.xls')
15
+ @location = Location.first(:conditions => {:location_number => '174'})
16
+ LocationImport.test('spec/test-locations.xls')
17
+ end
18
+
19
+ it 'imports a location from the import file' do
20
+ @location.location_number.should == '174'
21
+ @location.name.should == 'Mobile Mini Jericho'
22
+ @location.address1.should == '1158 Jericho Turnpike'
23
+ @location.city.should == 'Commack'
24
+ @location.state_long_name.should == 'New York'
25
+ @location.zip.should == '11725'
26
+ @location.phone.should == '(877) 300-0059'
27
+ @location.email.should == 'sales@mobilemini.com'
28
+ @location.meta_description.should == 'Long Island portable storage rentals. Secure mobile storage units or pods, shipping container rentals, and custom mobile office trailers shipped to your home or construction site.'
29
+ end
30
+
31
+ it 'initializes a locale tree for each location' do
32
+ @location.locale.kind.should == 'location'
33
+ @location.locale.name.should == @location.name
34
+ Locale.all.map{ |l| l.kind }.sort.should == ['city', 'city', 'country', 'location', 'location', 'planet', 'state']
35
+ Locale.all.map{ |l| l.name.to_s }.sort.should == ['Commack', 'Earth', 'Mobile Mini Jericho', 'Mobile Mini Utica', 'New York', 'United States', 'Utica']
36
+ end
37
+
38
+ it "doesn't create duplicate locations" do
39
+ Location.all.map{ |l| l.location_number }.sort.should == ['157', '174']
40
+ end
41
+ end
@@ -0,0 +1,195 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Location do
4
+ it 'handles query-limit errors' do
5
+ ActiveSupport::JSON.stubs(:decode)
6
+ location = Location.new(:city => 'new york').geocode.should == :new
7
+ end
8
+ end
9
+
10
+ describe Location do
11
+ before(:all) do
12
+ Locale.delete_all
13
+ Location.delete_all
14
+ end
15
+
16
+ before(:each) do
17
+ GoogleMapsGeocoder.stubs(:new).returns(stub('white house', :city => 'Washington', :country_short_name => 'US', :country_long_name => 'United States', :formatted_address => '1600 Pennsylvania Ave NW', :formatted_street_address => '1600 Pennsylvania Ave NW', :lat => 1, :lng => 1, :partial_match? => true, :postal_code => '20500', :state_long_name => 'District of Columbia', :state_short_name => 'DC'))
18
+ @white_house = Location.new :address1 => '1600 Pennsylvania', :city => 'Washington', :name => 'White House'
19
+ @white_house.validate_address!
20
+ @white_house.localize
21
+
22
+ GoogleMapsGeocoder.stubs(:new).returns(stub('tea lounge', :city => 'Brooklyn', :country_short_name => 'US', :country_long_name => 'United States', :formatted_address => '837 Union St', :formatted_street_address => '837 Union St', :lat => 1, :lng => 1, :partial_match? => false, :postal_code => '11217', :state_long_name => 'New York', :state_short_name => 'NY'))
23
+ @tea_lounge = Location.new :address1 => ' 837 Union St ', :address2 => ' Suite 1 ', :city => ' Brooklyn ', :name => ' Tea Lounge ', :state => ' NY ', :zip => ' 11217 '
24
+ @tea_lounge.validate_address!
25
+ @tea_lounge.localize
26
+
27
+ GoogleMapsGeocoder.stubs(:new).returns(stub('two boots', :city => 'Brooklyn', :country_short_name => 'US', :country_long_name => 'United States', :formatted_address => '837 Union St', :formatted_street_address => '837 Union St', :lat => 1, :lng => 1, :partial_match? => false, :postal_code => '11217', :state_long_name => 'New York', :state_short_name => 'NY'))
28
+ @two_boots = Location.new(:address1 => '514 2nd Street', :city => 'Brooklyn', :name => 'Two Boots of Brooklyn', :state => 'NY')
29
+ @two_boots.validate_address!
30
+ @two_boots.localize
31
+
32
+ GoogleMapsGeocoder.stubs(:new).returns(stub('new park', :city => 'Howard Beach', :country_short_name => 'US', :country_long_name => 'United States', :formatted_address => '15671 Crossbay Boulevard', :formatted_street_address => '15671 Crossbay Boulevard', :lat => 1, :lng => 1, :partial_match? => false, :postal_code => '11217', :state_long_name => 'New York', :state_short_name => 'NY'))
33
+ @new_park = Location.new(:address1 => '15671 Crossbay Boulevard', :city => 'Howard Beach', :name => 'New Park Pizza', :state => 'NY')
34
+ @new_park.validate_address!
35
+ @new_park.localize
36
+ end
37
+
38
+ context 'addressing' do
39
+ it 'returns an address for geocoding' do
40
+ @tea_lounge.geocoding_address.should == '837 Union St Brooklyn NY'
41
+ @white_house.geocoding_address.should == '1600 Pennsylvania Washington DC'
42
+ end
43
+
44
+ it 'returns a concatenated street address' do
45
+ @tea_lounge.street_address.should == '837 Union St Suite 1'
46
+ @white_house.street_address.should == '1600 Pennsylvania'
47
+ end
48
+
49
+ it 'approves user-validated addresses' do
50
+ @white_house.approve_address!
51
+ @white_house.should be_validated
52
+ end
53
+ end
54
+
55
+ context 'geocoding' do
56
+ it 'geocodes itself' do
57
+ @tea_lounge.geocoded?.should be_true
58
+ @white_house.geocoded?.should be_true
59
+ end
60
+
61
+ it 'validates addresses with Google Maps' do
62
+ @tea_lounge.country_long_name.should == 'United States'
63
+ @tea_lounge.state_long_name.should == 'New York'
64
+ @tea_lounge.validated_address1.should be_blank
65
+ @tea_lounge.validated_city.should be_blank
66
+ @tea_lounge.validated_zip.should be_blank
67
+ @tea_lounge.should be_validated
68
+
69
+ @white_house.should be_partial_match
70
+ @white_house.country_long_name.should == 'United States'
71
+ @white_house.state_long_name.should == 'District of Columbia'
72
+ @white_house.validated_address1.should == '1600 Pennsylvania Ave NW'
73
+ @white_house.validated_city.should be_blank
74
+ @white_house.validated_zip.should == '20500'
75
+ end
76
+
77
+ it 'detects matching address fields' do
78
+ @tea_lounge.matches_geocoder?(:address1).should be_true
79
+ @tea_lounge.matches_geocoder?(:city).should be_true
80
+ @tea_lounge.matches_geocoder?(:zip).should be_true
81
+ end
82
+
83
+ it 'detects mismatching address fields' do
84
+ @white_house.matches_geocoder?(:address1).should be_false
85
+ @white_house.matches_geocoder?(:zip).should be_false
86
+ end
87
+
88
+ it 're-validates modified addresses with Google Maps' do
89
+ @white_house.address1 = ''
90
+ GoogleMapsGeocoder.stubs(:new).returns(stub('white house', :city => 'Washington', :country_short_name => 'US', :country_long_name => 'United States', :formatted_address => '1600 Pennsylvania Ave NW', :formatted_street_address => '1600 Pennsylvania Ave NW', :lat => 1, :lng => 1, :partial_match? => false, :postal_code => '20500', :state_long_name => 'District of Columbia', :state_short_name => 'DC'))
91
+ @white_house.validate_address!
92
+ @white_house.should be_validated
93
+ end
94
+
95
+ it 'preserves geocoding if updated with irrelevant changes' do
96
+ @white_house.update_attributes :name => 'The White House', :city => 'Washington'
97
+ @white_house.geocoded?.should be_true
98
+ end
99
+
100
+ it 'clears geocoding if any address field is modified' do
101
+ @white_house.city = 'New York'
102
+ @white_house.geocoder.should be_nil
103
+ @white_house.lat_lng.should be_nil
104
+ @white_house.state_long_name.should be_nil
105
+ @white_house.validated_address1.should be_nil
106
+ @white_house.validated_city.should be_nil
107
+ @white_house.validated_zip.should be_nil
108
+ @white_house.should be_new
109
+ end
110
+ end
111
+
112
+ context 'localization' do
113
+ it 'creates a locale tree for a new location' do
114
+ @tea_lounge.locale.name.should == 'Tea Lounge'
115
+ @tea_lounge.city_locale.city?.should be_true
116
+ @tea_lounge.city_locale.name.should == 'Brooklyn'
117
+ @tea_lounge.state_locale.state?.should be_true
118
+ @tea_lounge.state_locale.name.should == 'New York'
119
+ @tea_lounge.state_locale.parent.should == Locale.us.first
120
+ end
121
+
122
+ it 'merges a new location into a matching city tree' do
123
+ @two_boots.locale.name.should == 'Two Boots of Brooklyn'
124
+ @two_boots.locale.sibling_of?(@tea_lounge.locale).should be_true
125
+ @two_boots.city_locale.should == @tea_lounge.city_locale
126
+ @two_boots.state_locale.should == @tea_lounge.state_locale
127
+ @two_boots.state_locale.parent.should == Locale.us.first
128
+ end
129
+
130
+ it 'merges a new location into a matching state tree' do
131
+ @new_park.locale.name.should == 'New Park Pizza'
132
+ @new_park.city_locale.city?.should be_true
133
+ @new_park.city_locale.name.should == 'Howard Beach'
134
+ @new_park.state_locale.should == @tea_lounge.state_locale
135
+ @new_park.state_locale.parent.should == Locale.us.first
136
+ end
137
+
138
+ it 'leaves the locale tree intact if other locations belong to it' do
139
+ @two_boots.city_locale.city?.should be_true
140
+ @two_boots.city_locale.name.should == 'Brooklyn'
141
+ @two_boots.state_locale.state?.should be_true
142
+ @two_boots.state_locale.name.should == 'New York'
143
+ @two_boots.state_locale.parent.should == Locale.us.first
144
+ end
145
+
146
+ it 'updates the locale tree of a modified location' do
147
+ @tea_lounge.name = 'White House'
148
+ @tea_lounge.address1 = '1600 Pennsylvania Ave NW'
149
+ @tea_lounge.city = 'Washington'
150
+ @tea_lounge.state = 'DC'
151
+ GoogleMapsGeocoder.stubs(:new).returns(stub('overridden tea lounge', :city => 'Washington', :country_short_name => 'US', :country_long_name => 'United States', :formatted_address => '1600 Pennsylvania Ave NW', :formatted_street_address => '1600 Pennsylvania Ave NW', :lat => 1, :lng => 1, :partial_match? => false, :postal_code => '20500', :state_long_name => 'District of Columbia', :state_short_name => 'DC'))
152
+ @tea_lounge.validate_address!
153
+ @tea_lounge.localize
154
+ @tea_lounge.city_locale.city?.should be_true
155
+ @tea_lounge.city_locale.name.should == 'Washington'
156
+ @tea_lounge.state_locale.state?.should be_true
157
+ @tea_lounge.state_locale.name.should == 'District of Columbia'
158
+ @tea_lounge.state_locale.parent.should == Locale.us.first
159
+ end
160
+ end
161
+
162
+ it "destroys its city when it's the only location" do
163
+ Locale.exists?(:conditions => {:name => 'Tea Lounge'}).should be_true
164
+ Locale.exists?(:conditions => {:name => 'Brooklyn'}).should be_true
165
+ Locale.exists?(:conditions => {:name => 'New York'}).should be_true
166
+ @tea_lounge.destroy
167
+ # TODO: strangely, this assertion fails even though destroys work in the app and all subsequent assertions pass
168
+ # Location.exists?(:conditions => {:name => 'Tea Lounge'}).should be_false
169
+ Locale.exists?(:conditions => {:name => 'Tea Lounge'}).should be_false
170
+ Locale.exists?(:conditions => {:name => 'Brooklyn'}).should be_true
171
+ Locale.exists?(:conditions => {:name => 'New York'}).should be_true
172
+
173
+ Locale.exists?(:conditions => {:name => 'Two Boots of Brooklyn'}).should be_true
174
+ Locale.exists?(:conditions => {:name => 'Brooklyn'}).should be_true
175
+ Locale.exists?(:conditions => {:name => 'New York'}).should be_true
176
+ @two_boots.destroy
177
+ # TODO: strangely, this assertion fails even though destroys work in the app and all subsequent assertions pass
178
+ # Location.exists?(:conditions => {:name => 'Two Boots of Brooklyn'}).should be_false
179
+ Locale.exists?(:conditions => {:name => 'Two Boots of Brooklyn'}).should be_false
180
+ Locale.exists?(:conditions => {:name => 'Brooklyn'}).should be_false
181
+ Locale.exists?(:conditions => {:name => 'New York'}).should be_true
182
+ end
183
+
184
+ it "destroys its state when it's the only location" do
185
+ Locale.exists?(:conditions => {:name => 'White House'}).should be_true
186
+ Locale.exists?(:conditions => {:name => 'Washington'}).should be_true
187
+ Locale.exists?(:conditions => {:name => 'District of Columbia'}).should be_true
188
+ @white_house.destroy
189
+ # TODO: strangely, this assertion fails even though destroys work in the app and all subsequent assertions pass
190
+ # Location.exists?(:conditions => {:name => 'White House'}).should be_false
191
+ Locale.exists?(:conditions => {:name => 'White House'}).should be_false
192
+ Locale.exists?(:conditions => {:name => 'Washington'}).should be_false
193
+ Locale.exists?(:conditions => {:name => 'District of Columbia'}).should be_false
194
+ end
195
+ end
data/spec/rcov.opts ADDED
@@ -0,0 +1,2 @@
1
+ --exclude \.rvm
2
+ --rails
@@ -0,0 +1,12 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ require File.expand_path("../../config/environment", __FILE__)
4
+ require 'rspec/rails'
5
+
6
+ # Requires supporting ruby files with custom matchers and macros, etc,
7
+ # in spec/support/ and its subdirectories.
8
+ Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+ config.mock_with :mocha
12
+ end
@@ -0,0 +1 @@
1
+ Location # Name Address 1 Address 2 City State Country Code Postal Code Phone Email Description