mls 0.2.2 → 0.2.3

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 (57) hide show
  1. data/.gitignore +1 -0
  2. data/README.rdoc +54 -0
  3. data/Rakefile +20 -2
  4. data/lib/mls.rb +307 -78
  5. data/lib/mls/errors.rb +13 -4
  6. data/lib/mls/model.rb +36 -5
  7. data/lib/mls/models/account.rb +74 -66
  8. data/lib/mls/models/address.rb +87 -6
  9. data/lib/mls/models/area.rb +27 -0
  10. data/lib/mls/models/flyer.rb +41 -0
  11. data/lib/mls/models/listing.rb +180 -34
  12. data/lib/mls/models/photo.rb +25 -3
  13. data/lib/mls/models/tour_request.rb +18 -31
  14. data/lib/mls/parser.rb +5 -4
  15. data/lib/mls/properties/datetime.rb +5 -1
  16. data/lib/mls/properties/decimal.rb +3 -1
  17. data/lib/mls/properties/hash.rb +7 -0
  18. data/lib/mls/resource.rb +35 -5
  19. data/lib/rdoc/generator/template/42floors/_context.rhtml +209 -0
  20. data/lib/rdoc/generator/template/42floors/_head.rhtml +7 -0
  21. data/lib/rdoc/generator/template/42floors/class.rhtml +39 -0
  22. data/lib/rdoc/generator/template/42floors/file.rhtml +35 -0
  23. data/lib/rdoc/generator/template/42floors/index.rhtml +13 -0
  24. data/lib/rdoc/generator/template/42floors/resources/apple-touch-icon.png +0 -0
  25. data/lib/rdoc/generator/template/42floors/resources/css/github.css +129 -0
  26. data/lib/rdoc/generator/template/42floors/resources/css/main.css +339 -0
  27. data/lib/rdoc/generator/template/42floors/resources/css/panel.css +389 -0
  28. data/lib/rdoc/generator/template/42floors/resources/css/reset.css +48 -0
  29. data/lib/rdoc/generator/template/42floors/resources/favicon.ico +0 -0
  30. data/lib/rdoc/generator/template/42floors/resources/i/arrows.png +0 -0
  31. data/lib/rdoc/generator/template/42floors/resources/i/results_bg.png +0 -0
  32. data/lib/rdoc/generator/template/42floors/resources/i/tree_bg.png +0 -0
  33. data/lib/rdoc/generator/template/42floors/resources/js/highlight.pack.js +1 -0
  34. data/lib/rdoc/generator/template/42floors/resources/js/jquery-1.3.2.min.js +19 -0
  35. data/lib/rdoc/generator/template/42floors/resources/js/jquery-effect.js +593 -0
  36. data/lib/rdoc/generator/template/42floors/resources/js/main.js +20 -0
  37. data/lib/rdoc/generator/template/42floors/resources/js/searchdoc.js +442 -0
  38. data/lib/rdoc/generator/template/42floors/resources/panel/index.html +73 -0
  39. data/lib/rdoc/generator/template/42floors/se_index.rhtml +8 -0
  40. data/mls.gemspec +7 -4
  41. data/test/factories/account.rb +18 -0
  42. data/test/factories/address.rb +15 -0
  43. data/test/factories/listing.rb +30 -0
  44. data/test/factories/tour_request.rb +9 -0
  45. data/test/fixtures/flyer.pdf +68 -0
  46. data/test/test_helper.rb +44 -5
  47. data/test/units/models/test_account.rb +20 -43
  48. data/test/units/models/test_flyer.rb +22 -0
  49. data/test/units/models/test_listing.rb +119 -0
  50. data/test/units/models/test_photo.rb +136 -3
  51. data/test/units/models/test_tour_request.rb +25 -20
  52. data/test/units/test_errors.rb +12 -4
  53. data/test/units/test_mls.rb +263 -3
  54. data/test/units/test_resource.rb +1 -0
  55. metadata +78 -57
  56. data/lib/mls/models/user.rb +0 -7
  57. data/lib/mls/version.rb +0 -3
