Advertisement
  1. Code
  2. CMS

Membangun CMS: rubyPress

Scroll to top
Read Time: 15 min

() translation by (you can also view the original English article)

Setelah membuat struktur dasar sistem manajemen konten (CMS), dan server sebenarnya menggunakan Go dan Node.js, Anda siap untuk mencoba dengan bahasa lainnya.

Kali ini, saya menggunakan bahasa Ruby untuk membuat server-nya. Saya telah menemukan bahwa dengan membuat program yang sama dalam banyak bahasa, Anda mulai mendapatkan wawasan baru tentang cara yang lebih baik untuk menerapkan program ini. Anda juga melihat lebih banyak cara untuk menambahkan fungsionalitas ke program. Mari kita mulai.

Pengaturan dan Memuat Perpustakaannya

Untuk memprogram di Ruby, Anda perlu instalasi versi terbaru pada sistem Anda. Banyak sistem operasi yang telah diinstal sebelumnya dengan Ruby akhir-akhir ini (Linux dan OS X), namun biasanya memiliki versi yang lebih lawas. Tutorial ini mengasumsikan bahwa Anda memiliki versi Ruby 2.4.

Cara termudah untuk upgrade ke ruby versi terbaru adalah dengan menggunakan RVM. Untuk menginstal RVM di Linux atau Mac OS X, ketik berikut ini di terminal:

1
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
2
curl -sSL https://get.rvm.io | bash -s stable

Ini akan membuat koneksi yang aman untuk mengunduh dan menginstal RVM. Ini akan menginstal rilis Ruby terbaru yang stabil juga. Anda harus memuat ulang shell Anda untuk menyelesaikan instalasi.

Untuk Windows, Anda bisa mendownload Windows Ruby Installer. Saat ini, paket ini sampai ke Ruby 2.2.2, yang mana bagus untuk menjalankan perpustakaan dan skrip dalam tutorial ini.

Setelah bahasa Ruby terpasang dengan benar, Anda sekarang dapat menginstal perpustakaannya. Ruby, sama seperti Go dan Node, memiliki manajer paket untuk menginstal perpustakaan pihak ketiga. Di terminal, ketik berikut ini:

1
gem install sinatra
2
gem install ruby-handlebars
3
gem install kramdown
4
gem install slim

Ini menginstal perpustakaan Sinatra, Ruby Handlebars, Kramdown, dan Slim. Sinatra adalah framework aplikasi web. Ruby Handlebars menerapkan mesin template Handlebars di Ruby. Kramdown adalah konverter Markdown ke HTML. Slim adalah perpustakaan bekerja mirip Jade, namun tidak mencakup definisi makro Jade. Oleh karena itu, makro yang digunakan dalam indeks posting Berita dan Blog sekarang adalah Jade normal.

Membuat File rubyPress.rb

Di direktori teratas, buat file rubyPress.rb dan tambahkan kode berikut. Saya akan berkomentar tentang setiap bagian saat ditambahkan ke file.

1
#

2
# Load the Libraries.

3
#

4
require 'sinatra'  		# http://www.sinatrarb.com/

5
require 'ruby-handlebars' 	# https://github.com/vincent-psarga/ruby-handlebars

6
require 'kramdown' 			# http://kramdown.gettalong.org

7
require 'slim' 				# http://slim-lang.com/

8
require 'json'
9
require 'date'

Hal pertama yang harus dilakukan adalah memuat perpustakaannya. Berbeda dengan Node.js, ini tidak dimasukkan ke dalam variabel. Perpustakaan Ruby menambahkan fungsinya ke lingkup program.

1
#

2
# Setup the Handlebars engine.

3
#

4
$hbs = Handlebars::Handlebars.new
5
6
#

7
# HandleBars Helper:   date

8
#

9
# Description:         This helper returns the current date

10
#                      based on the format given.

11
#

12
$hbs.register_helper('date') {|context, format|
13
	now = Date.today
14
	now.strftime(format)
15
}
16
17
#

18
# HandleBars Helper:   cdate

19
#

20
# Description:         This helper returns the given date

21
#                      based on the format given.

22
#

23
$hbs.register_helper('cdate') {|context, date, format|
24
	day = Date.parse(date)
25
	day.strftime(format)
26
}
27
28
#

29
# HandleBars Helper:   save

30
#

31
# Description:         This helper expects a

32
#                      "|" where the name

33
#                      is saved with the value for future

34
#                      expansions. It also returns the

35
#                      value directly.

36
#

