mls 1.5.0 → 1.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9caa8aa44855ec88845d59e8732212c33e9ae86f
4
- data.tar.gz: e443bbd96ee37784f7bd360d185cafab43e4d0d4
3
+ metadata.gz: d679db7d9456ed2f5924a612fd51157607a5c360
4
+ data.tar.gz: 869b5a1f4d647eb414827322bc9904cb68220488
5
5
  SHA512:
6
- metadata.gz: c0b278d87cf5e782045105c1f8461a8feda501f74da7af7bc8cc33a14d1d38e614632073609b208f1f6aaf978f029a0680f11e6fa10cfc088374fb6640581ef5
7
- data.tar.gz: 19ebd1f220371778d1c74a5ebfde587bfea7c076f62fac80c912d6b966a34adc710ed459b557bf58bae3bb03466e6016c4fde716bee6ab8c21d423d22048e699
6
+ metadata.gz: ec8233dc1a01a4341d793e112bb67540796eb3d5ec90752704213fff4075755e8ad81e7c497bce35a23c47ca81fe0f4cb496f30a0a6150249bd08fc87a088608
7
+ data.tar.gz: ba2ae50b4822afac5febe29d8bcca90a6af7cd17e5a0d283f29f2ed8d717cbd8571674ae1e06554f0baca2a8b17ffa6304a613f1d6409666e1c65f0e8b18a3f3
data/lib/mls.rb CHANGED
@@ -1,4 +1,7 @@
1
+ require 'phony'
1
2
  require 'sunstone'
3
+ require 'arel/extensions'
4
+ require 'bcrypt'
2
5
 
3
6
  module MLS
4
7
 
@@ -98,8 +101,12 @@ module MLS::Slugger
98
101
  def find(*ids)
99
102
  friendly = -> (id) { id.respond_to?(:to_i) && id.to_i.to_s != id.to_s }
100
103
  return super if ids.size > 1 || !ids.all? { |x| friendly.call(x) }
101
-
102
- find_by_slug!(ids.first)
104
+
105
+ if ids.first.include?("@")
106
+ self.filter(email_addresses: {address: ids.first}).first
107
+ else
108
+ find_by_slug!(ids.first)
109
+ end
103
110
  end
104
111
 
105
112
  end
@@ -149,31 +156,6 @@ module MLS::Avatar
149
156
  belongs_to :avatar, :class_name => 'Image'
150
157
  end
151
158
 
152
- def avatar_url(options={})
153
- return nil unless avatar_hash_key
154
-
155
- options.reverse_merge!({
156
- :style => nil,
157
- :bg => nil,
158
- :protocol => 'https',
159
- :format => "jpg",
160
- :host => MLS.image_host
161
- });
162
-
163
- url_params = { s: options[:style], bg: options[:bg] }.select{ |k, v| v }
164
-
165
- if options[:protocol] == :relative # Protocol Relative
166
- result = '//'
167
- else options[:protocol]
168
- result = "#{options[:protocol]}://"
169
- end
170
-
171
- result += "#{options[:host]}/#{avatar_hash_key}.#{options[:format]}"
172
- result += "?#{url_params.to_param}" if url_params.size > 0
173
-
174
- result
175
- end
176
-
177
159
  end
178
160
 
179
161
  Dir.glob(File.join(File.dirname(__FILE__), 'mls', '*.rb'), &method(:require))
@@ -3,21 +3,29 @@ class Account < MLS::Model
3
3
  include MLS::Slugger
4
4
  include MLS::Avatar
5
5
 
6
- has_one :lead, foreign_key: :account_id
7
-
8
6
  belongs_to :organization
9
- belongs_to :green_sheet, :foreign_key => :green_sheet_uuid
7
+ belongs_to :membership
10
8
 
11
9
  has_many :tasks
12
10
  has_many :sources
13
- has_many :agencies, :inverse_of => :agent, :foreign_key => :agent_id
11
+ has_many :ownerships, :inverse_of => :account, :dependent => :delete_all
12
+ has_many :assets, through: :ownerships
13
+ has_many :coworking_spaces, through: :ownerships, source: :asset, source_type: 'CoworkingSpace'
14
+ has_many :listings, through: :ownerships, source: :asset, source_type: 'Listing', inverse_of: :accounts
15
+ has_many :email_digests
16
+ has_many :subscriptions, as: :subject
17
+ has_many :leads
14
18
 