@@ -0,0 +1,73 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
3
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
6
+ <head>
7
+ <title>search index</title>
8
+ <link rel="stylesheet" href="../css/reset.css" type="text/css" media="screen" charset="utf-8" />
9
+ <link rel="stylesheet" href="../css/panel.css" type="text/css" media="screen" charset="utf-8" />
10
+ <script src="../js/search_index.js" type="text/javascript" charset="utf-8"></script>
11
+ <script src="../js/searcher.js" type="text/javascript" charset="utf-8"></script>
12
+ <script src="tree.js" type="text/javascript" charset="utf-8"></script>
13
+ <script src="../js/jquery-1.3.2.min.js" type="text/javascript" charset="utf-8"></script>
14
+ <script src="../js/searchdoc.js" type="text/javascript" charset="utf-8"></script>
15
+ <script type="text/javascript" charset="utf-8">
16
+ function placeholder() {
17
+ if ($('<input type="text">')[0].placeholder !== undefined) return;
18
+
19
+ $('#search-label').click(function() {
20
+ $('#search').focus();
21
+ $('#search-label').hide();
22
+ });
23
+
24
+ $('#search').focus(function() {
25
+ $('#search-label').hide();
26
+ });
27
+ $('#search').blur(function() {
28
+ this.value == '' && $('#search-label').show()
29
+ });
30
+
31
+ $('#search')[0].value == '' && $('#search-label').show();
32
+ }
33
+ $(function() {
34
+ placeholder();
35
+ $('#links').hide();
36
+ var panel = new Searchdoc.Panel($('#panel'), search_data, tree, top.frames[1]);
37
+ $('#search').focus();
38
+
39
+ var s = window.parent.location.search.match(/\?q=([^&]+)/);
40
+ if (s) {
41
+ s = decodeURIComponent(s[1]).replace(/\+/g, ' ');
42
+ if (s.length > 0)
43
+ {
44
+ $('#search').val(s);
45
+ panel.search(s, true);
46
+ }
47
+ }
48
+ })
49
+ </script>
50
+ </head>
51
+ <body>
52
+ <div class="panel panel_tree" id="panel">
53
+ <div class="header">
54
+ <div>
55
+ <label for="search" id="search-label" style="display: none">Search</label>
56
+ <table>
57
+ <tr><td>
58
+ <input type="Search" placeholder="Search" autosave="searchdoc" results="10" id="search" autocomplete="off"/>
59
+ </td></tr>
60
+ </table></div>
61
+ </div>
62
+ <div class="tree">
63
+ <ul>
64
+ </ul>
65
+ </div>
66
+ <div class="result">
67
+ <ul>
68
+ </ul>
69
+ </div>
70
+ </div>
71
+ <a href="links.html" id="links">index</a>
72
+ </body>
73
+ </html>
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>File index</head>
3
+ <body>
4
+ <% @files.each do |file| %>
5
+ <a href="../<%= file.path %>"><%= file.relative_name %></a>
6
+ <% end %>
7
+ </body>
8
+ </html>
data/mls.gemspec CHANGED
@@ -1,10 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  $:.push File.expand_path("../lib", __FILE__)
3
- require "mls/version"
4
3
 
5
4
  Gem::Specification.new do |s|
6
5
  s.name = "mls"
7
- s.version = MLS::VERSION
6
+ s.version = '0.2.3'
8
7
  s.authors = ["James R. Bracy"]
9
8
  s.email = ["james@42floors.com"]
10
9
  s.homepage = "http://mls.42floors.com"
@@ -24,9 +23,13 @@ Gem::Specification.new do |s|
24
23
  s.add_development_dependency 'bundler'
25
24
  s.add_development_dependency 'minitest'
26
25
  s.add_development_dependency 'turn'
26
+ s.add_development_dependency 'faker'
27
+ s.add_development_dependency 'factory_girl'
28
+ s.add_development_dependency 'fakeweb'
27
29
 
28
30
  # Runtime
29
- s.add_runtime_dependency 'activeresource'
31
+ s.add_runtime_dependency 'activesupport'
30
32
  s.add_runtime_dependency 'yajl-ruby'