37
$hbs.register_helper('save') {|context, name, text|
38
	#

39
	# If the text parameter isn't there, then it is the 

40
	# goPress format all combined into the name. Split it 

41
	# out. The parameters are not String objects. 

42
	# Therefore, they need converted first.

43
	#

44
	name = String.try_convert(name)
45
	if name.count("|") > 0
46
		parts = name.split('|')
47
		name = parts[0]
48
		text = parts[1]
49
	end
50
51
	#

52
	# Register the new helper.

53
	#

54
	$hbs.register_helper(name) {|context, value|
55
		text
56
	}
57
58
	#

59
	# Return the text.

60
	#

61
	text
62
}

Perpustakaan Handlebars diinisialisasi dengan fungsi pembantu yang berbeda. Fungsi pembantu yang didefinisikan adalah date, cdate, dan save.

Fungsi helper date mengambil tanggal dan waktu sekarang, dan memformatnya sesuai dengan format string yang dikirimkan ke helper. cdate adalah mirip kecuali mengirimkan tanggal dahulu. Helper save memungkinkan Anda untuk menentukan name dan value. Ini menciptakan helper baru dengan nama name dan mengembalikan value-nya. Ini memungkinkan Anda untuk membuat variabel yang ditentukan sekali dan mempengaruhi banyak lokasi. Fungsi ini juga mengambil versi Go, yang mengharapkan sebuah string dengan name, '|' sebagai pemisah, dan value.

1
#

2
# Load Server Data.

3
#

4
$parts = {}
5
$parts = JSON.parse(File.read './server.json')
6
$styleDir = Dir.getwd + '/themes/styling/' + $parts['CurrentStyling']
7
$layoutDir = Dir.getwd + '/themes/layouts/' + $parts['CurrentLayout']
8
9
#

10
# Load the layouts and styles defaults.

11
#

12
$parts["layout"] = File.read $layoutDir + '/template.html'
13
$parts["404"] = File.read $styleDir + '/404.html'
14
$parts["footer"] = File.read $styleDir + '/footer.html'
15
$parts["header"] = File.read $styleDir + '/header.html'
16
$parts["sidebar"] = File.read $styleDir + '/sidebar.html'
17
18
#

19
# Load all the page parts in the parts directory.

20
#

21
Dir.entries($parts["Sitebase"] + '/parts/').select {|f|
22
	if !File.directory? f
23
		$parts[File.basename(f, ".*")] = File.read $parts["Sitebase"] + '/parts/' + f
24
	end
25
}
26
27
#

28
# Setup server defaults:

29
#

30
port = $parts["ServerAddress"].split(":")[2]
31
set :port, port

Bagian selanjutnya dari kode ini adalah untuk memuat item yang dapat disimpan dalam cache dari situs web. Ini adalah segalanya dalam gaya dan tata letak tema Anda, dan item di sub-direktori parts. Variabel global, $parts, pertama kali dimuat dari file server.json. Informasi itu kemudian digunakan untuk memuat item yang tepat untuk tata letak dan tema yang ditentukan. Mesin template Handlebars menggunakan informasi ini untuk mengisi template.

1
#

2
# Define the routes for the CMS.

3
#

4
get '/' do
5
  page "main"
6
end
7
8
get '/favicon.ico', :provides => 'ico' do
9
	File.read "#{$parts['Sitebase']}/images/favicon.ico"
10
end
11
12
get '/stylesheets.css', :provides => 'css'  do
13
	File.read "#{$parts["Sitebase"]}/css/final/final.css"
14
end
15
16
get '/scripts.js', :provides => 'js'  do
17
	File.read "#{$parts["Sitebase"]}/js/final/final.js"
18
end
19
20
get '/images/:image', :provides => 'image' do
21
	File.read "#{$parts['Sitebase']}/images/#{parms['image']}"
22
end
23
24
get '/posts/blogs/:blog' do
25
	post 'blogs', params['blog'], 'index'
26
end
27
28
get '/posts/blogs/:blog/:post' do
29
	post 'blogs', params['blog'], params['post']
30
end
31
32
get '/posts/news/:news' do
33
	post 'news', params['news'], 'index'
34
end
35
36
get '/posts/news/:news/:post' do
37
	post 'news', params['news'], params['post']
38
end
39
40
get '/:page' do
41
	page params['page']
42
end

Bagian selanjutnya berisi definisi untuk semua rute. Sinatra adalah server yang sesuai REST yang lengkap. Tapi untuk CMS ini, saya hanya akan menggunakan kata kerja get. Setiap rute mengambil item dari rute yang akan dikirimkan ke fungsi untuk menghasilkan halaman yang benar. Di Sinatra, sebuah nama yang didahului oleh sebuah kolon yang menentukan bagian dari rute yang akan dikirmkan ke handler rute. Item ini ada dalam tabel hash params.

