mls 1.4.0 → 1.4.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.
- checksums.yaml +4 -4
- data/lib/mls.rb +16 -40
- data/lib/mls/account.rb +33 -2
- data/lib/mls/agency.rb +1 -1
- data/lib/mls/change.rb +40 -0
- data/lib/mls/coworking_space.rb +12 -0
- data/lib/mls/document.rb +157 -0
- data/lib/mls/email.rb +41 -3
- data/lib/mls/email_address.rb +8 -0
- data/lib/mls/event.rb +20 -0
- data/lib/mls/event_action.rb +6 -0
- data/lib/mls/image_ordering.rb +6 -0
- data/lib/mls/inquiry.rb +5 -0
- data/lib/mls/lead.rb +4 -3
- data/lib/mls/listing.rb +9 -41
- data/lib/mls/mistake.rb +10 -0
- data/lib/mls/organization.rb +1 -0
- data/lib/mls/phone.rb +8 -0
- data/lib/mls/property.rb +42 -10
- data/lib/mls/{lead_listing.rb → recommendation.rb} +1 -3
- data/lib/mls/reference.rb +9 -0
- data/lib/mls/region.rb +43 -1
- data/lib/mls/session.rb +6 -6
- data/lib/mls/slug.rb +11 -0
- data/lib/mls/source.rb +15 -0
- data/lib/mls/space.rb +2 -0
- data/lib/mls/task.rb +52 -0
- data/lib/mls/time_log.rb +14 -0
- data/lib/mls/unit.rb +38 -0
- data/lib/mls/use.rb +1 -1
- data/lib/mls/vendor.rb +13 -0
- data/lib/mls/webpage.rb +7 -0
- data/mls.gemspec +1 -1
- metadata +21 -8
- data/lib/mls/floorplan.rb +0 -11
- data/lib/mls/flyer.rb +0 -11
- data/lib/mls/lead_property.rb +0 -10
- data/lib/mls/photo.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb0e3846ad2100b2167d386911bf6e4d504ffb47
|
4
|
+
data.tar.gz: f413e79ba06c41ccb8d105d88cae096c338374de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9d84713798402d9da3f892e09c036918432c965ffdd2030ccc35286033b0dec2022419d65e2ea049355c41c5aa6c816afce9e31ff3243f6b8891881b96fa5a0
|
7
|
+
data.tar.gz: ce4f8326d2255448f5513fffc31a8438bae12d7c68ff6e781831e523634a9bc51c871fb1ac95161471efc7742f0d5528cb7190a4b5486da42438ce76349d8307
|
data/lib/mls.rb
CHANGED
@@ -31,6 +31,13 @@ module MLS
|
|
31
31
|
Thread.current[:sunstone_cookie_store] = nil
|
32
32
|
end
|
33
33
|
|
34
|
+
def self.with_api_key(key, &block)
|
35
|
+
Thread.current[:sunstone_api_key] = key
|
36
|
+
yield
|
37
|
+
ensure
|
38
|
+
Thread.current[:sunstone_api_key] = nil
|
39
|
+
end
|
40
|
+
|
34
41
|
end
|
35
42
|
|
36
43
|
class MLS::Model < ActiveRecord::Base
|
@@ -43,14 +50,11 @@ module MLS::Slugger
|
|
43
50
|
|
44
51
|
module ClassMethods
|
45
52
|
|
46
|
-
def find(*
|
47
|
-
friendly = -> (
|
53
|
+
def find(*ids)
|
54
|
+
friendly = -> (id) { id.respond_to?(:to_i) && id.to_i.to_s != id.to_s }
|
55
|
+
return super if ids.size > 1 || !ids.all? { |x| friendly.call(x) }
|
48
56
|
|
49
|
-
|
50
|
-
find_by_slug!(args)
|
51
|
-
else
|
52
|
-
super
|
53
|
-
end
|
57
|
+
find_by_slug!(ids)
|
54
58
|
end
|
55
59
|
|
56
60
|
end
|
@@ -66,11 +70,11 @@ module MLS::Avatar
|
|
66
70
|
extend ActiveSupport::Concern
|
67
71
|
|
68
72
|
included do
|
69
|
-
belongs_to :avatar, :class_name => '
|
73
|
+
belongs_to :avatar, :class_name => 'Image'
|
70
74
|
end
|
71
75
|
|
72
76
|
def avatar_url(options={})
|
73
|
-
|
77
|
+
|
74
78
|
options.reverse_merge!({
|
75
79
|
:style => nil,
|
76
80
|
:bg => nil,
|
@@ -86,8 +90,8 @@ module MLS::Avatar
|
|
86
90
|
else options[:protocol]
|
87
91
|
result = "#{options[:protocol]}://"
|
88
92
|
end
|
89
|
-
|
90
|
-
result += "#{options[:host]}/#{
|
93
|
+
|
94
|
+
result += "#{options[:host]}/#{avatar_hash_key}.#{options[:format]}"
|
91
95
|
result += "?#{url_params.to_param}" if url_params.size > 0
|
92
96
|
|
93
97
|
result
|
@@ -95,32 +99,4 @@ module MLS::Avatar
|
|
95
99
|
|
96
100
|
end
|
97
101
|
|
98
|
-
|
99
|
-
require 'mls/account'
|
100
|
-
require 'mls/email'
|
101
|
-
require 'mls/organization'
|
102
|
-
require 'mls/property'
|
103
|
-
require 'mls/region'
|
104
|
-
require 'mls/listing'
|
105
|
-
require 'mls/space'
|
106
|
-
require 'mls/lead'
|
107
|
-
require 'mls/lead_listing'
|
108
|
-
require 'mls/lead_property'
|
109
|
-
require 'mls/address'
|
110
|
-
require 'mls/locality'
|
111
|
-
require 'mls/flyer'
|
112
|
-
require 'mls/inquiry'
|
113
|
-
require 'mls/agency'
|
114
|
-
require 'mls/session'
|
115
|
-
require 'mls/floorplan'
|
116
|
-
require 'mls/use'
|
117
|
-
require 'mls/comment'
|
118
|
-
|
119
|
-
# Models
|
120
|
-
# # Helpers
|
121
|
-
# class MLS
|
122
|
-
#
|
123
|
-
# def current_account
|
124
|
-
# end
|
125
|
-
#
|
126
|
-
# end
|
102
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'mls', '*.rb'), &method(:require))
|
data/lib/mls/account.rb
CHANGED
@@ -7,10 +7,41 @@ class Account < MLS::Model
|
|
7
7
|
|
8
8
|
belongs_to :organization
|
9
9
|
|
10
|
+
has_many :tasks
|
11
|
+
has_many :sources
|
10
12
|
has_many :agencies, :inverse_of => :agent, :foreign_key => :agent_id
|
11
|
-
|
13
|
+
|
14
|
+
has_and_belongs_to_many :regions, :foreign_key => :agent_id
|
15
|
+
|
16
|
+
has_many :email_addresses, :dependent => :destroy do
|
17
|
+
def primary
|
18
|
+
# For cases where the number is not primary we order
|
19
|
+
order(:primary => :desc).first
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
has_many :phones, dependent: :destroy do
|
24
|
+
|
12
25
|
def primary
|
13
|
-
where
|
26
|
+
# For cases where the number is not primary we order
|
27
|
+
order(:primary => :desc).first
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def email_address
|
33
|
+
if email_addresses.loaded?
|
34
|
+
email_addresses.to_a.find{|p| p.primary }.try(:address)
|
35
|
+
else
|
36
|
+
email_addresses.primary.try(:address)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def phone
|
41
|
+
if phones.loaded?
|
42
|
+
phones.to_a.find{|p| p.primary }.try(:number)
|
43
|
+
else
|
44
|
+
phones.primary.try(:number)
|
14
45
|
end
|
15
46
|
end
|
16
47
|
|
data/lib/mls/agency.rb
CHANGED
data/lib/mls/change.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
class Change < MLS::Model
|
2
|
+
self.inheritance_column = nil
|
3
|
+
|
4
|
+
belongs_to :subject, :polymorphic => true
|
5
|
+
has_many :event_actions, :as => :action
|
6
|
+
has_many :mistakes
|
7
|
+
|
8
|
+
# Returns the association instance for the given name, instantiating it if it doesn't already exist
|
9
|
+
def association(name) #:nodoc:
|
10
|
+
association = super
|
11
|
+
|
12
|
+
return association if name.to_s != 'subject'
|
13
|
+
|
14
|
+
association.instance_exec do
|
15
|
+
def klass
|
16
|
+
type = owner[reflection.foreign_type]
|
17
|
+
type.presence && type.constantize
|
18
|
+
rescue NameError
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def reader(force_reload=false)
|
23
|
+
if klass
|
24
|
+
if force_reload
|
25
|
+
klass.uncached { reload }
|
26
|
+
elsif !loaded? || stale_target?
|
27
|
+
reload
|
28
|
+
end
|
29
|
+
|
30
|
+
target
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
association
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/lib/mls/document.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
class Document < MLS::Model
|
2
|
+
|
3
|
+
def self.create(file)
|
4
|
+
if doc = find_matching(file)
|
5
|
+
doc
|
6
|
+
else
|
7
|
+
data, headers = Multipart::Post.prepare_query("document[file]" => file)
|
8
|
+
|
9
|
+
req = Net::HTTP::Post.new("/documents")
|
10
|
+
req.body = data
|
11
|
+
req['Content-Type'] = headers['Content-Type']
|
12
|
+
|
13
|
+
res = Document.connection.instance_variable_get(:@connection).send_request(req)
|
14
|
+
instantiate(JSON.parse(res.body).select{|k,v| column_names.include?(k.to_s) })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def url(style=:original)
|
19
|
+
MLS.config['document_host'] + path(style)
|
20
|
+
end
|
21
|
+
|
22
|
+
def path(style=:original)
|
23
|
+
"/documents/#{hash_key}/#{style}#{File.extname(filename)}"
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def width
|
28
|
+
return nil if !dimensions
|
29
|
+
return dimensions.split('x')[0].to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
def height
|
33
|
+
return nil if !dimensions
|
34
|
+
return dimensions.split('x')[1].to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def aspect_ratio
|
38
|
+
return nil if !width || !height
|
39
|
+
return width.to_f / height.to_f
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.find_matching(file)
|
43
|
+
filename = file.original_filename if file.respond_to?(:original_filename)
|
44
|
+
filename ||= File.basename(file.path)
|
45
|
+
|
46
|
+
# If we can tell the possible mime-type from the filename, use the
|
47
|
+
# first in the list; otherwise, use "application/octet-stream"
|
48
|
+
content_type = file.content_type if file.respond_to?(:content_type)
|
49
|
+
content_type ||= (MIME::Types.type_for(filename)[0] || MIME::Types["application/octet-stream"][0]).simplified
|
50
|
+
|
51
|
+
matching_docs = Document.where(:filename => filename, :content_type => content_type, :size => file.size)
|
52
|
+
if matching_docs.count > 0
|
53
|
+
matching_docs = matching_docs.where(:md5 => Digest::MD5.file(file.path).hexdigest)
|
54
|
+
end
|
55
|
+
|
56
|
+
matching_docs.first
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
class Image < Document
|
62
|
+
|
63
|
+
def url(options={})
|
64
|
+
options.reverse_merge!({
|
65
|
+
:style => nil,
|
66
|
+
:bg => nil,
|
67
|
+
:protocol => 'https',
|
68
|
+
:format => "jpg",
|
69
|
+
:host => MLS.image_host
|
70
|
+
});
|
71
|
+
|
72
|
+
url_params = {s: options[:style], bg: options[:bg]}.select{ |k, v| v }
|
73
|
+
|
74
|
+
if options[:protocol] == :relative # Protocol Relative
|
75
|
+
result = '//'
|
76
|
+
else options[:protocol]
|
77
|
+
result = "#{options[:protocol]}://"
|
78
|
+
end
|
79
|
+
|
80
|
+
result += "#{options[:host]}/#{hash_key}.#{options[:format]}"
|
81
|
+
result += "?#{url_params.to_param}" if url_params.size > 0
|
82
|
+
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
class PDF < Document
|
89
|
+
include MLS::Avatar
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
# Takes a hash of string and file parameters and returns a string of text
|
95
|
+
# formatted to be sent as a multipart form post.
|
96
|
+
module Multipart
|
97
|
+
# Formats a given hash as a multipart form post
|
98
|
+
# If a hash value responds to :string or :read messages, then it is
|
99
|
+
# interpreted as a file and processed accordingly; otherwise, it is assumed
|
100
|
+
# to be a string
|
101
|
+
class Post
|
102
|
+
|
103
|
+
def self.prepare_query(params)
|
104
|
+
boundary = "-----RubyMultipartClient~#{SecureRandom.hex(32)}"
|
105
|
+
|
106
|
+
parts = params.map do |k, v|
|
107
|
+
if v.respond_to?(:path) && v.respond_to?(:read)
|
108
|
+
FileParam.new(k, v)
|
109
|
+
else
|
110
|
+
StringParam.new(k, v)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
query = parts.map {|p| "--#{boundary}\r\n#{p.to_multipart}" }.join('') + "--#{boundary}--"
|
115
|
+
return query, { "Content-Type" => "multipart/form-data; boundary=#{boundary}" }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# Formats a basic string key/value pair for inclusion with a multipart post
|
122
|
+
class StringParam
|
123
|
+
attr_accessor :k, :v
|
124
|
+
|
125
|
+
def initialize(k, v)
|
126
|
+
@k = k
|
127
|
+
@v = v
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_multipart
|
131
|
+
return "Content-Disposition: form-data; name=\"#{@k}\"\r\n\r\n#{v}\r\n"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Formats the contents of a file or string for inclusion with a multipart
|
136
|
+
# form post
|
137
|
+
class FileParam
|
138
|
+
|
139
|
+
def initialize(k, file)
|
140
|
+
@k = k
|
141
|
+
@file = file
|
142
|
+
|
143
|
+
@filename = file.original_filename if file.respond_to?(:original_filename)
|
144
|
+
@filename ||= File.basename(file.path)
|
145
|
+
|
146
|
+
# If we can tell the possible mime-type from the filename, use the
|
147
|
+
# first in the list; otherwise, use "application/octet-stream"
|
148
|
+
@content_type = file.content_type if file.respond_to?(:content_type)
|
149
|
+
@content_type ||= (MIME::Types.type_for(@filename)[0] || MIME::Types["application/octet-stream"][0]).simplified
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_multipart
|
153
|
+
return "Content-Disposition: form-data; name=\"#{@k}\"; filename=\"#{ @filename }\"\r\n" +
|
154
|
+
"Content-Type: #{ @content_type }\r\n\r\n#{ @file.read }\r\n"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/lib/mls/email.rb
CHANGED
@@ -1,8 +1,46 @@
|
|
1
1
|
class Email < MLS::Model
|
2
|
-
|
2
|
+
|
3
|
+
belongs_to :source
|
4
|
+
has_and_belongs_to_many :attachments, :class_name => 'Document'
|
5
|
+
|
6
|
+
def from
|
7
|
+
if from_name
|
8
|
+
"\"#{from_name}\" <#{from_address}>"
|
9
|
+
else
|
10
|
+
from_address
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
from
|
16
|
+
end
|
17
|
+
|
18
|
+
def to
|
19
|
+
to_names.zip(to_addresses).map{|t| t[0] ? "\"#{t[0]}\" <#{t[1]}>" : t[1] }.join(', ')
|
20
|
+
end
|
21
|
+
|
22
|
+
def sender
|
23
|
+
headers['Sender']
|
24
|
+
end
|
25
|
+
|
26
|
+
def cc
|
27
|
+
cc_names.zip(cc_addresses).map{|t| t[0] ? "\"#{t[0]}\" <#{t[1]}>" : t[1] }.join(', ')
|
28
|
+
end
|
29
|
+
|
30
|
+
def bcc
|
31
|
+
bcc_names.zip(bcc_addresses).map{|t| t[0] ? "\"#{t[0]}\" <#{t[1]}>" : t[1] }.join(', ')
|
32
|
+
end
|
33
|
+
|
34
|
+
def reply_to
|
35
|
+
reply_to_names.zip(reply_to_addresses).map{|t| t[0] ? "\"#{t[0]}\" <#{t[1]}>" : t[1] }.join(', ')
|
36
|
+
end
|
3
37
|
|
4
|
-
|
38
|
+
def multipart?
|
39
|
+
body.keys.size > 1
|
40
|
+
end
|
5
41
|
|
6
|
-
|
42
|
+
def parts
|
43
|
+
body.keys
|
44
|
+
end
|
7
45
|
|
8
46
|
end
|
data/lib/mls/event.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class Event < MLS::Model
|
2
|
+
|
3
|
+
SOURCE_TYPES = %w(call email website)
|
4
|
+
|
5
|
+
belongs_to :account
|
6
|
+
belongs_to :task
|
7
|
+
|
8
|
+
has_many :event_actions, :dependent => :destroy
|
9
|
+
|
10
|
+
has_many :regards, :dependent => :destroy
|
11
|
+
|
12
|
+
def actions
|
13
|
+
event_actions.map(&:action)
|
14
|
+
end
|
15
|
+
|
16
|
+
def regarding
|
17
|
+
regards.map(&:thing)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/mls/inquiry.rb
CHANGED
data/lib/mls/lead.rb
CHANGED
data/lib/mls/listing.rb
CHANGED
@@ -2,11 +2,9 @@ class Listing < MLS::Model
|
|
2
2
|
self.inheritance_column = nil
|
3
3
|
|
4
4
|
include MLS::Slugger
|
5
|
-
include MLS::Avatar
|
6
5
|
|
7
|
-
LEASE_STATES = %w(listed leased)
|
8
6
|
SPACE_TYPES = %w(unit floor building)
|
9
|
-
TYPES = %w(Sale Lease Sublease
|
7
|
+
TYPES = %w(Sale Lease Sublease)
|
10
8
|
TERMS = ['Full Service', 'Net Lease', 'NN', 'NNN', 'Absolute NNN', 'Gross Lease', 'Modified Gross', 'Industrial Gross', 'Absolute Gross', 'Ground Lease', 'Other']
|
11
9
|
SALE_TERMS = ['Cash to Seller', 'Purchase Money Mtg.', 'Owner Financing', 'Build-to-Suit', 'Sale/Leaseback', 'Other']
|
12
10
|
RATE_UNITS = {
|
@@ -19,20 +17,19 @@ class Listing < MLS::Model
|
|
19
17
|
AMENITIES = %W(kitchen showers outdoor_space reception turnkey build_to_suit furniture
|
20
18
|
natural_light high_ceilings plug_and_play additional_storage storefront)
|
21
19
|
|
22
|
-
belongs_to :
|
23
|
-
belongs_to :
|
24
|
-
belongs_to :flyer
|
20
|
+
belongs_to :flyer, :class_name => 'Document'
|
21
|
+
belongs_to :unit
|
25
22
|
|
26
|
-
|
23
|
+
has_one :property, :through => :unit
|
27
24
|
|
28
25
|
has_many :photos, -> { order(:order => :asc) }, :as => :subject, :inverse_of => :subject
|
29
26
|
|
30
|
-
has_many :agencies, -> { order(:order) }
|
27
|
+
has_many :agencies, -> { order(:order) }
|
31
28
|
has_many :agents, -> { order('agencies.order') }, :through => :agencies, :source => :agent
|
32
29
|
|
33
30
|
has_one :address
|
34
31
|
has_many :addresses
|
35
|
-
|
32
|
+
|
36
33
|
# has_many :comments
|
37
34
|
# has_many :regions
|
38
35
|
# has_many :agents
|
@@ -42,16 +39,12 @@ class Listing < MLS::Model
|
|
42
39
|
# has_many :inquiries, :as => :subject, :inverse_of => :subject
|
43
40
|
# has_many :agencies, -> { order('"order"') }, :dependent => :destroy, :inverse_of => :subject, :as => :subject
|
44
41
|
# has_many :agents, -> { order('agencies.order') }, :through => :agencies, :inverse_of => :listings, :source => :agent
|
45
|
-
# has_many :email_proxies, :as => :subject, :inverse_of => :subject
|
46
42
|
# has_many :lead_listings, :dependent => :delete_all
|
47
43
|
|
48
|
-
has_and_belongs_to_many :uses
|
49
|
-
|
50
44
|
def contact
|
51
45
|
@contact ||= agents.first
|
52
46
|
end
|
53
|
-
|
54
|
-
|
47
|
+
|
55
48
|
def rate(units=nil)
|
56
49
|
return nil if !read_attribute(:rate)
|
57
50
|
units ||= rate_units
|
@@ -123,37 +116,12 @@ class Listing < MLS::Model
|
|
123
116
|
type == 'Sublease'
|
124
117
|
end
|
125
118
|
|
126
|
-
def coworking? # TODO: test me
|
127
|
-
type == 'CoworkingSpace'
|
128
|
-
end
|
129
|
-
|
130
119
|
def sale?
|
131
120
|
type == 'Sale'
|
132
121
|
end
|
133
122
|
|
134
123
|
def name
|
135
124
|
return read_attribute(:name) if read_attribute(:name)
|
136
|
-
|
137
|
-
case space_type
|
138
|
-
when 'unit'
|
139
|
-
if unit
|
140
|
-
"Unit #{unit}"
|
141
|
-
elsif floor
|
142
|
-
"#{floor.ordinalize} Floor Unit"
|
143
|
-
else
|
144
|
-
"Unit Lease"
|
145
|
-
end
|
146
|
-
when 'building'
|
147
|
-
"Entire Building"
|
148
|
-
when 'floor'
|
149
|
-
if floor
|
150
|
-
"#{floor.ordinalize} Floor"
|
151
|
-
elsif unit
|
152
|
-
"Unit #{unit}"
|
153
|
-
else
|
154
|
-
"Floor Lease"
|
155
|
-
end
|
156
|
-
end
|
125
|
+
unit.name
|
157
126
|
end
|
158
|
-
|
159
|
-
end
|
127
|
+
end
|
data/lib/mls/mistake.rb
ADDED
data/lib/mls/organization.rb
CHANGED
data/lib/mls/phone.rb
ADDED
data/lib/mls/property.rb
CHANGED
@@ -3,13 +3,12 @@ class Property < MLS::Model
|
|
3
3
|
include MLS::Slugger
|
4
4
|
include MLS::Avatar
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
has_many :listings
|
6
|
+
has_many :units
|
7
|
+
has_many :references, as: :subject
|
8
|
+
has_many :listings, :through => :units
|
9
9
|
has_many :localities
|
10
10
|
has_many :regions, :through => :localities
|
11
|
-
has_many :
|
12
|
-
has_many :internal_photos, -> { order(:order => :asc) }, :as => :subject, :inverse_of => :subject
|
11
|
+
has_many :image_orderings, as: :subject
|
13
12
|
|
14
13
|
has_many :addresses do
|
15
14
|
def primary
|
@@ -17,11 +16,44 @@ class Property < MLS::Model
|
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
def photos
|
20
|
+
image_orderings.sort_by(&:order).map(&:image)
|
21
|
+
end
|
22
|
+
|
23
|
+
def contact
|
24
|
+
@contact ||= listings.where(leased_at: nil, authorized: true, type: ['Lease', 'Sublease'])
|
25
|
+
.order(size: :desc).first.try(:contact)
|
25
26
|
end
|
26
27
|
|
28
|
+
def address
|
29
|
+
addresses.find(&:primary)
|
30
|
+
end
|
31
|
+
|
32
|
+
def neighborhood_region
|
33
|
+
params = {:query => address.neighborhood} if address.try(:neighborhood)
|
34
|
+
params ||= {:query => neighborhood} if neighborhood
|
35
|
+
params ||= {:type => "Neighborhood"}
|
36
|
+
fetch_region(params)
|
37
|
+
end
|
38
|
+
|
39
|
+
def city_region
|
40
|
+
fetch_region(:type => "City")
|
41
|
+
end
|
42
|
+
|
43
|
+
def market
|
44
|
+
fetch_region(:is_market => true)
|
45
|
+
end
|
46
|
+
|
47
|
+
def target_region
|
48
|
+
fetch_region(:target => true)
|
49
|
+
end
|
50
|
+
|
51
|
+
def fetch_region(params)
|
52
|
+
if regions.loaded?
|
53
|
+
params = params.map{|k,v| [k, v]}
|
54
|
+
regions.to_a.find{|r| r[params[0][0]] == params[0][1]}
|
55
|
+
else
|
56
|
+
regions.where(params).first
|
57
|
+
end
|
58
|
+
end
|
27
59
|
end
|
data/lib/mls/region.rb
CHANGED
@@ -4,10 +4,52 @@ class Region < MLS::Model
|
|
4
4
|
|
5
5
|
self.inheritance_column = nil
|
6
6
|
|
7
|
-
belongs_to :cover_photo, :class_name => '
|
7
|
+
belongs_to :cover_photo, :class_name => 'Image'
|
8
|
+
belongs_to :market, :class_name => 'Region'
|
8
9
|
|
9
10
|
has_and_belongs_to_many :parents, :join_table => 'regions_regions', :class_name => 'Region', :foreign_key => 'child_id', :association_foreign_key => 'parent_id'
|
10
11
|
has_and_belongs_to_many :children, :join_table => 'regions_regions', :class_name => 'Region', :foreign_key => 'parent_id', :association_foreign_key => 'child_id'
|
11
12
|
|
13
|
+
def name
|
14
|
+
if common_name.try(:[], 'eng')
|
15
|
+
common_name['eng'].is_a?(Array) ? common_name['eng'].first : common_name['eng']
|
16
|
+
else
|
17
|
+
official_name['eng'].is_a?(Array) ? official_name['eng'].first : official_name['eng']
|
18
|
+
end
|
19
|
+
end
|
12
20
|
|
21
|
+
def target
|
22
|
+
return @target if @target
|
23
|
+
@target = children.where(:target => true).first
|
24
|
+
@target ||= market.try(:target)
|
25
|
+
@target ||= market
|
26
|
+
@target ||= self
|
27
|
+
|
28
|
+
@target
|
29
|
+
end
|
30
|
+
|
31
|
+
def cover_photo_url(options={})
|
32
|
+
|
33
|
+
options.reverse_merge!({
|
34
|
+
:style => nil,
|
35
|
+
:bg => nil,
|
36
|
+
:protocol => 'https',
|
37
|
+
:format => "jpg",
|
38
|
+
:host => MLS.image_host
|
39
|
+
});
|
40
|
+
|
41
|
+
url_params = { s: options[:style], bg: options[:bg] }.select{ |k, v| v }
|
42
|
+
|
43
|
+
if options[:protocol] == :relative # Protocol Relative
|
44
|
+
result = '//'
|
45
|
+
else options[:protocol]
|
46
|
+
result = "#{options[:protocol]}://"
|
47
|
+
end
|
48
|
+
|
49
|
+
result += "#{options[:host]}/#{cover_photo_digest}.#{options[:format]}"
|
50
|
+
result += "?#{url_params.to_param}" if url_params.size > 0
|
51
|
+
|
52
|
+
result
|
53
|
+
end
|
54
|
+
|
13
55
|
end
|
data/lib/mls/session.rb
CHANGED
@@ -2,15 +2,15 @@ class Session < MLS::Model
|
|
2
2
|
|
3
3
|
belongs_to :account
|
4
4
|
|
5
|
-
# Authenticate with
|
5
|
+
# Authenticate with email_address and password.
|
6
6
|
# Returns either the newly created session or nil
|
7
|
-
def self.authenticate(
|
8
|
-
if
|
9
|
-
password =
|
10
|
-
|
7
|
+
def self.authenticate(email_address, password=nil)
|
8
|
+
if email_address.is_a? Hash
|
9
|
+
password = email_address[:password]
|
10
|
+
email_address = email_address[:email_address]
|
11
11
|
end
|
12
12
|
|
13
|
-
Session.create(:
|
13
|
+
Session.create(:email_address => email_address, :password => password)
|
14
14
|
rescue Sunstone::Exception::Unauthorized
|
15
15
|
nil
|
16
16
|
end
|
data/lib/mls/slug.rb
ADDED
data/lib/mls/source.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class Source < MLS::Model
|
2
|
+
|
3
|
+
has_many :tasks, :as => :subject
|
4
|
+
has_many :emails
|
5
|
+
|
6
|
+
belongs_to :account
|
7
|
+
belongs_to :organization
|
8
|
+
belongs_to :upload, class_name: 'Flyer'
|
9
|
+
|
10
|
+
def name
|
11
|
+
return email_address if email_address.present?
|
12
|
+
url.match(/^(?:https?:\/\/)?(?:www\.)?([^\/]+)/)[1]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/lib/mls/space.rb
CHANGED
data/lib/mls/task.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
class Task < MLS::Model
|
2
|
+
self.inheritance_column = nil
|
3
|
+
|
4
|
+
belongs_to :subject, :polymorphic => true
|
5
|
+
belongs_to :account
|
6
|
+
|
7
|
+
has_many :events
|
8
|
+
has_many :mistakes
|
9
|
+
has_many :time_logs
|
10
|
+
has_many :reviews, -> { where(:type => "review") }, :class_name => "Task", :as => :subject, :inverse_of => :subject
|
11
|
+
has_many :fixes, -> { where(:type => "fix") }, :class_name => "Task", :as => :subject, :inverse_of => :subject
|
12
|
+
|
13
|
+
def for_source?
|
14
|
+
subject_type == "Source"
|
15
|
+
end
|
16
|
+
|
17
|
+
def for_task?
|
18
|
+
subject_type == "Taks"
|
19
|
+
end
|
20
|
+
|
21
|
+
def review?
|
22
|
+
type == "review"
|
23
|
+
end
|
24
|
+
|
25
|
+
def fix?
|
26
|
+
type == "fix"
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse?
|
30
|
+
type == "parse"
|
31
|
+
end
|
32
|
+
|
33
|
+
def duration
|
34
|
+
time_logs.where(:started_at => true, :stopped_at => true).sum("duration")
|
35
|
+
end
|
36
|
+
|
37
|
+
def pause
|
38
|
+
log = time_logs.where(:started_at => true, :stopped_at => false).first
|
39
|
+
if log
|
40
|
+
log.update(:stopped_at => Time.now)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def resume
|
45
|
+
time_logs << TimeLog.create(:started_at => Time.now)
|
46
|
+
end
|
47
|
+
|
48
|
+
def paused?
|
49
|
+
!started_at.nil? && completed_at.nil? && time_logs.where(:started_at => true, :stopped_at => false).length == 0
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/mls/time_log.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class TimeLog < MLS::Model
|
2
|
+
|
3
|
+
belongs_to :task
|
4
|
+
belongs_to :account
|
5
|
+
|
6
|
+
scope :completed, -> { where('started_at IS NOT NULL AND stopped_at IS NOT NULL') }
|
7
|
+
scope :incompleted, -> { where('started_at IS NOT NULL AND stopped_at IS NULL') }
|
8
|
+
|
9
|
+
def duration
|
10
|
+
return nil if !(started_at && stopped_at)
|
11
|
+
stopped_at - started_at
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
data/lib/mls/unit.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
class Unit < MLS::Model
|
2
|
+
self.inheritance_column = nil
|
3
|
+
|
4
|
+
include MLS::Slugger
|
5
|
+
include MLS::Avatar
|
6
|
+
|
7
|
+
belongs_to :property
|
8
|
+
belongs_to :floorplan, :class_name => 'Document'
|
9
|
+
|
10
|
+
has_many :listings
|
11
|
+
has_many :image_orderings, as: :subject, dependent: :destroy
|
12
|
+
has_many :photos, through: :image_orderings, source: :image
|
13
|
+
# has_many :photos, -> { order(:order => :asc) }, :as => :subject, :inverse_of => :subject
|
14
|
+
|
15
|
+
has_and_belongs_to_many :uses
|
16
|
+
|
17
|
+
def tags
|
18
|
+
read_attribute(:tags) || []
|
19
|
+
end
|
20
|
+
|
21
|
+
def name
|
22
|
+
name = ""
|
23
|
+
case self.type
|
24
|
+
when 'unit'
|
25
|
+
name += "Unit"
|
26
|
+
name += " #{self.number}" if self.number
|
27
|
+
name += " (Floor #{self.floor})" if self.floor
|
28
|
+
when 'floor'
|
29
|
+
name += "Floor"
|
30
|
+
name += " #{self.floor}" if self.floor
|
31
|
+
name += " (Unit #{self.number})" if self.number
|
32
|
+
when 'building'
|
33
|
+
"Entire Building"
|
34
|
+
end
|
35
|
+
name
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/mls/use.rb
CHANGED
data/lib/mls/vendor.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
class Vendor < MLS::Model
|
2
|
+
self.inheritance_column = nil
|
3
|
+
|
4
|
+
include MLS::Slugger
|
5
|
+
include MLS::Avatar
|
6
|
+
|
7
|
+
has_and_belongs_to_many :regions
|
8
|
+
belongs_to :account
|
9
|
+
|
10
|
+
has_many :image_orderings, as: :subject, dependent: :destroy
|
11
|
+
has_many :photos, through: :image_orderings, source: :image
|
12
|
+
|
13
|
+
end
|
data/lib/mls/webpage.rb
ADDED
data/mls.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "mls"
|
6
|
-
s.version = '1.4.
|
6
|
+
s.version = '1.4.3'
|
7
7
|
s.authors = ["Jon Bracy", "James R. Bracy"]
|
8
8
|
s.email = ["jon@42floors.com", "james@42floors.com"]
|
9
9
|
s.homepage = "http://mls.42floors.com"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mls
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Bracy
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-06-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -195,23 +195,36 @@ files:
|
|
195
195
|
- lib/mls/account.rb
|
196
196
|
- lib/mls/address.rb
|
197
197
|
- lib/mls/agency.rb
|
198
|
+
- lib/mls/change.rb
|
198
199
|
- lib/mls/comment.rb
|
200
|
+
- lib/mls/coworking_space.rb
|
201
|
+
- lib/mls/document.rb
|
199
202
|
- lib/mls/email.rb
|
200
|
-
- lib/mls/
|
201
|
-
- lib/mls/
|
203
|
+
- lib/mls/email_address.rb
|
204
|
+
- lib/mls/event.rb
|
205
|
+
- lib/mls/event_action.rb
|
206
|
+
- lib/mls/image_ordering.rb
|
202
207
|
- lib/mls/inquiry.rb
|
203
208
|
- lib/mls/lead.rb
|
204
|
-
- lib/mls/lead_listing.rb
|
205
|
-
- lib/mls/lead_property.rb
|
206
209
|
- lib/mls/listing.rb
|
207
210
|
- lib/mls/locality.rb
|
211
|
+
- lib/mls/mistake.rb
|
208
212
|
- lib/mls/organization.rb
|
209
|
-
- lib/mls/
|
213
|
+
- lib/mls/phone.rb
|
210
214
|
- lib/mls/property.rb
|
215
|
+
- lib/mls/recommendation.rb
|
216
|
+
- lib/mls/reference.rb
|
211
217
|
- lib/mls/region.rb
|
212
218
|
- lib/mls/session.rb
|
219
|
+
- lib/mls/slug.rb
|
220
|
+
- lib/mls/source.rb
|
213
221
|
- lib/mls/space.rb
|
222
|
+
- lib/mls/task.rb
|
223
|
+
- lib/mls/time_log.rb
|
224
|
+
- lib/mls/unit.rb
|
214
225
|
- lib/mls/use.rb
|
226
|
+
- lib/mls/vendor.rb
|
227
|
+
- lib/mls/webpage.rb
|
215
228
|
- mls.gemspec
|
216
229
|
- test/fixtures/flyer.pdf
|
217
230
|
- test/mls/attribute_test.rb
|
@@ -237,7 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
237
250
|
version: '0'
|
238
251
|
requirements: []
|
239
252
|
rubyforge_project: mls
|
240
|
-
rubygems_version: 2.
|
253
|
+
rubygems_version: 2.4.5
|
241
254
|
signing_key:
|
242
255
|
specification_version: 4
|
243
256
|
summary: 42Floors MLS Client
|
data/lib/mls/floorplan.rb
DELETED
data/lib/mls/flyer.rb
DELETED
data/lib/mls/lead_property.rb
DELETED
data/lib/mls/photo.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
class Photo < MLS::Model
|
2
|
-
|
3
|
-
belongs_to :subject, :polymorphic => true, :inverse_of => :photos
|
4
|
-
|
5
|
-
def url(options={})
|
6
|
-
options.reverse_merge!({
|
7
|
-
:style => nil,
|
8
|
-
:bg => nil,
|
9
|
-
:protocol => 'https',
|
10
|
-
:format => "jpg",
|
11
|
-
:host => MLS.image_host
|
12
|
-
});
|
13
|
-
|
14
|
-
url_params = {s: options[:style], bg: options[:bg]}.select{ |k, v| v }
|
15
|
-
|
16
|
-
if options[:protocol] == :relative # Protocol Relative
|
17
|
-
result = '//'
|
18
|
-
else options[:protocol]
|
19
|
-
result = "#{options[:protocol]}://"
|
20
|
-
end
|
21
|
-
|
22
|
-
result += "#{options[:host]}/#{digest}.#{options[:format]}"
|
23
|
-
result += "?#{url_params.to_param}" if url_params.size > 0
|
24
|
-
|
25
|
-
result
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
class InternalPhoto < Photo; end
|