2. What’s the talk about?
• No diabolic tricks and wicked craft
• A year experience sharing
• Goes through our practice
• You will know more gems
• You will know what media websites care about most
• You will know how to handle larger traffic for media websites
• Nothing funny
3. What’s the talk about?
• No diabolic tricks and wicked craft
• A year experience sharing
• Goes through our practice
• You will know more gems
• You will know what media websites care about most
• You will know how to handle larger traffic for media websites
• Nothing funny
4. Backer-Founder
• First and leading crowdfunding consulting
agency in Taiwan
• Has carried out 40 projects to domestic and
abroad
• Raised over $7 million in 11 months since its
establishment in October 2014.
9. TNL
• A group of people who are dissatisfied with the
existing media environment and want to make a
difference.
• We aspire to build a media that provides not only
facts, but also diverse perspectives.
13. Wordpress
• WordPress is a free and open-source content
management system (CMS) based on PHP and
MySQL.
• Features include a plugin architecture and a
template system.
• WordPress was used by more than 23.3% of the
top 10 million websites as of January 2015.
• Initial release May 27, 2003 (12 years ago)
http://www.wikiwand.com/en/WordPress
19. Wordpress
• More than 30000 commits
• 23+ contributors
• 127+ version releases
• Nearly 500,000 lines of code, which contains nearly
300,000 lines of php code
https://github.com/wordpress/wordpress
33. But in wordpress …
• All of those we mentioned before are in
only one table.
• Also include custom post types, links,
navigation menu items!!!
post_type
•post
•page
•revision
•attachment
•nav_menu_item
•…
40. If you need to know more…
• http://codex.wordpress.org/Database_Description
• http://code.tutsplus.com/tutorials/understanding-
and-working-with-data-in-wordpress--cms-20567
41. And we made a gem
• wpdb_activerecord: https://github.com/hothero/
wpdb_activerecord
• It’s a ORM wrapper for the WordPress database,
using ActiveRecord.
42. wpdb_activerecord
1 # Gemfile
2 gem "wpdb_activerecord"
3
4 # Post
5 WPDB::Post.all # Get all posts
6 @post = WPDB::Post.find(75)
7 @post.tags
8 @post.attachments # No matter what type
9 @post.revisions
10 @post.author
11
12 # Term
13 WPDB::Term.tag # get all tags
14 WPDB::Term.category # get all categories
43. wpdb_activerecord -
advanced
1 # config/wpdb_activerecord.yml
2 WPDB_PREFIX: "cgjbugpbs_"
# the table of WPDB::Post is cgjbugpbs_posts, not wp_posts
4 WPDB_USER_CLASS: "WUser"
5
6 # models/w_user.rb
7 class WUser < WPDB::User
8 def hello
9 puts "world"
10 end
11 end
12
13 # usage
14 @author = WPDB::Post.find(25).author
15 @author.class_name # will get WUser, not WPDB::
16 User
17 @author.hello # world
64. editflow.org
• Edit Flow gives you custom statuses, a calendar, editorial comments, and
more, all to make it much easier for your team to collaborate within WordPress.
• Calendar
• Custom Statuses
• Editorial Comments
• Editorial Metadata
• Notifications
• Story Budget
• User Groups
68. Editorial Comments
• Use this gem: acts_as_commentable
• Polymorphic Associations
1 commentable = Post.create
2 comment = commentable.comments.create
3 comment.title = "First comment."
4 comment.comment = "This is the first comment."
5 comment.save
69. What’s the Polymorphic
Associations
• Assume a situation
• An employee has many pictures
• A Product has many pictures
• And each of those pictures have owned
information detail.
70. What’s the Polymorphic
Associations
• In general: two relationship tables to associate objects
and pictures
Employee
Product
Employee_
Relationship
employee_id
picture_id
Product_
Relationship
product_id
picture_id
Picture
75. editflow.org
• Edit Flow gives you custom statuses, a calendar, editorial comments, and
more, all to make it much easier for your team to collaborate within WordPress.
• Calendar
• Custom Statuses
• Editorial Comments
• Editorial Metadata
• Notifications
• Story Budget
• User Groups
76. Story Budget
• View all of your upcoming posts in a more traditional story
budget view, and hit the print button to take it to your
planning meeting.
77. Search Design In General
• In general, we maybe use many conditions to match different
situation. 1 def search
2 @posts = Post.send(params[:scope])
3 if params.key?(:author_id)
4 @posts = @posts.where(author_id: params[:
5 author_id])
6 end
7 if params.key?(:post_type)
8 @posts = @posts.where(post_type: params[:
9 post_type])
10 end
11 if params.key?(:start_date) || params.key?(:
12 end_date)
13 @posts = @posts.where(updated_at: params[:
14 start_date]..params[:end_date])
15 end
16 # ...
17 end
78. Search Design In General
• In general, we maybe use many conditions to match different
situation. 1 def search
2 @posts = Post.send(params[:scope])
3 if params.key?(:author_id)
4 @posts = @posts.where(author_id: params[:
5 author_id])
6 end
7 if params.key?(:post_type)
8 @posts = @posts.where(post_type: params[:
9 post_type])
10 end
11 if params.key?(:start_date) || params.key?(:
12 end_date)
13 @posts = @posts.where(updated_at: params[:
14 start_date]..params[:end_date])
15 end
16 # ...
17 end
https://github.com/
activerecord-hackery/
ransack
79. Ransack - View Helper1 <%= search_form_for @q do |f| %>
2 # Search if the name field contains...
3 <%= f.label :name_cont %>
4 <%= f.search_field :name_cont %>
5
6 # Search if an associated articles.title
7 starts with...
8 <%= f.label :articles_title_start %>
9 <%= f.search_field :articles_title_start %>
10
11 # Attributes may be chained. Search multiple
12 attributes for one value...
13 <%= f.label :
14 name_or_description_or_email_or_articles_t
15 itle_cont %>
16 <%= f.search_field :
17 name_or_description_or_email_or_articles_t
18 itle_cont %>
19
20 <%= f.submit %>
21 <% end %>
name_cont
80. Ransack - View Helper1 <%= search_form_for @q do |f| %>
2 # Search if the name field contains...
3 <%= f.label :name_cont %>
4 <%= f.search_field :name_cont %>
5
6 # Search if an associated articles.title
7 starts with...
8 <%= f.label :articles_title_start %>
9 <%= f.search_field :articles_title_start %>
10
11 # Attributes may be chained. Search multiple
12 attributes for one value...
13 <%= f.label :
14 name_or_description_or_email_or_articles_t
15 itle_cont %>
16 <%= f.search_field :
17 name_or_description_or_email_or_articles_t
18 itle_cont %>
19
20 <%= f.submit %>
21 <% end %>
articles_title_start
81. Ransack - View Helper1 <%= search_form_for @q do |f| %>
2 # Search if the name field contains...
3 <%= f.label :name_cont %>
4 <%= f.search_field :name_cont %>
5
6 # Search if an associated articles.title
7 starts with...
8 <%= f.label :articles_title_start %>
9 <%= f.search_field :articles_title_start %>
10
11 # Attributes may be chained. Search multiple
12 attributes for one value...
13 <%= f.label :
14 name_or_description_or_email_or_articles_t
15 itle_cont %>
16 <%= f.search_field :
17 name_or_description_or_email_or_articles_t
18 itle_cont %>
19
20 <%= f.submit %>
21 <% end %>
name_or_description_or_email
_or_articles_title_cont
82. Ransack - Controller
1 def index
2 @q = Person.ransack(params[:q])
3 @people = @q.result(distinct: true)
4 end
83. Ransack - Controller
1 def index
2 @q = Person.ransack(params[:q])
3 @people = @q.result(distinct: true)
4 end
85. editflow.org
• Edit Flow gives you custom statuses, a calendar, editorial comments, and
more, all to make it much easier for your team to collaborate within WordPress.
• Calendar
• Custom Statuses
• Editorial Comments
• Editorial Metadata
• Notifications
• Story Budget
• User Groups
89. STI (Single-table inheritance)
class User < ActiveRecord::Base
end
class Author < User
enum role: [:normal, :blogger]
def publish # ex: only author can use
end
end
class Editor < User
enum role: [:editor, :senior_editor, :chief]
end
class Admin < User
end
User
type
type: rails reserved word
90. STI with Priority Problem
• STI is unfit for priority feature.
• Chief Editor > Senior Editor > Editor > Author
• Enum-design is more fit.
1 class User < ActiveRecord::Base
2 enum role:
[:normal, :blogger, :editor, :senior_editor, :chief]
4 end
91. Other parts
• Except edit-flow, there are some more features
• takeover & editing lock
• media gallery
• globalize
93. takeover & editing lock
• Use this gem: message_bus
• MessageBus implements a Server to Server
channel protocol and Server to Web Client protocol
(using polling or long-polling)
94. • We use current_editor_id field as lock in post.
• Subscribe a channel for takeover when start editing in front-
end.
• Publish a message to the channel when someone wanna
edit.
takeover & editing lock
1 MessageBus.start();
2 MessageBus.callbackInterval = 500;
3 MessageBus.subscribe("/posts/<%= @post.id %>/takeover/request",
4 function(msg){
5 unlock_post_and_auto_save();
6 window.location = "<%= posts_path %>";
7 });
1 MessageBus.publish(“/posts/#{post.id}/takeover/request”,
username: user.name)
Channel
96. Media Gallery - Tricks
Browsers don't allow file
uploads via XMLHttpRequest
(aka XHR) for security reasons.
97. General in jQuery
• Hijack the forms submit event to execute our custom
iFrame-method function
• Submit the form to the iFrame normal-style (non-AJAX)
• Copy the response content from the iFrame back into the
parent window.
http://www.alfajango.com/blog/ajax-file-uploads-with-the-iframe-method/
98. remotipart
• Use this gem: remotipart
• Remotipart is a Ruby on Rails gem enabling AJAX file
uploads with jQuery in Rails 3 and Rails 4 remote forms.
1 # Gemfile
2 gem "remotipart"
3
4 # js
5 //= require jquery.remotipart
100. globalize
• Use this gem: globalize
• Each locale have their owned tags & revisions
1 # config/initializers/post_translation.rb
2 Post::Translation.module_eval do
3 acts_as_ordered_taggable
4 has_paper_trail only: [:content, :title, :
5 excerpt]
6 end
102. API Design In General
class ApiController < ApplicationController
def all
# get review & news type post
case params[:post_type]
when "new" # latest
when "hot" # hot
end
end
def review
case params[:post_type]
when "new"
when "hot"
end
end
end
def news
case params[:post_type]
when "new"
when "hot"
end
end
103. has_scope
• Use this gem: has_scope
• In model
1 class Graduation < ActiveRecord::Base
2 scope :featured, -> { where(:featured => true)
3 }
4 scope :by_degree, -> degree { where(:degree =>
5 degree) }
6 scope :by_period, -> started_at, ended_at {
7 where("started_at = ? AND ended_at = ?",
8 started_at, ended_at) }
9 end
104. has_scope
• You can use those named scopes as filters by declaring
them on your controller and just need to call apply_scopes
to an specific resource.
1 class GraduationsController <
2 ApplicationController
3 has_scope :featured, :type => :boolean
4 has_scope :by_degree
5 has_scope :by_period, :using => [:started_at,
6 :ended_at], :
7 type => :hash
8
9 def index
10 @graduations = apply_scopes(Graduation).all
11 end
12 end
105. has_scope
• You can use those named scopes as filters by declaring
them on your controller and just need to call apply_scopes
to an specific resource.
1 class GraduationsController <
2 ApplicationController
3 has_scope :featured, :type => :boolean
4 has_scope :by_degree
5 has_scope :by_period, :using => [:started_at,
6 :ended_at], :
7 type => :hash
8
9 def index
10 @graduations = apply_scopes(Graduation).all
11 end
12 end
/graduations?featured=true
106. has_scope
• You can use those named scopes as filters by declaring
them on your controller and just need to call apply_scopes
to an specific resource.
1 class GraduationsController <
2 ApplicationController
3 has_scope :featured, :type => :boolean
4 has_scope :by_degree
5 has_scope :by_period, :using => [:started_at,
6 :ended_at], :
7 type => :hash
8
9 def index
10 @graduations = apply_scopes(Graduation).all
11 end
12 end
/graduations?by_period[started_at]=20100701&by_period[ended_at]=20101013
107. has_scope
• You can use those named scopes as filters by declaring
them on your controller and just need to call apply_scopes
to an specific resource.
1 class GraduationsController <
2 ApplicationController
3 has_scope :featured, :type => :boolean
4 has_scope :by_degree
5 has_scope :by_period, :using => [:started_at,
6 :ended_at], :
7 type => :hash
8
9 def index
10 @graduations = apply_scopes(Graduation).all
11 end
12 end
/graduations?featured=true&by_degree=phd
108. Tragedy at the admin theme
Find out a good design
which have smooth
operating and fancy
109. turbolinks & pjax
• Instead of letting the browser recompile the JavaScript and
CSS between each page change, it keeps the current
page instance alive and replaces only the body (or parts
of) and the title in the head.
• Same advantages: good user experience, reduce
bandwidth and server cost
• In particular with Rails:
• more detail: http://goo.gl/Lx7mHk
# Gemfile
gem “turbolinks”
# app/assets/javascripts/
application.js
//= require tubolinks
$.pjax({url:’authors’,
container:’#main’})
if request.headers[‘X-PJAX’]
render:layout
end
110.
111. rails-gem-list
• A workman must sharpen his tools if he is to do his work
well / ⼯工欲善其事,必先利其器
• We’ve done a gem: rails-gem-list
• You can go through rails-gem-list to know how to construct
your project architecture and DB schema. Just like
• acts_as_taggable
• ransack
• …
112. rails-gem-list
• A workman must sharpen his tools if he is to do his work
well / ⼯工欲善其事,必先利其器
• We’ve done a gem: rails-gem-list
• You can go through rails-gem-list to know how to construct
your project architecture and DB schema. Just like
• acts_as_taggable
• ransack
• …
113. Finally
• If you want to do a media website, you can follow edit-flow to
manage your posts.
• If your WordPress website becomes larger and larger, you can
rewrite your front-end with Rails.
• Please don’t even think to transfer the whole site from WordPress to
Rails.
• After we had spoken many topics, there are still two main parts we
didn’t present
• Server Architecture
• Data Migration