Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6316853

Browse files
committedAug 26, 2014
[WIP#335] Update old digest email to send monthly popular protips
Generated a protips mailer WIP: query for popular protips directly and bypass Tire and our search implementation Added mail_view WIP: wiring up the popular protips WIP: cleaning up the old weekly digest so I can merge the styles and structure WIP: formatting a popular protip email Re-re-add MailView Pretty close to complete on the popular protips email
1 parent cf49296 commit 6316853

File tree

24 files changed

+565
-187
lines changed

24 files changed

+565
-187
lines changed
 

‎Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ gem 'awesome_print'
9797
gem 'faraday', '~> 0.8.1'
9898
gem 'metamagic'
9999

100+
gem "mail_view", "~> 2.0.4"
101+
100102
# ----------------
101103

102104

‎Gemfile.lock

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,3 @@
1-
GIT
2-
remote: git://github.com/emberjs/ember-rails.git
3-
revision: 5e5a398f3c67c3a3b84b7513b93b22bf81055cc9
4-
specs:
5-
ember-rails (0.15.0)
6-
active_model_serializers
7-
barber (>= 0.4.1)
8-
ember-data-source (>= 1.0.0.beta.5)
9-
ember-source (>= 1.1.0)
10-
execjs (>= 1.2)
11-
handlebars-source (> 1.0.0)
12-
jquery-rails (>= 1.0.17)
13-
railties (>= 3.1)
14-
151
GIT
162
remote: git://github.com/nixme/jazz_hands.git
173
revision: 5e4b48f145883ecb14b55bf04eacc28ac9662676
@@ -38,6 +24,20 @@ GIT
3824
mime-types (>= 1.25, < 3.0)
3925
rest-client (~> 1.4)
4026

27+
GIT
28+
remote: git://github.com/emberjs/ember-rails.git
29+
revision: 5e5a398f3c67c3a3b84b7513b93b22bf81055cc9
30+
specs:
31+
ember-rails (0.15.0)
32+
active_model_serializers
33+
barber (>= 0.4.1)
34+
ember-data-source (>= 1.0.0.beta.5)
35+
ember-source (>= 1.1.0)
36+
execjs (>= 1.2)
37+
handlebars-source (> 1.0.0)
38+
jquery-rails (>= 1.0.17)
39+
railties (>= 3.1)
40+
4141
GEM
4242
remote: https://rubygems.org/
4343
remote: https://rails-assets.org/
Loading

‎app/helpers/protips_helper.rb

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -294,36 +294,35 @@ def user_upvoted?(protip)
294294
current_user && current_user_upvotes.include?(protip.public_id)
295295
end
296296

297-
def protip_stat_class(protip)
298-
class_name = best_stat_name(protip)
299-
#class_name << " " << (user_upvoted?(protip) ? "upvoted" : "")
300-
end
301-
302297
def formatted_best_stat_value(protip)
303-
value =
304-
case best_stat_name(protip).to_sym
305-
when :views
306-
views_stat_value(protip)
307-
else
308-
best_stat_value(protip)
309-
end
310-
number_to_human(value, units: {unit: "", thousand: "k"})
298+
value = case best_stat_name(protip).to_sym
299+
when :views
300+
views_stat_value(protip)
301+
else
302+
best_stat_value(protip)
303+
end
304+
305+
number_to_human(value, units: { unit: '', thousand: 'k' })
311306
end
312307

313-
def blur_protips?
314-
params[:show_all].nil? && !signed_in?
308+
def best_stat_name(protip)
309+
protip.best_stat.is_a?(Tire::Results::Item) ? protip.best_stat.name : protip.best_stat[0]
315310
end
316311

317-
def followings_fragment_cache_key(user_id)
318-
['v1', 'followings_panel', user_id]
312+
def views_stat_value(protip)
313+
best_stat_value(protip) * Protip::COUNTABLE_VIEWS_CHUNK
319314
end
320315

321316
def best_stat_value(protip)
322317
protip.best_stat.is_a?(Tire::Results::Item) ? protip.best_stat.value.to_i : protip.best_stat[1]
323318
end
324319

325-
def best_stat_name(protip)
326-
protip.best_stat.is_a?(Tire::Results::Item) ? protip.best_stat.name : protip.best_stat[0]
320+
def blur_protips?
321+
params[:show_all].nil? && !signed_in?
322+
end
323+
324+
def followings_fragment_cache_key(user_id)
325+
['v1', 'followings_panel', user_id]
327326
end
328327

329328
def protip_networks(protip)
@@ -354,10 +353,6 @@ def protip_display_mode
354353
mobile_device? ? "fullpage" : "popup"
355354
end
356355

357-
def views_stat_value(protip)
358-
best_stat_value(protip) * Protip::COUNTABLE_VIEWS_CHUNK
359-
end
360-
361356
def display_protip_stats?(protip)
362357
stat_name = best_stat_name(protip)
363358
# if stat is present, and the stat we're displaying is views over 50, display.

‎app/helpers/users_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def users_image_tag(user, options = {})
3030

3131
#TODO Remove
3232
def users_image_path(user)
33+
return ''
3334
user.avatar.url
3435
end
3536

‎app/mailers/mail_preview.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class MailPreview < MailView
2+
USERNAME = 'just3ws'
3+
4+
def popular_protips
5+
from = 60.days.ago
6+
to = 0.days.ago
7+
user = User.with_username(USERNAME)
8+
protips = ProtipMailer::Queries.popular_protips(from, to)
9+
ProtipMailer.popular_protips(user, protips, from, to).deliver
10+
end
11+
12+
def old_weekly_digest
13+
WeeklyDigestMailer.weekly_digest(USERNAME)
14+
end
15+
end

‎app/mailers/protip_mailer.rb

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
class ProtipMailer < ActionMailer::Base
2+
include ActionView::Helpers::TextHelper
3+
4+
add_template_helper(UsersHelper)
5+
add_template_helper(ProtipsHelper)
6+
add_template_helper(ApplicationHelper)
7+
8+
default_url_options[:host] = 'coderwall.com'
9+
default_url_options[:only_path] = false
10+
default from: '"Coderwall" <support@coderwall.com>'
11+
12+
SPAM_NOTICE = "You're receiving this email because you signed up for Coderwall. We hate spam and make an effort to keep notifications to a minimum. To change your notification preferences, you can update your email settings here: http://coderwall.com/settings#email or immediately unsubscribe by clicking this link %unsubscribe_url%"
13+
STARS = {
14+
protip_upvotes: 'pro tip upvotes',
15+
followers: 'followers',
16+
endorsements: 'endorsements',
17+
protips_count: 'protips'
18+
}
19+
20+
#################################################################################
21+
def popular_protips(user, protips, from, to)
22+
fail "Protips are required." if protips.nil? || protips.empty?
23+
headers['X-Mailgun-Campaign-Id'] = 'coderwall-popular_protips'
24+
25+
@user = user
26+
@protips = protips
27+
@team, @job = get_team_and_job_for(@user)
28+
@issue = campaign_params
29+
30+
stars = @user.following_users.where('last_request_at > ?', 1.month.ago)
31+
@star_stat = star_stat_for_this_week
32+
@star_stat_string = STARS[@star_stat]
33+
34+
@most = star_stats(stars).sort_by { |star| -star[@star_stat] }.first
35+
@most = nil if @most && (@most[@star_stat] <= 0)
36+
37+
mail to: 'mike@just3ws.com', subject: 'Popular Protips on Coderwall'
38+
end
39+
#################################################################################
40+
41+
def campaign_params
42+
{
43+
utm_campaign: 'coderwall-popular_protips',
44+
utm_content: Date.today.midnight,
45+
utm_medium: 'email'
46+
}
47+
end
48+
49+
def star_stat_for_this_week
50+
STARS.keys[week_of_the_month % 4]
51+
end
52+
53+
def star_stats(stars, since=1.week.ago)
54+
stars.collect { |star| star.activity_stats(since, true) }.each_with_index.map { |stat, index| stat.merge(user: stars[index]) }
55+
end
56+
57+
def week_of_the_month
58+
Date.today.cweek - Date.today.at_beginning_of_month.cweek
59+
end
60+
61+
def get_team_and_job_for(user)
62+
if user.team.try(:hiring?)
63+
[user.team, user.team.jobs.sample]
64+
else
65+
teams = teams_for_user(user)
66+
teams.each do |team|
67+
best_job = team.best_positions_for(user).detect do |job|
68+
(job.team_document_id == user.team_document_id) || !already_sent?(job, user)
69+
end
70+
return [team, best_job] unless best_job.nil?
71+
end
72+
end
73+
[nil, nil]
74+
end
75+
76+
def teams_for_user(user)
77+
Team.most_relevant_featured_for(user).select do |team|
78+
team.hiring?
79+
end
80+
end
81+
82+
def already_sent?(mailable, user)
83+
SentMail.where(user_id: user.id, mailable_id: mailable.id, mailable_type: mailable.class.name).exists?
84+
end
85+
86+
module Queries
87+
def self.popular_protips(from, to)
88+
search_results = ProtipMailer::Queries.search_for_popular_protips(from, to)
89+
public_ids = search_results.map { |protip| protip['public_id'] }
90+
91+
Protip.eager_load(:user, :comments).where("public_id in (?)", public_ids)
92+
end
93+
94+
def self.search_for_popular_protips(from, to, max_results=10)
95+
url = "#{ENV['ELASTICSEARCH_URL']}/#{ENV['ELASTICSEARCH_PROTIPS_INDEX']}/_search"
96+
query = {
97+
'query' => {
98+
'bool' => {
99+
'must' => [
100+
{
101+
'range' => {
102+
'protip.created_at' => {
103+
'from' => from.strftime('%Y-%m-%d'),
104+
'to' => to.strftime('%Y-%m-%d')
105+
}
106+
}
107+
}
108+
]
109+
}
110+
},
111+
'size' => max_results,
112+
'sort' => [
113+
{
114+
'protip.popular_score' => {
115+
'order' => 'desc'
116+
}
117+
}
118+
]
119+
}
120+
response = RestClient.post(url, MultiJson.dump(query), content_type: :json, accept: :json)
121+
# TODO: check for response code
122+
MultiJson.load(response.body)['hits']['hits'].map { |protip| protip['_source'] }
123+
end
124+
end
125+
end

‎app/mailers/weekly_digest_mailer.rb

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
class WeeklyDigestMailer < ActionMailer::Base
44
include ActionView::Helpers::TextHelper
55
include ActiveSupport::Benchmarkable
6+
67
add_template_helper(UsersHelper)
78
add_template_helper(ProtipsHelper)
89
add_template_helper(ApplicationHelper)
@@ -17,54 +18,52 @@ def self.queue
1718

1819
SPAM_NOTICE = "You're receiving this email because you signed up for Coderwall. We hate spam and make an effort to keep notifications to a minimum. To change your notification preferences, you can update your email settings here: http://coderwall.com/settings#email or immediately unsubscribe by clicking this link %unsubscribe_url%"
1920

20-
2121
WEEKLY_DIGEST_EVENT = 'weekly_digest'
2222
ACTIVITY_SUBJECT_PREFIX = "[Coderwall]"
2323

24+
#################################################################################
2425
def weekly_digest(username)
2526
headers['X-Mailgun-Variables'] = {email_type: WEEKLY_DIGEST_EVENT}.to_json
2627
track_campaign(WEEKLY_DIGEST_EVENT)
2728

2829
@user = User.find_by_username(username)
2930
since = [@user.last_request_at || Time.at(0), 1.week.ago].min
3031

31-
benchmark "digest:stats" do
32-
@stats = @user.activity_stats(since, true).sort_by { |stat, count| -(count || 0) }
33-
end
32+
# benchmark "digest:stats" do
33+
@stats = @user.activity_stats(since, true).sort_by { |stat, count| -(count || 0) }
3434

3535
#@networks = @user.following_networks.most_protips
3636
@user.touch(:last_email_sent)
3737
@issue = weekly_digest_utm
38-
benchmark "digest:protips" do
39-
@protips = protips_for(@user, 6)
40-
end
38+
#
39+
# benchmark "digest:protips" do
40+
@protips = protips_for(@user, 6)
4141

4242
abort_delivery if @protips.blank? || @protips.count < 4
4343

44-
benchmark "digest:stars" do
45-
@stars = @user.following_users.where('last_request_at > ?', 1.month.ago)
46-
@star_stat = star_stat_for_this_week
47-
@star_stat_string = STARS[@star_stat]
48-
@most = star_stats(@stars).sort_by { |star| -star[@star_stat] }.first
49-
@most = nil if @most && (@most[@star_stat] <= 0)
50-
end
44+
# benchmark "digest:stars" do
45+
stars = @user.following_users.where('last_request_at > ?', 1.month.ago)
46+
@star_stat = star_stat_for_this_week
47+
@star_stat_string = STARS[@star_stat]
48+
@most = star_stats(stars).sort_by { |star| -star[@star_stat] }.first
49+
@most = nil if @most && (@most[@star_stat] <= 0)
5150

52-
benchmark "digest:team" do
53-
@team, @job = get_team_and_job_for(@user)
54-
end
51+
# benchmark "digest:team" do
52+
@team, @job = get_team_and_job_for(@user)
5553

56-
benchmark "digest:mark_sent" do
57-
mark_sent(@job) unless @job.nil?
58-
end
54+
# benchmark "digest:mark_sent" do
55+
mark_sent(@job) unless @job.nil?
5956

6057
mail to: @user.email, subject: "#{ACTIVITY_SUBJECT_PREFIX} #{weekly_digest_subject_for(@user, @stats, @most)}"
58+
6159
rescue Exception => e
62-
abort_delivery(e.message)
60+
abort_delivery(e)
6361
end
62+
#################################################################################
6463

65-
def abort_delivery(message="")
64+
def abort_delivery(error=nil)
6665
#self.perform_deliveries = false
67-
Rails.logger.error "sending bad email:#{message}"
66+
Rails.logger.error "sending bad email:#{error.message}"
6867
end
6968

7069
private
@@ -91,7 +90,7 @@ def protips_for(user, how_many=6)
9190
protips = Protip.trending_for_user(user).first(how_many)
9291
protips += Protip.trending.first(how_many-protips.count) if protips.count < how_many
9392
else
94-
protips =Protip.hawt_for_user(user).results.first(how_many)
93+
protips = Protip.hawt_for_user(user).results.first(how_many)
9594
protips +=Protip.hawt.results.first(how_many) if protips.count < how_many
9695
end
9796
protips

‎app/models/protip.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,9 @@ def best_stat
551551
upvotes: self.upvotes,
552552
comments: self.comments.count,
553553
hawt: self.hawt? ? 100 : 0
554-
}.sort_by { |k, v| -v }.first
554+
}.sort_by do |k, v|
555+
-v
556+
end.first
555557
end
556558