1
#

2
# Various functions used in the making of the server:

3
#

4
5
#

6
# Function:    page

7
#

8
# Description: This function is for processing a page

9
#              in the CMS.

10
#

11
# Inputs:

12
# 				pg 	The page name to lookup

13
#

14
def page(pg)
15
	processPage $parts["layout"], "#{$parts["Sitebase"]}/pages/#{pg}"
16
end

Fungsi page mendapatkan nama halaman dari rute dan mengirimkan tata letak di variabel $parts bersamaan dengan path lengkap ke file halaman yang dibutuhkan untuk fungsi processPage. Fungsi processPage mengambil informasi ini dan membuat halaman yang tepat, yang kemudian dikembalikan. Di Ruby, keluaran dari fungsi terakhir adalah nilai kembalian untuk fungsinya.

1
#

2
# Function:    post

3
#

4
# Description: This function is for processing a post type

5
#              page in the CMS. All blog and news pages are

6
#    				post type pages.

7
#

8
# Inputs:

9
#              type   The type of the post

10
#              cat    The category of the post (blog, news)

11
#              post   The actual page of the post

12
#

13
def post(type, cat, post)
14
	processPage $parts["layout"], "#{$parts["Sitebase"]}/posts/#{type}/#{cat}/#{post}"
15
end

Fungsi post sama seperti fungsi page, kecuali berfungsi untuk semua halaman berjenis posting. Fungsi ini mengharapkan type posting, category posting, dan post itu sendiri. Ini akan membuat alamat untuk menampilkan halaman yang benar.

1
#

2
# Function:    figurePage

3
#

4
# Description: This function is to figure out the page

5
#              type (ie: markdown, HTML, jade, etc), read

6
#              the contents, and translate it to HTML.

7
#

8
# Inputs:

9
#              page      The address of the page 

10
#                        without its extension.

11
#

12
def figurePage(page)
13
	result = ""
14
15
	if File.exist? page + ".html"
16
		#

17
		# It's an HTML file.

18
		#

19
		result = File.read page + ".html"
20
	elsif File.exist? page + ".md"
21
		#

22
		# It's a markdown file.

23
		#

24
		result = Kramdown::Document.new(File.read page + ".md").to_html
25
26
		#

27
		# Fix the fancy quotes from Kramdown. It kills

28
		# the Handlebars parser.

29
		#

30
		result.gsub!("“","\"")
31
		result.gsub!("”","\"")
32
	elsif File.exist? page + ".amber"
33
		#

34
		# It's a jade file. Slim doesn't support

35
		# macros. Therefore, not as powerful as straight jade.

36
		# Also, we have to render any Handlebars first

37
		# since the Slim engine dies on them.

38
		#

39
		File.write("./tmp.txt",$hbs.compile(File.read page + ".amber").call($parts))
40
		result = Slim::Template.new("./tmp.txt").render()
41
	else
42
		#

43
		# Doesn't exist. Give the 404 page.

44
		#

45
		result = $parts["404"]
46
	end
47
48
	#

49
	# Return the results.

50
	#

51
	return result
52
end

Fungsi figurePage menggunakan fungsi processPage untuk membaca konten halaman dari sistem file. Fungsi ini menerima path lengkap ke file tanpa ekstensi. figurePage kemudian menguji untuk file dengan nama yang diberikan dengan ekstensi html untuk membaca file HTML. Pilihan kedua adalah untuk ekstensi md untuk file Markdown.

Terakhir, ia memeriksa ekstensi amber untuk file Jade. Ingat: Amber adalah nama perpustakaan untuk memproses file sintaks Jade di Go. Saya menyimpannya sama untuk antar-fungsionalitas. File HTML dikirimkan kembali, sementara semua file Markdown dan Jade dikonversi ke HTML sebelum dikembalikan.

Jika file tidak ditemukan, pengguna akan menerima halaman 404. Dengan cara ini, halaman "page not found" Anda terlihat seperti halaman lain kecuali isinya.

1
#

2
# Function:    processPage

3
#

4
# Description: The function processes a page by getting

5
#              its contents, combining with all the page

6
#              parts using Handlebars, and processing the

7
#              shortcodes.

8
#

9
# Inputs:

10
#              layout  The layout structure for the page

11
#              page    The complete path to the desired