31
- s.add_runtime_dependency 'simplecov'
33
+ s.add_runtime_dependency 'rest-client'
34
+
32
35
  end
@@ -0,0 +1,18 @@
1
+ FactoryGirl.define do
2
+ factory :account, :class => MLS::Account do
3
+ name { Faker::Name.name }
4
+ email { Faker::Internet.email }
5
+ password { 'test' }
6
+ password_confirmation 'test'
7
+ end
8
+
9
+ factory :broker, :class => MLS::Account, :parent => :account do
10
+ role 'broker'
11
+ end
12
+
13
+ factory :ghost_account, :class => MLS::Account do
14
+ name { Faker::Name.name }
15
+ email { Faker::Internet.email }
16
+ password_required { false }
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ FactoryGirl.define do
2
+ factory :address, :class => MLS::Address do
3
+ street_number { Kernel.rand(1000).to_s }
4
+ street { Faker::Address.street_name }
5
+ neighborhood { Faker::Name.name }
6
+ city { Faker::Address.city }
7
+ county { |p| p.city }
8
+ state { Faker::Address.state_abbr }
9
+ country 'US'
10
+ postal_code { Faker::Address.zip_code }
11
+ formatted_address { |p| "#{p.street_number} #{p.street}, #{p.city}, #{p.state}, #{p.postal_code}" }
12
+ latitude { Kernel.rand((-90.0)..(90.0)) }
13
+ longitude { Kernel.rand((-180.0)..(180.0)) }
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ FactoryGirl.define do
2
+ factory :listing, :class => MLS::Listing do
3
+ before(:create) { |l|
4
+ CACHE['auth_key'] = MLS.auth_key
5
+ MLS.auth_key = l.account.auth_key
6
+ }
7
+ after(:create) { |l|
8
+ MLS.auth_key = CACHE['auth_key']
9
+ }
10
+
11
+ association :account
12
+ association :address, :strategy => :build
13
+
14
+ #agents_attributes { {'0' => FactoryGirl.attributes_for(:broker)} }
15
+
16
+ use 'Office'
17
+ total_size { Kernel.rand(3000..900000) }
18
+ maximum_contiguous_size { Kernel.rand(3000..900000) }
19
+ minimum_divisable_size { Kernel.rand(3000..900000) }
20
+ kind 'lease'
21
+ #lease_type { ::MLS::Listing::LEASE_TYPES.sample }
22
+ space_type 'unit'
23
+ rate { rand(15..300) }
24
+ nnn_expenses { |l| l.lease_type == 'NNN' ? Kernel.rand(15..200) : nil }
25
+ available_on { Time.now + (20 + rand(0..360).to_i).days }
26
+ sublease_expiration { |l| l.sublease? ? (l.available_on + (30 + Kernel.rand(10..360)).days) : nil }
27
+ name { |l| l.kind == 'coworking' ? Faker::Name.name : nil }
28
+ end
29
+ end
30
+
@@ -0,0 +1,9 @@
1
+ FactoryGirl.define do
2
+ factory :tour_request, :class => MLS::TourRequest do
3
+ message { Faker::Lorem.paragraph }
4
+ company { Faker::Company.email }
5
+ population { Kernel.rand(2..200) }
6
+ funding { Faker::Lorem.sentance }
7
+ move_in_date '1234-12-21'
8
+ end
9
+ end
@@ -0,0 +1,68 @@
1
+ %PDF-1.7
2
+
3
+ 1 0 obj % entry point
4
+ <<
5
+ /Type /Catalog
6
+ /Pages 2 0 R
7
+ >>
8
+ endobj
9
+
10
+ 2 0 obj
11
+ <<
12
+ /Type /Pages
13
+ /MediaBox [ 0 0 200 200 ]
14
+ /Count 1
15
+ /Kids [ 3 0 R ]
16
+ >>
17
+ endobj
18
+
19
+ 3 0 obj
20
+ <<
21
+ /Type /Page
22
+ /Parent 2 0 R
23
+ /Resources <<
24
+ /Font <<
25
+ /F1 4 0 R
26
+ >>
27
+ >>
28
+ /Contents 5 0 R
29
+ >>
30
+ endobj
31
+
32
+ 4 0 obj
33
+ <<
34
+ /Type /Font
35
+ /Subtype /Type1
36
+ /BaseFont /Times-Roman
37
+ >>
38
+ endobj
39
+
40
+ 5 0 obj % page content
41
+ <<
42
+ /Length 44
43
+ >>
44
+ stream
45
+ BT
46
+ 70 50 TD
47
+ /F1 12 Tf
48
+ (Hello, world!) Tj
49
+ ET
50
+ endstream
51
+ endobj
52
+
53
+ xref
54
+ 0 6
55
+ 0000000000 65535 f
56
+ 0000000010 00000 n
57
+ 0000000079 00000 n
58
+ 0000000173 00000 n
59
+ 0000000301 00000 n
60
+ 0000000380 00000 n
61
+ trailer
62
+ <<
63
+ /Size 6
64
+ /Root 1 0 R
65
+ >>
66
+ startxref
67
+ 492
68
+ %%EOF
data/test/test_helper.rb CHANGED
@@ -1,7 +1,46 @@
1
- require 'simplecov'
2
- SimpleCov.start do
3
- add_filter "/test/"
4
- end
1
+ # require 'simplecov'
2
+ # SimpleCov.start do
3
+ # add_filter "/test/"
4
+ # use_merging true
5
+ # end
5
6
 