15
- has_and_belongs_to_many :regions, :foreign_key => :agent_id
19
+ has_many :credit_cards
16
20
 
17
21
  has_many :email_addresses do
18
22
  def primary
19
23
  # For cases where the number is not primary we order
20
- order(:primary => :desc).first
24
+ if loaded?
25
+ select{|x| x.primary}.first
26
+ else
27
+ order(:primary => :desc).first
28
+ end
21
29
  end
22
30
  end
23
31
 
@@ -25,30 +33,126 @@ class Account < MLS::Model
25
33
 
26
34
  def primary
27
35
  # For cases where the number is not primary we order
28
- order(:primary => :desc).first
36
+ if loaded?
37
+ select{|x| x.primary}.first
38
+ else
39
+ order(:primary => :desc).first
40
+ end
29
41
  end
30
42
 
31
43
  end
32
44
 
45
+ has_and_belongs_to_many :advertised_regions, join_table: 'accounts_regions_advertised', class_name: 'Region'
46
+ has_and_belongs_to_many :inquiries
47
+ has_and_belongs_to_many :teams
48
+
49
+ attr_accessor :password, :password_required
50
+ accepts_nested_attributes_for :phones, :email_addresses
51
+
52
+ validates :password, confirmation: true, if: Proc.new {|a| (!a.persisted? && a.password_required?) || !a.password.nil? }
53
+ validates :password, length: { minimum: 6 }, if: :password
54
+ validates :password_confirmation, presence: true, if: :password
55
+
56
+ def regions
57
+ Region.filter(id: {in: self.advertised_region_ids})
58
+ end
59
+
60
+ def city_regions
61
+ advertised_regions.filter(type: Region::CITY_TYPES).order(listings_count: :desc)
62
+ end
63
+
64
+ def properties
65
+ Property.where(listings: {ownerships: {account_id: self.id}})
66
+ end
67
+
68
+ def tim_alerts?
69
+ self.membership&.subscriptions&.filter(started_at: true, status: {not: "closed"}, type: "tim_alerts", subject_id: self.id, subject_type: "Account")&.count.try(:>, 0)
70
+ end
71
+
72
+ def unlimited?
73
+ self.membership&.subscriptions&.filter(started_at: true, status: {not: "closed"}, type: "unlimited", subject_id: self.id, subject_type: "Account")&.count.try(:>, 0)
74
+ end
75
+
76
+ def paying?
77
+ self.membership&.subscriptions&.filter(started_at: true, status: {not: "closed"})&.count.try(:>, 0)
78
+ end
79
+
80
+ def password_required?
81
+ @password_required != false
82
+ end
83
+
84
+ def password=(pass)
85
+ @password = pass
86
+ self.password_digest = BCrypt::Password.create(pass)
87
+ end
88
+
33
89
  def email_address
34
- if email_addresses.loaded?
35
- email_addresses.to_a.find{|p| p.primary }.try(:address)
36
- else
37
- email_addresses.primary.try(:address)
90
+ email_addresses.to_a.find{|p| p.primary }.try(:address)
91
+ end
92
+
93
+ def regions_attributes=(regions_attrs)
94
+ # regions.clear was trying to destroy all regions going to "/accounts_regions/" method = Destroy
95
+ AccountsRegion.where(:agent_id => self.id).destroy_all
96
+ return if regions_attrs.nil?
97
+ regions_attrs.each do |attrs|
98
+ region = Region.find(attrs["id"])
99
+ regions.push(region)
38
100
  end
39
101
  end
40
102
 
41
103
  def phone
42
- if phones.loaded?
43
- phones.to_a.find{|p| p.primary }.try(:number)
44
- else
45
- phones.primary.try(:number)
46
- end
104
+ (phones.to_a.find{|p| p.primary } || phones.first).try(:number)
47
105
  end
48
106
 
107
+ def role?(*compare_roles)
108
+ (roles & compare_roles).any?
109
+ end
110
+ alias_method :roles?, :role?
111
+
49
112
  def company_name