12
#                      page without its extension.

13
#

14
def processPage(layout, page)
15
	#

16
	# Get the page contents and name.

17
	#

18
	$parts["content"] = figurePage page
19
	$parts["PageName"] = File.basename page
20
21
	#

22
	# Run the page through Handlebars engine.

23
	#

24
	begin
25
		pageHB = $hbs.compile(layout).call($parts)
26
	rescue
27
		pageHB = "

28
Render Error

29


30
"
31
	end
32
33
	#

34
	# Run the page through the shortcodes processor.

35
	#

36
	pageSH = processShortCodes pageHB
37
38
	#

39
	# Run the page through the Handlebar engine again.

40
	#

41
	begin
42
		pageFinal = $hbs.compile(pageSH).call($parts)
43
	rescue
44
		pageFinal = "

45
Render Error

46


47
" + pageSH
48
	end
49
50
	#

51
	# Return the results.

52
	#

53
	return pageFinal
54
end

Fungsi processPage melakukan semua perluasan template pada data halaman. Dimulai dengan memanggil fungsi figurePage untuk mendapatkan isi halaman. Kemudian memproses tata letak diteruskan ke sana dengan Handlebars untuk memperluas template.

Kemudian fungsi processShortCode akan menemukan dan memproses semua shortcode di halaman. Hasilnya kemudian diteruskan ke Handlebars untuk kedua kalinya untuk memproses macro yang ditinggalkan oleh shortcode. Pengguna menerima hasil akhirnya.

1
#

2
# Function:    processShortCodes

3
#

4
# Description: This function takes the page and processes

5
#              all of the shortcodes in the page.

6
#

7
# Inputs:

8
#              page     The contents of the page to 

9
#                       process.

10
#

11
def processShortCodes(page)
12
	#

13
	# Initialize the result variable for returning.

14
	#

15
	result = ""
16
17
	#

18
	# Find the first shortcode

19
	#

20
	scregFind = /\-\[([^\]]*)\]\-/
21
	match1 = scregFind.match(page)
22
	if match1 != nil
23
		#

24
		# We found one! get the text before it

25
		# into the result variable and initialize

26
		# the name, param, and contents variables.

27
		#

28
		name = ""
29
		param = ""
30
		contents = ""
31
		nameLine = match1[1]
32
		loc1 = scregFind =~ page
33
		result = page[0, loc1]
34
35
		#

36
		# Separate out the nameLine into a shortcode

37
		# name and parameters.

38
		#

39
		match2 = /(\w+)(.*)*/.match(nameLine)
40
		if match2.length == 2
41
			#

42
			# Just a name was found.

43
			#

44
			name = match2[1]
45
		else
46
			#

47
			# A name and parameter were found.

48
			#

49
			name = match2[1]
50
			param = match2[2]
51
		end
52
53
		#

54
		# Find the closing shortcode

55
		#

56
		rest = page[loc1+match1[0].length, page.length]
57
		regEnd = Regexp.new("\\-\\[\\/#{name}\\]\\-")
58
		match3 = regEnd.match(rest)
59
		if match3 != nil
60
			#

61
			# Get the contents the tags enclose.

62
			#

63
			loc2 = regEnd =~ rest
64
			contents = rest[0, loc2]
65
66
			#

67
			# Search the contents for shortcodes.

68
			#

69
			contents = processShortCodes(contents)
70
71
			#

72
			# If the shortcode exists, run it and include

73
			# the results. Otherwise, add the contents to

74
			# the result.

75
			#

76
			if $shortcodes.include?(name)
77
				result += $shortcodes[name].call(param, contents)
78
			else
79
				result += contents
80
			end
81
82
			#

83
			# process the shortcodes in the rest of the

84
			# page.

85
			#

86
			rest = rest[loc2 + match3[0].length, page.length]
87
			result += processShortCodes(rest)
88
		else
89
			#

90
			# There wasn't a closure. Therefore, just 

91
			# send the page back.

92
			#

93
			result = page
94
		end
95
	else
96
		#

97
		# No shortcodes. Just return the page.

98
		#

99
		result = page
100
	end
101
102
	return result
103
end

Fungsi processShortCodes mengambil teks yang diberikan, menemukan masing-masing shortcode, dan menjalankan shortcode yang ditentukan dengan argumen dan isi shortcode. Saya menggunakan shortcode routine untuk memproses isi shortcode juga.

Shortcode adalah tag mirip HTML yang menggunakan -[ dan ]- untuk membatasi tag pembuka dan tag penutup -[/ dan ]-. Tag pembuka berisi parameter untuk shortcode juga. Oleh karena itu, contoh shortcode adalah:

