Langsung ke konten utama

Template Engine dan Fungsi dari Handle Post

Template Engine

Sebelumnya kita sudah membuat webserver dinamis yang di dalamnya dapat menjalankan program lain contohnya seperti berikut code ini:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    {%
        l = _QUERY_STRING['login']
        u = _POST['username']
        p = _POST['password']
        emit(l)
        if (u == 'aku') and (p == 'saya'):
            emit('Login successful')
        else:
            emit('Login failed')
        %}
</body>
</html>

Jadi, ketika webserver di jalankan maka baris kode yang ada didalam {%%} akan di eksekusi, sebelum di eksekusi baris kode tersebut perlu di ubah ke dalam baris kode yang dapat di eksekusi setelah itu  dapat di render, untuk mengubah atau membaca program tersebut maka dibuatlah sebuah class yang diberi nama Template, kodenya seperti code berikut :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import re
 
class Template():
    """Compile an text into a template function"""
 
    def __init__(self, text):
        self.delimiter = re.compile(r'{%(.*?)%}', re.DOTALL)
        self.tokens = self.compile(text)
 
    def kompile(self, text):
        tokens = []
        for index, token in enumerate(self.delimiter.split(text)):
            if index % 2 == 0:
                # plain string
                if token:
                    tokens.append((False, token.replace('%\}', '%}').replace('{\%', '{%')))
            else:
                # code block
                # find out the indentation
                lines = token.replace('{\%', '{%').replace('%\}', '%}').splitlines()
                # indent = 0
                # for l in lines:
                #     if l.strip():
                #         indent = min([len(l)- len(l.strip())])
                # print(indent)
                indent = min([len(l) - len(l.lstrip()) for l in lines if l.strip()])
                realigned = '\n'.join(l[indent:] for l in lines)
                tokens.append((True, compile(realigned, '<tempalte> %s' % realigned[:20], 'exec')))
        return tokens
 
    def render(self, context = None, **kw):
        """Render the template according to the given context"""
        global_context = {}
        if context:
            global_context.update(context)
        if kw:
            global_context.update(kw)
 
        # add function for output
        def emit(*args):
            result.extend([str(arg) for arg in args])
        def fmt_emit(fmt, *args):
            result.append(fmt % args)
 
        global_context['emit'] = emit
        global_context['fmt_emit'] = fmt_emit
         
 
        # run the code
        result = []
        for is_code, token in self.tokens:
            if is_code:
                exec(token, global_context)
            else:
                result.append(token)
        return ''.join(result)

Lalu kita akan membedah kodenya mulai dari awal, di posisi teratas terdapat kode import re, re merupakan kependekan dari Regular Expression, Regular expression (regex) adalah deretan karakter yang digunakan untuk pencarian string atau teks dengan menggunakan pola (pattern). Contohnya penulisan email ryanhadi123890@gmail.com, jika diperhatikan dengan teliti email memiliki semacam pola, setelah huruf dan angka akan di ikuti dengan tanda @ lalu di ikuti huruf lalu titik dan huruf lagi. jadi Regex ini memudahkan kita mencari string tertentu dalam text yang banyak.

Setelah kita mengimport modul yang akan digunakan selanjutnya mulai menulis kode yang di awali dengan membuat class lalu diberi nama Template, kemudian kita membuat constructor, constructor adalah method yang pertama kali dijalankan ketika class atau objek dibuat, pada python untuk membuat constructor cara menuliskannya seperti code berikut :

1
def __init__(self, text):

Lalu untuk membuat function atau method di python di awali dengen def kemudian dilanjutkan dengan nama methodnya lalu parameter  yang ada didalam kurung, karena method diatas merupakan constructor, maka nama methodnya harus _init_, di setiap class harus ada self untuk menjadi parameter pertamanya, dan juga ada parameter text yang dibuat untuk menampung data yang diterima dari luar class.

1
2
3
def __init__(self, text):
    self.delimiter = re.compile(r'{%(.*?)%}', re.DOTALL)
    self.tokens = self.kompile(text)

Kemudian didalam constructor kita membuat properti self.delimiter yang di isi dengan hasil compile dari Regular Expression dan juga membuat properti self.token yang di isi dengan hasil method kompile dengan input text, lalu kita lanjutkan dengan method kompile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def kompile(self, text):
    tokens = []
    for index, token in enumerate(self.delimiter.split(text)):
        if index % 2 == 0:
            # plain string
            if token:
                tokens.append((False, token.replace('%\}', '%}').replace('{\%', '{%')))
        else:
            # code block
            # find out the indentation
            lines = token.replace('{\%', '{%').replace('%\}', '%}').splitlines()
            # indent = 0
            # for l in lines:
            #     if l.strip():
            #         indent = min([len(l)- len(l.strip())])
            # print(indent)
            indent = min([len(l) - len(l.lstrip()) for l in lines if l.strip()])
            realigned = '\n'.join(l[indent:] for l in lines)
            tokens.append((True, compile(realigned, '<tempalte> %s' % realigned[:20], 'exec')))
    return tokens