557559
def upvotes

‎app/views/layouts/email.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,4 @@
144144
</table>
145145

146146
</body>
147-
</html>
147+
</html>
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
- nopad = 'margin: 0; padding: 0; '
2+
- sans_serif = 'font-family: Helvetica Neue, Helvetica, Arial, sans-serif;'
3+
- serif = 'font-family: Georgia, Times, Times New Roman, serif;'
4+
!!!
5+
%html{style: nopad}
6+
%head{style: nopad}
7+
%body{style: "margin: 0; padding: 60px 0; background-color: #48494e; -webkit-font-smoothing: antialiased; width: 100% !important; -webkit-text-size-adjust: none;"}
8+
%table{style: "width: 100%; margin: 0 auto; background: #48494e;"}
9+
%tr
10+
%td{style: "background: #48494e;"}
11+
%table{style: "margin: 0 auto 40px auto; padding: 0; width: 100%;"}
12+
%tr
13+
%td{style: "margin: 0 auto; padding: 0; width: 600px;"}
14+
%table.logo{border: 0, cellpadding: 0, cellspacing: 0, style: "margin: 0 auto 40px auto; padding: 0; width: 211px;", width: 211}
15+
%tr{style: nopad}
16+
%td{style: nopad}
17+
%img{alt: "Coderwall Logo", height: 35, src: image_url('email/coderwall-logo.jpg'), style: nopad, width: 211}
18+
%table.header{border: 0, cellpadding: 0, cellspacing: 0, style: "margin: 0 auto; padding: 0; width: 600px; background: #fff; -webkit-border-top-left-radius: 6px; -webkit-border-top-right-radius: 6px; border-top-left-radius: 6px; border-top-right-radius: 6px;", width: 600}
19+
%tr{style: nopad}
20+
%td{style: nopad}
21+
%img{alt: "Email Header", height: 159, src: image_url('email/email-popular_protip-header.png'), style: nopad, width: 600}
22+
23+
24+
%table.outside{border: 0, cellpadding: 0, cellspacing: 0, style: "margin: 0 auto; padding: 0 40px 20px 40px; width: 600px; background: #fff;", width: 600}
25+
%tr{style: nopad}
26+
%td{style: nopad}
27+
%table.tips{border: 0, cellpadding: 0, cellspacing: 0, style: "#{nopad} width: 520px; border: #cbc9c4 solid 2px; -webkit-border-radius: 6px; border-radius: 6px; overflow: hidden;"}
28+
%tr.title{style: "#{nopad} height: 50px; line-height: 50px;"}
29+
%td{colspan: 6, style: nopad}
30+
%h2{style: "#{nopad} font-weight: normal; #{serif} text-align: center; background: #ECE9E2; font-size: 19px; color: #48494e; margin-bottom: 20px;"}
31+
Popular protips
32+
33+
- @protips.each do |protip|
34+
- best_stat = OpenStruct.new(name: protip.best_stat.first.to_s, value: protip.best_stat.last)
35+
36+
%tr.tip{style: nopad}
37+
%td.avatar{style: "#{nopad} padding-left: 30px; width: 36px; padding-bottom: 20px;"}
38+
%img{alt: "Avatar", height: 36, src: image_url(users_image_path(protip.user)), style: "#{nopad} border: solid 2px #fff; -webkit-box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1);", width: 36}
39+
%td.link{style: "#{nopad} padding-right: 20px; padding-left: 10px; width: 270px; padding-bottom: 20px;"}
40+
%a{href: protip_url(protip.public_id, @issue), style: "#{nopad} #{sans_serif} font-size: 14px; line-height: 22px; color: #48494E; text-decoration: none; display: block;"}
41+
= protip.title
42+
- if best_stat.value.try(:to_i) == 0
43+
%td.thumb{style: "margin: 0; padding: 0 5px; width: 15px; padding-bottom: 20px; content: 'u'; font-size: 19px;"}
44+
- elsif best_stat.name == "upvotes"
45+
%td.thumb{style: "margin: 0; padding: 0 5px; width: 15px; padding-bottom: 20px; content: 'u'; font-size: 19px;"}
46+
= image_tag("email/upvote.png")
47+
- elsif best_stat.name == "comments"
48+
%td.thumb{style: "margin: 0; padding: 0 5px; width: 15px; padding-bottom: 20px; font-family: 'oli'; speak: none; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; line-height: 1; -webkit-font-smoothing: antialiased; content: '7'; font-size: 19px;"}
49+
= image_tag("email/comment.png")
50+
- elsif best_stat.name == "views"
51+
%td.thumb{style: "margin: 0; padding: 0 5px; width: 15px; padding-bottom: 20px; content: '6'; font-size: 19px;"}
52+
= image_tag("email/eye.png")
53+
- elsif best_stat.name == "hawt"
54+
%td.thumb{style: "margin: 0; padding: 0 5px; width: 15px; padding-bottom: 20px; content: '2'; font-size: 19px; color: #f35e39;"}
55+
= image_tag("email/flame.png")
56+
%td.upvotes{style: "margin: 0; padding: 0 5px; #{sans_serif} font-size: 14px; line-height: 22px; width: 15px; height: 36px; padding-bottom: 20px;"}
57+
%h4{style: "#{nopad} font-weight: normal;"}
58+
= formatted_best_stat_value(protip) unless best_stat.name =~ /hawt/ || best_stat.value.try(:to_i) == 0
59+
%tr.btns{style: nopad}
60+
%td.btns-box{colspan: 6, style: "margin: 0; padding: 20px 90px; border-top: solid 1px #cbc9c4;"}
61+
%a.share-tip{href: new_protip_url(@issue), style: "margin: 0; padding: 6px 16px; background: #d75959; margin-right: 20px; #{sans_serif} font-size: 14px; line-height: 22px; display: inline-block; width: 120px; color: #fff; text-decoration: none; -webkit-border-radius: 4px; border-radius: 4px; text-align: center;"}
62+
Share a protip
63+
%a.browse-networks{href: root_url(@issue), style: "margin: 0; padding: 6px 16px; background: #3d8dcc; #{sans_serif} font-size: 14px; line-height: 22px; display: inline-block; width: 120px; color: #fff; text-decoration: none; -webkit-border-radius: 4px; border-radius: 4px; text-align: center;"}
64+
Trending protips
65+
66+
- unless @most.nil?
67+
%table.outside{border: 0, cellpadding: 0, cellspacing: 0, style: "margin: 0 auto; padding: 0 40px 20px 40px; width: 600px; background: #fff;", width: 600}
68+
%tr{style: nopad}
69+
%td{style: nopad}
70+
%table.activity{border: 0, cellpadding: 0, cellspacing: 0, style: "#{nopad} width: 520px; border: #cbc9c4 solid 2px; -webkit-border-radius: 6px; border-radius: 6px; overflow: hidden;"}
71+
%tr.title{style: "#{nopad} height: 50px; line-height: 50px;"}
72+
%td{colspan: 2, style: nopad}
73+
%h2{style: "#{nopad} font-weight: normal; #{serif} text-align: center; background: #ECE9E2; font-size: 19px; color: #48494e; margin-bottom: 20px;"}
74+
Activity from your connections
75+
%tr{style: nopad}
76+
%td.activity-title{style: "margin: 0; padding: 0 0 0 30px;"}
77+
%h2{style: "#{nopad} font-weight: normal; #{serif} font-size: 20px; background: url(#{image_url('email/big-gold-star.png')}) no-repeat left top; padding-left: 30px;"}
78+
== Most #{@star_stat_string}
79+
%td.activity-avatar{style: "#{nopad} padding-right: 30px;"}
80+
%img{alt: "User Avatar", src: image_url(users_image_path(@most[:user])), style: "#{nopad} border: solid 2px #fff; -webkit-box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1);"}
81+
82+
%tr{style: nopad}
83+
%td.activity-message{style: "margin: 0; padding: 10px 20px 20px 30px;"}
84+
%p{style: "#{nopad} #{sans_serif} font-size: 14px; line-height: 22px;"}
85+
%a{href: badge_url(@most[:user].username, @issue), style: "#{nopad} color: #3d8dcc;"}
86+
= @most[:user].short_name
87+
had
88+
= @most[@star_stat] > 1 ? "#{@most[@star_stat]}" : "most"
89+
==#{@star_stat_string} this week.
90+
= succeed "." do
91+
%a{href: badge_url(@most[:user].username, @issue), style: "#{nopad} color: #3d8dcc;"}
92+
== View #{@most[:user].username}'s profile
93+
- unless @team.nil?
94+
%table.outside{border: 0, cellpadding: 0, cellspacing: 0, style: "margin: 0 auto; padding: 0 40px 20px 40px; width: 600px; background: #fff;", width: 600}
95+
%tr{style: nopad}
96+
%td{style: nopad}
97+
%table.team{border: 0, cellpadding: 0, cellspacing: 0, style: "#{nopad} width: 520px; border: #cbc9c4 solid 2px; -webkit-border-radius: 6px; border-radius: 6px; overflow: hidden;"}
98+
%tr.title{style: "#{nopad} height: 50px; line-height: 50px;"}
99+
%td{colspan: 2, style: nopad}
100+
%h2{style: "#{nopad} font-weight: normal; #{serif} text-align: center; background: #ECE9E2; font-size: 19px; color: #48494e; margin-bottom: 20px;"}
101+
Featured engineering team
102+
%tr{style: nopad}
103+
%td.team-avatar{style: "margin: 0; padding: 10px 0 30px 20px; width: 120px;"}
104+
%img{alt: "Team Avatar", height: 89, src: image_url(@team.avatar_url), style: "#{nopad} border: solid 3px #eaeaea;", width: 89}
105+
%td.job-info{style: "margin: 0; padding: 25px;"}
106+
%h2{style: "#{nopad} font-weight: normal; #{serif} font-size: 24px; line-height: 22px; margin-bottom: 6px;"}
107+
= @team.name
108+
%h3{style: "#{nopad} font-weight: normal; #{serif} font-size: 16px; line-height: 22px; margin-bottom: 6px;"}
109+
= truncate(@team.hiring_message, length: 80)
110+
%a{href: teamname_url(@team.slug, @issue) + "#open-positions", style: "#{nopad} color: #3d8dcc;"}
111+
= @team.name
112+
is looking for
113+
= @job.title
114+
%tr
115+
%td{colspan: 2, style: "width: 100%;"}
116+
%table{style: "width: 100%;"}
117+
%tr.team-btm{style: nopad}
118+
%td.team-members{style: "margin: 0; padding: 25px 15px 25px 25px; width: 158px; border-top: solid 1px #eaeaea; border-right: solid 1px #eaeaea;"}
119+
-@team.most_influential_members_for(@user).first(3).each do |member|
120+
%img{alt: "Avatar", height: 36, src: image_url(users_image_path(member)), style: "#{nopad} border: solid 2px #fff; -webkit-box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); box-shadow: 0px 1px 1px 1px rgba(0, 0, 0, 0.1); margin-right: 10px;", width: 36}
121+
%td.stack{style: "margin: 0; padding: 0 0 0 25px; border-top: solid 1px #eaeaea;"}
122+
%p{style: "#{nopad} #{serif} font-size: 16px; line-height: 22px;"}
123+
=truncate(@team.tags_for_jobs.join(", "), length: 35)
124+
%table.outside{border: 0, cellpadding: 0, cellspacing: 0, style: "margin: 0 auto; padding: 0 40px 20px 40px; width: 600px; background: #fff;", width: 600}
125+
%tr{style: nopad}
126+
%td{style: nopad}
127+
%table.top-tip{border: 0, cellpadding: 0, cellspacing: 0, style: nopad}
128+
%tr{style: nopad}
129+
%td.glasses{style: "margin: 0; padding: 0 40px 30px 0;"}
130+
%img{alt: "Glasses", height: 114, src: image_url("email/glasses.png"), style: nopad, width: 155}
131+
%td.tip{style: "margin: 0; padding: 0 0 30px 0; text-align: right;"}
132+
%h2{style: "#{nopad} font-weight: normal; #{serif} color: #99958b; margin-bottom: 10px;"}
133+
This weeks top tip:
134+
%h3{style: "#{nopad} font-weight: bold; #{sans_serif} font-size: 16px; line-height: 22px; color: #48494E;"}
135+
- unless @user.team.nil?
136+
-if @user.team.premium?
137+
- if @user.team.hiring?
138+
The more popular pro tips
139+
= @user.team.name
140+
team members author, the more exposure your jobs receive
141+
- else
142+
add open positions to your team page and they will get featured here
143+
-else
144+
Want
145+
=@user.team.name
146+
featured here?
147+
%a{href: employers_url(@issue), style: "#{nopad} color: #3d8dcc;"} add open positions
148+
- else
149+
Want your team featured here?
150+
%a{href: employers_url(@issue), style: "#{nopad} color: #3d8dcc;"} create team
151+
152+
%table.outside{border: 0, cellpadding: 0, cellspacing: 0, style: "margin: 0 auto; padding: 0 40px 20px 40px; width: 600px; background: #48494e;", width: 600}
153+
%tr{style: nopad}
154+
%td{style: "#{nopad} text-align: center"}
155+
%p.reminder{style: "color: #fff; font-size: 12px; #{sans_serif} margin-top: 0; margin-bottom: 15px; padding-top: 0; padding-bottom: 0; line-height: 18px;"}
156+
You're receiving this email because you signed up for Coderwall. We hate spam and make an effort to keep notifications to a minimum.
157+
%p{style: "color: #c9c9c9; font-size: 12px; #{sans_serif}"}
158+
%preferences{style: "color: #3ca7dd; text-decoration: none;"}>
159+
%strong
160+
%a{href: "https: //coderwall.com/settings#email", style: "color: #3ca7dd; text-decoration: none;"}
161+
Edit your subscription
162+
\  |  
163+
%unsubscribe{style: "color: #3ca7dd; text-decoration: none;"}
164+
%strong
165+
%a{href: '%unsubscribe_url%', style: "color: #3ca7dd; text-decoration: none;"}
166+
Unsubscribe instantly