50
113
  return organization.name if organization
51
114
  return company
52
115
  end
116
+
117
+ def merge_in(account_id)
118
+ req = Net::HTTP::Put.new("/accounts/#{self.id}/merge")
119
+ req.body = { account_id: account_id }.to_json
120
+ Account.connection.instance_variable_get(:@connection).send_request(req)
121
+ return true
122
+ rescue Sunstone::Exception::NotFound
123
+ return false
124
+ end
125
+
126
+ def self.send_reset_password_email(url, email_address)
127
+ req = Net::HTTP::Post.new("/accounts/password")
128
+ req.body = { email_address: email_address, url: url }.to_json
129
+ Account.connection.instance_variable_get(:@connection).send_request(req)
130
+ end
131
+
132
+ def self.update_password(token, password, password_confirmation)
133
+ req = Net::HTTP::Put.new("/accounts/password")
134
+ req.body = {
135
+ token: token,
136
+ password: password,
137
+ password_confirmation: password_confirmation
138
+ }.to_json
139
+ Account.connection.instance_variable_get(:@connection).send_request(req)
140
+ end
141
+
142
+ def self.confirm(token)
143
+ req = Net::HTTP::Post.new("/accounts/confirm")
144
+ req.body = { token: token }.to_json
145
+ Account.connection.instance_variable_get(:@connection).send_request(req)
146
+ return true
147
+ rescue Sunstone::Exception::NotFound
148
+ return false
149
+ end
150
+
151
+ def send_confirmation_email(url)
152
+ req = Net::HTTP::Post.new("/accounts/#{self.id}/confirm")
153
+ req.body = {url: url}.to_json
154
+ Account.connection.instance_variable_get(:@connection).send_request(req)
155
+ end
156
+
53
157
 
54
158
  end
@@ -0,0 +1,6 @@
1
+ class AccountsRegion < MLS::Model
2
+
3
+ belongs_to :account
4
+ belongs_to :region
5
+
6
+ end
@@ -0,0 +1,18 @@
1
+ class Action < MLS::Model
2
+ self.inheritance_column = nil
3
+
4
+ belongs_to :event
5
+ belongs_to :subject, :polymorphic => true
6
+
7
+ has_many :mistakes
8
+ has_many :metadata, foreign_key: :event_id, primary_key: :event_id
9
+
10
+ def self.by_performer(filter)
11
+ req = Net::HTTP::Get.new("/actions/by_performer")
12
+ req.body = {
13
+ where: filter
14
+ }.to_json
15
+ JSON.parse(connection.instance_variable_get(:@connection).send_request(req).body)
16
+ end
17
+
18
+ end
@@ -0,0 +1,5 @@
1
+ class ApiKey < MLS::Model
2
+
3
+ belongs_to :account
4
+
5
+ end
@@ -4,15 +4,29 @@ class CoworkingSpace < MLS::Model
4
4
 
5
5
  belongs_to :organization
6
6
  belongs_to :property
7
+ belongs_to :membership
7
8
  has_many :image_orderings, as: :subject
8
9
  has_many :photos, through: :image_orderings, source: :image
9
10
  has_many :spaces
10
11
  has_many :addresses, :through => :property
11
12
 
12
- def name
13
+ has_many :ownerships, as: :asset
14
+ has_many :accounts, through: :ownerships
15
+
16
+ accepts_nested_attributes_for :spaces, :ownerships, :accounts
17
+
18
+ def display_name
13
19
  output = organization.name
14
20
  output += " - " + read_attribute(:name) if read_attribute(:name)
15
21
  output
16
22
  end
23
+
24
+ def longitude
25
+ location.x
26
+ end
27
+
28
+ def latitude
29
+ location.y
30
+ end
17
31
 
18
- end
32
+ end
@@ -0,0 +1,11 @@
1
+ class CreditCard < MLS::Model
2
+
3
+ belongs_to :account
4
+
5
+ def name
6
+ string = "&bull;&bull;&bull;&bull; &bull;&bull;&bull;&bull;&bull; &bull;" if self.brand == "American Express"
7
+ string ||= "&bull;&bull;&bull;&bull; &bull;&bull;&bull;&bull; &bull;&bull;&bull;&bull; "
8
+ string += self.last4
9
+ end
10
+
11
+ end
@@ -0,0 +1,5 @@
1
+ class Datum < MLS::Model
2
+
3
+ belongs_to :subject, :polymorphic => true
4
+
5
+ end
@@ -1,5 +1,7 @@
1
1
  class Document < MLS::Model