Disini method kompile menerima parameter self dan text, kemudian kita membuat array kosong lalu di beri nama

1
tokens = []

Kemudian lakukan perulangan seperti code berikut :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for index, token in enumerate(self.delimiter.split(text)):
 
    if index % 2 == 0:
        # plain string
        if token:
            tokens.append((False, token.replace('%\}', '%}').replace('{\%', '{%')))
    else:
        # code block
        # find out the indentation
        lines = token.replace('{\%', '{%').replace('%\}', '%}').splitlines()
        indent = min([len(l) - len(l.lstrip()) for l in lines if l.strip()])
        realigned = '\n'.join(l[indent:] for l in lines)
        tokens.append((True, compile(realigned, '<tempalte> %s' % realigned[:20], 'exec')))
return tokens

Code perulangan yang ada diatas digunakan untuk mendapatkan index dan tokenindex merupakan index dari text yang sudah di compile menggunakan Regular Expression, sehingga text berubah menjadi array dan memiliki index yang ambil oleh index dan token mengambil isi dari array, kemudian di lakukan pengecekan jika index merupakan modulus 2 dan juga ada token di dalamnya atau tidak kosong maka token akan dikirim apa adanya, dan ketika index bukan modulus 2 atau ganjil maka token akan di splitlines() kemudian di hilangkan tab atau indentasi karena pada python indentasi atau spasi sangat berpengaruh sekali, setelah disesuaikan tabnya maka diakukan realign atau penyusunan ulang setelah itu di kirim.

Setelah kode sudah di lakukan compile maka selanjutnya akan dilakukan eksekusi, method yang menjalankan eksekusi diberi nama render() yang memiliki parameter self dan context yang di inisialisasikan dengan None, kodenya seperti berikut :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def render(self, context = None, **kw):
        """Render the template according to the given context"""
        global_context = {}
        if context:
            global_context.update(context)
        if kw:
            global_context.update(kw)
 
        # add function for output
        def emit(*args):
            result.extend([str(arg) for arg in args])
        def fmt_emit(fmt, *args):
            result.append(fmt % args)
 
        global_context['emit'] = emit
        global_context['fmt_emit'] = fmt_emit
         
 
        # run the code
        result = []
        for is_code, token in self.tokens:
            if is_code:
                exec(token, global_context)
            else:
                result.append(token)
        return ''.join(result)

Kemudian buat lib baru yang bernama global_context, lalu dilakukan pengecekan ketika context memiliki isi, maka global_context akan diupdate dengan context lalu membuat function untuk mengeksekusi program

1
2
3
4
def emit(*args):
    result.extend([str(arg) for arg in args])
def fmt_emit(fmt, *args):
    result.append(fmt % args)

Kemudian didalam global_context di masukan code berikut :

1
2
global_context['emit'] = emit
global_context['fmt_emit'] = fmt_emit

Lalu dilakukan pengecekan jika self.token merupakan kode maka akan di eksekusi

1
2
3
4
5
6
result = []
for is_code, token in self.tokens:
    if is_code:
        exec(token, global_context)
    else:
        result.append(token)

Handle Post

Kemudian didalam webserver dinamis yang kemarin dibuat juga ada fungsi tambahan untuk menangani post, jadi ketika clien mengirim method post maka akan di handle oleh function handle post untuk kodenya sebagai berikut :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def handle_post(url, http_version, data):
    url = "htdocs/%s"%(url)
    if os.path.exists(url) and not os.path.isdir(url):
        response_line = b''.join([http_version.encode(), b'200', b'OK'])
        content_type = mimetypes.guess_type(url)[0] or 'text/html'
        entity_header = b''.join([b'Content-type: ', content_type.encode()])
        file = open(url, 'r')
        html = file.read()
        file.close()
        template = Template(html)
        _POST = {}
        for x in data.split('&'):
            y = x.split('=')
            _POST[y[0]]=y[1]
        print(_POST)
        context = {
            '_POST' : _POST
        }
        message_body = template.render(context).encode()
    else :
        response_line = b''.join([http_version.encode(), b'404', b'Not Found'])
        entity_header = b'Content-Type: text/html'
        message_body = b'<h1>404 Not Found</h1>'
    crlf = b'\r\n'
    response = b''.join([response_line, crlf, entity_header, crlf, crlf, message_body])
    return response

Dikarenakan ada handle post maka terjadi perubahan pada handle request

1
2
3
4
5
6
if(method == 'GET'):
    response = handle_get(url, http_version)