‎app/views/protips/_mini.html.haml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
%article{:class => dom_class(protip), :id => protip.public_id}
33
%header
44
-if display_protip_stats?(protip)
5-
%span{:class => protip_stat_class(protip)}
5+
%span{:class => best_stat_name(protip)}
66
= formatted_best_stat_value(protip) unless best_stat_name(protip) =~ /hawt/
77

88
-# We should remove this to cache , deleting should be from dashboard

‎app/views/weekly_digest_mailer/weekly_digest.html.haml

Lines changed: 129 additions & 110 deletions
Large diffs are not rendered by default.

‎app/views/weekly_digest_mailer/weekly_digest.text.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Your weekly brief
66
<% end %>
77
<% end %>
88

9-
View all your stats on your dashboard: <%= dashboard_url(@issue) %>
9+
View all your stats on your dashboard: DASHBOARD URL
1010

1111
This week's protips for you
1212

‎config/routes.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@
269269
get '/.json', to: proc { [444, {}, ['']] }
270270
get '/teams/.json', to: proc { [444, {}, ['']] }
271271

272+
273+
if Rails.env.development?
274+
mount MailPreview => 'mail_view'
275+
end
276+
272277
#TODO: REMOVE
273278
match 'protips/update', via: %w(get put)
274279
match 'protip/update' , via: %w(get put)