2
2
 
3
+ has_many :image_orderings, foreign_key: :image_id
4
+
3
5
  def self.create(file)
4
6
  if doc = find_matching(file)
5
7
  doc
@@ -16,13 +18,17 @@ class Document < MLS::Model
16
18
  end
17
19
 
18
20
  def url(style=:original)
19
- MLS.config['document_host'] + path(style)
21
+ MLS.config['document_host'] + '/' + path(style)
20
22
  end
21
23
 
22
24
  def path(style=:original)
23
- "/documents/#{hash_key}/#{style}#{File.extname(filename)}"
25
+ "#{partition(style == :original ? hash_key : "#{hash_key}-#{style}")}"
24
26
  end
25
27
 
28
+ def partition(value)
29
+ split = value.scan(/.{1,4}/)
30
+ split.shift(4).join("/") + split.join("")
31
+ end
26
32
 
27
33
  def width
28
34
  return nil if !dimensions
@@ -59,30 +65,6 @@ class Document < MLS::Model
59
65
  end
60
66
 
61
67
  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
68
  end
87
69
 
88
70
  class PDF < Document
@@ -1,6 +1,7 @@
1
1
  class Email < MLS::Model
2
2
 
3
3
  belongs_to :source
4
+ has_many :tasks, :as => :subject, :inverse_of => :subject
4
5
  has_and_belongs_to_many :attachments, :class_name => 'Document'
5
6
 
6
7
  def from
@@ -12,7 +13,7 @@ class Email < MLS::Model
12
13
  end
13
14
 
14
15
  def name
15
- from
16
+ from_address || from
16
17
  end
17
18
 
18
19
  def to
@@ -3,6 +3,11 @@ class EmailAddress < MLS::Model
3
3
 
4
4
  TYPES = %w(work personal other)
5
5
 
6
+ has_many :emails
7
+
6
8
  belongs_to :account
7
9
 
10
+ def name
11
+ address
12
+ end
8
13
  end
@@ -0,0 +1,10 @@
1
+ class EmailDigest < MLS::Model
2
+
3
+ belongs_to :account
4
+ accepts_nested_attributes_for :account
5
+
6
+ def filter
7
+ (read_attribute(:filter) || {}).with_indifferent_access
8
+ end
9
+
10
+ end
@@ -4,17 +4,42 @@ class Event < MLS::Model
4
4
 
5
5
  belongs_to :account
6
6
  belongs_to :task
7
+ belongs_to :api_key
7
8
 
8
- has_many :event_actions
9
+ has_many :actions
9
10
 
10
11
  has_many :regards
11
12
 
12
- def actions
13
- event_actions.map(&:action)
14
- end
13
+ enum type: { create: 0, update: 1, delete: 2 }, _suffix: true
15
14
 
16
15
  def regarding
17
16
  regards.map(&:thing)
18
17
  end
19
18
 
20
- end
19
+ def encapsulate(&block)
20
+ Thread.current[:sunstone_headers] ||= {}
21
+ Thread.current[:sunstone_headers]['Event-Id'] = self.id
22
+ yield
23
+ self
24
+ ensure
25
+ Thread.current[:sunstone_headers].delete('Event-Id')
26
+ end
27
+
28
+ # Allow you to encapsulate all modification to be attached to a single event
29
+ #
30
+ # Event.encapsulate(source: '...', source_type: 'API') do
31
+ # ...
32
+ # end
33
+ #
34
+ # Returns the event is needed in the future
35
+ def self.encapsulate(options={}, &block)
36
+ event = Event.create!(options)
37
+ Thread.current[:sunstone_headers] ||= {}
38
+ Thread.current[:sunstone_headers]['Event-Id'] = event.id
39
+ yield
40
+ event
41
+ ensure
42
+ Thread.current[:sunstone_headers].try(:delete, 'Event-Id')
43
+ end
44
+
45
+ end