6
- require 'test/unit'
7
7
  require 'mls'
8
+ require 'turn'
9
+ require 'faker'
10
+ require 'test/unit'
11
+ require 'factory_girl'
12
+ require 'fakeweb'
13
+
14
+ CACHE = {}
15
+ MLS_HOST = ENV['MLS_URL'] || 'http://localhost:4000/api'
16
+ FactoryGirl.find_definitions
17
+
18
+ MLS.url = ENV["MLS_TEST_URL"] || 'http://LBJXFC%2BhDiRRCYj6kXtXREfgNXRCJa8ALvPn%2FIeyjSe2QsQyHZ%2F%2BWwN2VZM2cw%3D%3D@localhost:4000'#
19
+ # MLS.auth_key = MLS::Account.authenticate('jonbracy@gmail.com', 'test').auth_key
20
+
21
+ # File 'lib/active_support/testing/declarative.rb', somewhere in rails....
22
+ class ::Test::Unit::TestCase
23
+ def self.test(name, &block)
24
+ test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
25
+ defined = instance_method(test_name) rescue false
26
+ raise "#{test_name} is already defined in #{self}" if defined
27
+ if block_given?
28
+ define_method(test_name, &block)
29
+ else
30
+ define_method(test_name) do
31
+ flunk "No implementation provided for #{name}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def mock_response(method=:get, code='200', body='')
38
+ FakeWeb.register_uri(method, "http://mls.test/test", :status => [code, "Filler"], :body => body)
39
+ uri = URI.parse("http://mls.test/test")
40
+ case method
41
+ when :get
42
+ Net::HTTP.get_response(uri)
43
+ when :post
44
+ Net::HTTP.post_form(uri)
45
+ end
46
+ end
@@ -1,51 +1,28 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class TestAccount < ::Test::Unit::TestCase
4
-
5
- def test_properties
6
- account = MLS::Account.new
7
-
8
- assert account.respond_to?(:id)
9
- assert account.respond_to?(:role)
10
- assert account.respond_to?(:name)
11
- assert account.respond_to?(:email)
12
- assert account.respond_to?(:password)
13
- assert account.respond_to?(:password_confirmation)
14
- assert account.respond_to?(:perishable_token)
15
- assert account.respond_to?(:perishable_token_set_at)
16
- assert account.respond_to?(:phone)
17
- assert account.respond_to?(:company)
18
- assert account.respond_to?(:license)
19
- assert account.respond_to?(:linkedin)
20
- assert account.respond_to?(:twitter)
21
- assert account.respond_to?(:facebook)
22
- assert account.respond_to?(:web)
23
- assert account.respond_to?(:mls_number)
24
- assert account.respond_to?(:state)
25
- assert account.respond_to?(:country)
26
- assert account.respond_to?(:auth_key)
27
- assert account.respond_to?(:funding)
28
- assert account.respond_to?(:message)
29
- assert account.respond_to?(:population)
30
- assert account.respond_to?(:growing)
31
- assert account.respond_to?(:move_in)
32
- assert account.respond_to?(:extra_info)
4
+
5
+ def test_create_account
6
+ account = FactoryGirl.create(:account)
7
+ assert account.id
33
8
  end