‎lib/tasks/mailers.rake

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace :mailers do
2+
task popular_protips: :environment do
3+
from = 60.days.ago
4+
to = 0.days.ago
5+
user = User.with_username('mcansky')
6+
protips = ProtipMailer::Queries.popular_protips(from, to)
7+
ProtipMailer.popular_protips(user, protips, from, to).deliver
8+
end
9+
end

‎lib/tasks/marketing.rake

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ namespace :marketing do
33
task :send => :environment do
44
LifecycleMarketing.process!
55
end
6+
7+
68
end
7-
end
9+
end

‎script/ide

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ rbenv gemset active
2424

2525
bundle install
2626
bundle clean --force
27-
bundle exec spring binstub --all.
27+
bundle exec spring binstub --all
2828
rbenv rehash
2929
vagrant up --no-provision
3030

@@ -56,8 +56,8 @@ tmux send-keys "clear ; bundle exec guard -c -g rspec" C-m
5656
# Web
5757
tmux select-window -t $SESSION:2
5858
tmux select-pane -t 0
59-
#tmux send-keys "clear ; env bin/rails server webrick -p3000" C-m
60-
tmux send-keys "clear ; bundle exec puma -C ./config/puma.rb" C-m
59+
tmux send-keys "clear ; bundle exec rails server webrick -p3000" C-m
60+
#tmux send-keys "clear ; bundle exec puma -C ./config/puma.rb" C-m
6161

