campfire_logic 1.1.7

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.
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