elif(method == 'POST'):
    data = request_message[len(request_message)-1]
    response = handle_post(url, http_version,data)
return response

Dikarenakan handle post mengirim data, maka data tersebut dapat diambil pada request_line yang terakhir lalu dikirim sebagai parameter pada handle post untuk diproses, berikut kode penerapan method post

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <form action="ceklogin.html" method="POST">
        <input type="text" name="username" >
        <input type="password" name="password" >
        <input type="submit" value="Login">
    </form>
</body>
</html>

Kode diatas merupakan form login dan ketika tombol submit ditekan maka akan mengirim request ke server dengan method POST, lalu halaman akan di alihkan ke halaman ceklogin.html, proses yang terjadi pada handle_post hampir sama dengan handle_get yang membedakan adalah adanya data yang dikirim, ketika menekan tombol submit yang terjadi adalah browser mengirim method POST dengan url ceklogin.html, data username dan  password yang kemudian data ini dimasukan kedalam context dengan kode perulangan di bawah ini :

1
2
3
for x in data.split('&'):
    y = x.split('=')
    _POST[y[0]]=y[1]

lalu data yang sudah dimasukan tadi dirender dan diubah ke bentuk byte yang selanjutnya disimpan ke massage_body untuk di return.

Komentar

Postingan populer dari blog ini

Membuat Halaman Profil Konsumen Dengan NextJS & Bulma

Apa itu Next.js?  Next.js merupakan sebuah React framework yang dibentuk buat mengatasi kasus client-side rendering yang dimiliki React. Sebuah halaman website yang dibentuk memakai React ‘terasa ringan’ lantaran tampilan website sangat interaktif.  Selain itu, ketika data berubah, React menggunakan efisien akan mengupdate bagian menurut halaman website yang memang perlu diupdate tanpa perlu reload satu halaman penuh.  Untuk menerima itu seluruh, client wajib load seluruh file JavaScript sebelum konten halaman ditampilkan. apabila file JS relatif akbar maka ketika yang diperlukan buat load pertama kali pula sebagai lebih lama.  Masalah lain menurut client-side rendering merupakan SEO, terdapat kemungkinan web crawler berusaha mengindex halaman yang belum terselesaikan dirender sepenuhnya (lantaran ketika load yang lama). Dan menduga web tadi blank.  Kedua kasus diatas bisa diselesaikan menggunakan teknik pre-rendering. Yaitu halaman HTML dan file JavaScript pada...

Membuat Database Menggunakan Aplikasi Excel ~ Part 4

Pengertian Normalisasi Database Normalisasi database adalah proses pengelompokan atribut data yang membentuk entitas sederhana, nonredundant, fleksibel, dan mudah beradaptasi. Sehingga dapat dipastikan bahwa database yang dibuat berkualitas baik. Tujuan Normalisasi Database Tujuan utama dari normalisasi database adalah: Menghilangkan dan mengurangi redudansi data. Memastikan dependensi data (data berada pada tabel yang tepat). Jika sebuah database belum dinormalisasi, kemungkinan terburuk yang akan merugikan sistem adalah: INSERT Anomali Situasi dimana tidak memungkinkan untuk memasukkan beberapa jenis data secara langsung di database. UPDATE Anomali Situasi dimana nilai yang diubah mengakibatkan ketidakkonsistenan database, artinya data yang diubah tidak sesuai dengan yang diinginkan. DELETE Anomali Penghapusan data tidak sesuai yang diharapkan, artinya data yang seharusnya tidak terhapus malah terhapus. Pada proses normalisasi terhadap tabel pada database dapat dilakukan dengan tiga ...

Design Sprint Alur Fitur Dari Wlijo

Design Sprint Fitur Wlijo      Wlijo adalah platform yang menghubungankan antara petani, pedagang sayur dan konsumen melalui aplikasi belanja sayur online. Saat ini wlijo masih dalam tahap pengembangan dan perbaikan. Untuk membantu proses pengembangan wlijo agar bisa selesai sesuai waktu yang ditargetkan diperlukannya sebuah metode efektif yakni metode design sprint. Apa Itu Metode Desain Sprint ?     Design sprint merupakan sebuah metode 5 langkah kerja untuk membantu perusahaan agar dapat membuat suatu produk berdasarkan design thinking dengan waktu lima hari. Design thinking sendiri merupakan proses yang mencakup proses strategis, kognitif, dan praktis. Design sprint sendiri dibuat pada tahun 2010 oleh seseorang yang berasal dari Google Venture bernama Jake Knapp. Sampai saat ini, sudah banyak perusahaan yang memakai design tersebut sampai keseluruh dunia.      Konsep kerjanya yaitu menggunakan waktu selama lima hari untuk membangun suatu ...