34
-
35
- def test_methods
36
- account = MLS::Account.new
37
-
38
- assert account.respond_to?(:update!)
39
- assert account.respond_to?(:create!)
40
- assert account.respond_to?(:agent?)
41
- assert account.respond_to?(:favorites)
42
- assert account.respond_to?(:favorite)
9
+
10
+ def test_create_invalid_account
11
+ account = FactoryGirl.build(:account, :email => nil)
12
+ account.save
13
+ assert account.errors[:email]
14
+ end
15
+
16
+ def test_search_email
17
+ account = FactoryGirl.create(:account)
18
+ matches = MLS::Account.search(account.email)
19
+ assert_equal [account].map(&:id), matches.map(&:id)
43
20
  end
44
21
 
45
- def test_class_methods
46
- assert MLS::Account.respond_to?(:current)
47
- assert MLS::Account.respond_to?(:authenticate)
48
- assert MLS::Account.respond_to?(:reset_password!)
49
- assert MLS::Account.respond_to?(:update_password!)
22
+ def test_search_name
23
+ account = FactoryGirl.create(:account)
24
+ matches = MLS::Account.search(account.name)
25
+ assert_equal [account].map(&:id), matches.map(&:id)
50
26
  end
27
+
51
28
  end
@@ -0,0 +1,22 @@
1
+ require 'test_helper'
2
+
3
+ class TestFlyer < ::Test::Unit::TestCase
4
+
5
+
6
+ def test_create_flyer
7
+ flyer = MLS::Flyer.create(:file => File.new('test/fixtures/flyer.pdf'))
8
+ assert flyer.id
9
+ end
10
+
11
+ def test_attach_flyer_to_listing
12
+ listing = MLS::Listing.create
13
+ flyer = MLS::Flyer.create(
14
+ :file => File.new('test/fixtures/flyer.pdf'),
15
+ :subject => listing)
16
+
17
+ flyer = MLS::Flyer.find(flyer.id)
18
+ assert_equal listing.id, flyer.subject_id
19
+ assert_equal "Listing", flyer.subject_type
20
+ end
21
+
22
+ end
@@ -70,5 +70,124 @@ class TestListing < ::Test::Unit::TestCase
70
70
  def test_class_methods
71
71
  assert MLS::Listing.respond_to?(:find)
72
72
  end