6262
# Sidekiq
6363
tmux select-window -t $SESSION:3
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ProtipMailer#popular_protips
2+
3+
Hi, find me in app/views/protip_mailer/popular_protips

‎spec/mailers/protip_mailer_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
require 'rails_helper'
2+
3+
RSpec.describe ProtipMailer, :type => :mailer do
4+
describe "popular_protips" do
5+
let(:mail) { ProtipMailer.popular_protips(@from, @to) }
6+
7+
before do
8+
@from = 30.days.ago
9+
@to = 0.days.ago
10+
end
11+
12+
it 'renders the headers' do
13+
expect(mail.subject).to match('Popular Protips on Coderwall')
14+
expect(mail.to).to eq(['someone@example.com'])
15+
expect(mail.from).to eq(['support@coderwall.com'])
16+
end
17+
18+
it 'renders the body' do
19+
expect(mail.body.encoded).to match("somethings that's meaningful and nice")
20+
end
21+
end
22+
23+
24+
describe ProtipMailer::Queries do
25+
it 'queries for the popular protips since a date' do
26+
from = 1.day.ago
27+
to = 0.days.ago
28+
ap results = ProtipMailer::Queries.popular_protips(from, to)
29+
end
30+
end
31+
end

‎vagrant/bootstrap.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ echo Where am I? You are in `pwd`
66
echo I think my home is $HOME
77
echo export EDITOR=vim >> $HOME/.bashrc
88
# Enable accessing Postgres from the host machine
9+
apt-get -y install libcurl3 libcurl3-dev libcurl3-gnutls libcurl4-openssl-dev
10+
apt-get -y install libpq-dev
11+
apt-get -y install libxml2 libxml2-dev libxslt1-dev
912
echo "listen_addresses = '*'" | tee -a /var/pgsql/data/postgresql.conf
1013
echo host all all 0.0.0.0/0 trust | tee -a /var/pgsql/data/pg_hba.conf
1114
sudo su postgres -c 'pg_ctl stop -D /var/pgsql/data 2>&1'