1
-[box]-
2
This is inside a box.
3
-[/box]-

Shortcode ini mendefinisikan shortcode box tanpa parameter dengan isi <p>This is inside a box.</p>. Shortcode box membungkus isinya dalam HTML yang sesuai untuk menghasilkan kotak di sekitar teks dengan teks yang terpusat di dalam kotak. Jika nanti Anda ingin mengubah bagaimana box itu di-render, Anda hanya perlu mengubah definisi shortcode. Ini menghemat banyak pekerjaan.

1
#

2
# Data Structure:  $shortcodes

3
#

4
# Description:     This data structure contains all

5
#                  the valid shortcodes names and the

6
#                  function. All shortcodes should

7
#                  receive the arguments and the

8
#                  that the shortcode encompasses.

9
#

10
$shortcodes = {
11
	"box" => lambda { |args, contents|
12
		return("#{contents}")
13
	},
14
   'Column1'=> lambda { |args, contents|
15
		return("#{contents}")
16
   },
17
   'Column2' => lambda { |args, contents|
18
      return("#{contents}")
19
   },
20
   'Column1of3' => lambda { |args, contents|
21
      return("#{contents}")
22
   },
23
   'Column2of3' => lambda { |args, contents|
24
      return("#{contents}")
25
   },
26
   'Column3of3' => lambda { |args, contents|
27
      return("#{contents}")
28
   },
29
   'php' => lambda { |args, contents|
30
      return("#{contents}") }, 'js' => lambda { |args, contents| return("#{contents}") }, "html" => lambda { |args, contents| return("#{contents}") }, 'css' => lambda {|args, contents| return("#{contents}") } }

Hal terakhir dalam file adalah tabel hash $shortcodes yang berisi rutinitas shortcode. Ini adalah shortcode sederhana, tapi Anda bisa membuat shortcode lain menjadi serumit yang Anda inginkan.

Semua shortcode harus menerima dua parameter: args and contents. String ini berisi parameter shortcode dan isi shortcode-nya. Karena shortcode berada di dalam tabel hash, saya menggunakan fungsi lambda untuk mendefinisikannya. Fungsi lambda adalah fungsi tanpa nama. Satu-satunya cara untuk menjalankan fungsi ini adalah dari array hash.

Menjalankan Server

Setelah Anda membuat file rubyPress.rb dengan isi di atas, Anda dapat menjalankan server dengan:

1
ruby rubyPress.rb

Karena kerangka Sinatra bekerja dengan struktur Ruby on Rails Rack, Anda dapat menggunakan Pow untuk menjalankan server. Pow akan menyiapkan file host sistem Anda untuk menjalankan server Anda secara lokal seperti pada situs host. Anda bisa menginstal Pow with Powder menggunakan perintah berikut di baris perintah:

1
gem install powder
2
powder install

Powder adalah rutin baris perintah untuk mengelola situs Pow di komputer Anda. Agar Pow bisa melihat situs Anda, Anda harus membuat soft link ke direktori proyek Anda di direktori ~/.pow. Jika server berada di direktori /Users/test/Documents/rubyPress, Anda akan menjalankan perintah berikut:

1
cd ~/.pow
2
ln -s /Users/test/Documents/rubyPress rubyPress

Ln -s membuat soft link ke direktori yang ditentukan terlebih dahulu, dengan nama yang ditentukan kedua. Pow kemudian akan membuat domain di sistem Anda dengan nama soft link. Pada contoh di atas, masuk ke situs web http://rubyPress.dev di browser akan memuat halaman dari server.

Untuk memulai server, ketik berikut ini setelah membuat soft link:

1
powder start

Untuk memuat ulang server setelah membuat beberapa perubahan kode, ketik berikut ini:

1
powder restart
rubyPress Main PagerubyPress Main PagerubyPress Main Page
Halaman Utama rubyPress

Halaman Utaman rubyPressPergi ke situs web di browser akan menghasilkan gambar di atas. Pow akan membuat situs di http://rubyPress.dev. Tidak peduli metode mana yang Anda gunakan untuk meluncurkan situs ini, Anda akan melihat halaman hasil yang sama.

Kesimpulan

Nah, Anda sudah melakukannya. CMS lainnya, tapi kali ini di Ruby. Versi ini adalah versi terpendek dari semua CMS yang dibuat dalam seri ini. Bereksperimenlah dengan kode dan lihat bagaimana Anda dapat memperluas kerangka dasar ini.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.