73
+
74
+ test '#request_tour for email without an account' do
75
+ @listing = FactoryGirl.create(:listing)
76
+ @name = Faker::Name.name
77
+ @email = Faker::Internet.email
78
+ tr = @listing.request_tour(@name, @email)
79
+
80
+
81
+ assert_equal({}, tr.errors)
82
+ assert_equal({}, tr.account.errors)
83
+ # TODO assert_equal({}, tr.listing.errors)
84
+ assert tr.id
85
+ end
86
+
87
+ test '#request_tour for email on a ghost account' do
88
+ @account = FactoryGirl.create(:ghost_account)
89
+ @listing = FactoryGirl.create(:listing)
90
+
91
+ tr = @listing.request_tour(@account.name, @account.email)
92
+ assert_equal({}, tr.errors)
93
+ assert_equal({}, tr.account.errors)
94
+ # TODO assert_equal({}, tr.listing.errors)
95
+ assert tr.id
96
+ end
97
+
98
+ test '#request_tour for email on an account' do
99
+ @account = FactoryGirl.create(:account)
100
+ @listing = FactoryGirl.create(:listing)
101
+
102
+ tr = @listing.request_tour(@account.name, @account.email)
103
+ assert_equal({}, tr.errors)
104
+ assert_equal({}, tr.account.errors)
105
+ # TODO assert_equal({}, tr.listing.errors)
106
+ assert tr.id
107
+ end
108
+
109
+ test '#request_tour for an non-existant listing' do
110
+ @listing = FactoryGirl.build(:listing, :id => 94332)
111
+
112
+ assert_raises(MLS::Exception::NotFound) do
113
+ @listing.request_tour(Faker::Name.name, Faker::Internet.email)
114
+ end
115
+ end
116
+
117
+ test '#request_tour without and account name' do
118
+ @listing = FactoryGirl.create(:listing)
119
+
120
+ tr = @listing.request_tour('', Faker::Internet.email)
121
+ assert !tr.id
122
+ assert_equal({:name => ["can't be blank"]}, tr.account.errors)
123
+
124
+ tr = @listing.request_tour(nil, Faker::Internet.email)
125
+ assert !tr.id
126
+ assert_equal({:name => ["can't be blank"]}, tr.account.errors)
127
+ end
128
+
129
+ test '#request_tour without an account email' do
130
+ @listing = FactoryGirl.create(:listing)
131
+
132
+ tr = @listing.request_tour(Faker::Name.name, '')
133
+ assert !tr.id
134
+ assert_equal({:email => ["can't be blank", "is invalid"]}, tr.account.errors)
135
+
136
+ tr = @listing.request_tour(Faker::Name.name, nil)
137
+ assert !tr.id
138
+ # assert !tr.persisted? #TODO move to persisted being based of id?
139
+ assert_equal({:email => ["can't be blank", "is invalid"]}, tr.account.errors)
140
+ end
141
+
142
+ test '#request_tour with an account email' do
143
+ @account = FactoryGirl.create(:account)
144
+ @listing = FactoryGirl.create(:listing)
145
+
146
+ tr = @listing.request_tour('', @account.email) # TODO should this try to set the name of the account?
147
+ assert_equal({}, tr.errors)
148
+ assert_equal({}, tr.account.errors)
149
+ # TODO assert_equal({}, tr.listing.errors)
150
+ assert tr.id
151
+ assert tr.persisted?
152
+ end
153
+
154
+ test '#request_tour multiple times for a listing' do
155
+ @account = FactoryGirl.create(:account)
156
+ @listing = FactoryGirl.create(:listing)
157
+
158
+ tr1 = @listing.request_tour(@account.name, @account.email)
159
+ assert_equal({}, tr1.errors) # TODO should errors be here for account?
160
+ assert_equal({}, tr1.account.errors)
161
+ # TODO assert_equal({}, tr.listing.errors)
162
+ assert tr1.persisted?
163
+
164
+ tr2 = @listing.request_tour(@account.name, @account.email)
165
+ assert_equal({}, tr2.errors)
166
+ assert_equal({}, tr2.account.errors)
167
+ # TODO assert_equal({}, tr.listing.errors)
168
+ assert tr2.persisted?
169
+
170
+ assert_not_equal tr1.id, tr2.id
171
+ end
172
+
173
+ test '#request_tour with optional info' do
174
+ @listing = FactoryGirl.create(:listing)
175
+
176
+ info = {:company => '42Floors', :population => 10, :funding => 'string thing', :move_in_date => '2012-09-12'}
177
+ tr = @listing.request_tour(Faker::Name.name, Faker::Internet.email, info)
178
+
179
+ assert tr.id
180
+ assert_equal '42Floors', info[:company]
181
+ assert_equal 10, info[:population]
182
+ assert_equal 'string thing', info[:funding]
183
+ assert_equal '2012-09-12', info[:move_in_date]
184
+
185
+ tr = @listing.request_tour('', nil, info)
186
+ assert !tr.id
187
+ assert_equal '42Floors', info[:company]
188
+ assert_equal 10, info[:population]
189
+ assert_equal 'string thing', info[:funding]
190
+ assert_equal '2012-09-12', info[:move_in_date]
191
+ end
73
192
 
74
193
  end