‎vagrant/coderwall-box/scripts/postinstall.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ DEBIAN_FRONTEND=noninteractive apt-get install -y oracle-java7-installer oracle-
5151
echo "export JAVA_OPTS=\"-Xmx400m -XX:MaxPermSize=80M -XX:+UseCompressedOops -XX:+AggressiveOpts\"" >> /etc/profile.d/jdk.sh
5252
echo "setenv JAVA_OPTS \"-Xmx400m -XX:MaxPermSize=80M -XX:+UseCompressedOops -XX:+AggressiveOpts\"" >> /etc/profile.d/jdk.csh
5353

54-
NODEJS_VERSION="0.11.12"
54+
NODEJS_VERSION="0.10.31"
5555
git clone https://github.com/joyent/node.git
5656
cd node
5757
git checkout v$NODE_VERSION
@@ -61,7 +61,7 @@ make install
6161
cd ..
6262
rm -rf node*
6363

64-
RUBY_VERSION="2.1.0"
64+
RUBY_VERSION="2.1.2"
6565
wget http://ftp.ruby-lang.org/pub/ruby/2.1/ruby-$RUBY_VERSION.tar.bz2
6666
tar jxf ruby-$RUBY_VERSION.tar.bz2
6767
cd ruby-$RUBY_VERSION
@@ -73,7 +73,7 @@ rm -rf ruby-$RUBY_VERSION*
7373
chown -R root:admin /opt/ruby
7474
chmod -R g+w /opt/ruby
7575

76-
RUBYGEMS_VERSION="2.2.2"
76+
RUBYGEMS_VERSION="2.4.1"
7777
wget http://production.cf.rubygems.org/rubygems/rubygems-$RUBYGEMS_VERSION.tgz
7878
tar xzf rubygems-$RUBYGEMS_VERSION.tgz
7979
cd rubygems-$RUBYGEMS_VERSION
@@ -92,7 +92,7 @@ groupadd puppet
9292
# Install Foreman
9393
/opt/ruby/bin/gem install foreman --no-ri --no-rdoc
9494

95-
POSTGRES_VERSION="9.3.2"
95+
POSTGRES_VERSION="9.3.3"
9696
wget http://ftp.postgresql.org/pub/source/v$POSTGRES_VERSION/postgresql-$POSTGRES_VERSION.tar.bz2
9797
tar jxf postgresql-$POSTGRES_VERSION.tar.bz2
9898
cd postgresql-$POSTGRES_VERSION
@@ -126,7 +126,7 @@ echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' |
126126
apt-get -y update
127127
apt-get -y install mongodb-10gen
128128

129-
REDIS_VERSION="2.8.3"
129+
REDIS_VERSION="2.8.4"
130130
wget http://download.redis.io/releases/redis-$REDIS_VERSION.tar.gz
131131
tar xzf redis-$REDIS_VERSION.tar.gz
132132
cd redis-$REDIS_VERSION
@@ -138,7 +138,7 @@ yes | sudo ./install_server.sh
138138
cd ../..
139139
rm -rf ~/redis-$REDIS_VERSION
140140

141-
ES_VERSION="0.20.5"
141+
ES_VERSION="0.90.13"
142142
wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-$ES_VERSION.deb
143143
dpkg -i elasticsearch-$ES_VERSION.deb
144144
rm -rf ~/elasticsearch-$ES_VERSION.deb

‎vagrant/coderwall-box/template.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"builders": [{
33
"type": "virtualbox-iso",
4-
"boot_wait": "10s",
5-
"disk_size": 10140,
4+
"boot_wait": "15s",
5+
"disk_size": 32768,
66
"guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso",
77
"guest_os_type": "Ubuntu_64",
88
"http_directory": "http",

‎vagrant/user-config.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ echo rvm_trust_rvmrcs_flag=1 >> $HOME/.rvmrc
1515
curl -k -L https://get.rvm.io | bash -s stable --autolibs=enabled
1616
source "$HOME/.rvm/scripts/rvm"
1717
[[ -s "$rvm_path/hooks/after_cd_bundle" ]] && chmod +x $rvm_path/hooks/after_cd_bundle
18+
rvm autolibs enable
1819
rvm requirements
1920
rvm reload
2021
_RUBY_VERSION=ruby-2.1.2

0 commit comments

Comments
 (0)
Please sign in to comment.