Ruby: Bisakah saya menulis string multi-line tanpa penggabungan?


397

Apakah ada cara untuk membuat ini terlihat sedikit lebih baik?

conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' +
          'from table1, table2, table3, etc, etc, etc, etc, etc, ' +
          'where etc etc etc etc etc etc etc etc etc etc etc etc etc'

Seperti, apakah ada cara untuk menyiratkan penggabungan?


28
Hati-hati dengan serangan injeksi SQL. :)
Roy Tinker

Jawaban:


595

Ada beberapa bagian dari jawaban ini yang membantu saya mendapatkan apa yang saya butuhkan (penggabungan multi-baris mudah TANPA spasi kosong tambahan), tetapi karena tidak ada jawaban aktual yang memilikinya, saya menyusunnya di sini:

str = 'this is a multi-line string'\
  ' using implicit concatenation'\
  ' to prevent spare \n\'s'

=> "this is a multi-line string using implicit concatenation to eliminate spare
\\n's"

Sebagai bonus, inilah versi menggunakan sintaks HEREDOC yang lucu (melalui tautan ini ):

p <<END_SQL.gsub(/\s+/, " ").strip
SELECT * FROM     users
         ORDER BY users.id DESC
END_SQL
# >> "SELECT * FROM users ORDER BY users.id DESC"

Yang terakhir sebagian besar akan untuk situasi yang membutuhkan lebih banyak fleksibilitas dalam pemrosesan. Saya pribadi tidak suka, itu menempatkan pemrosesan di tempat yang aneh wrt string (yaitu, di depannya, tetapi menggunakan metode contoh yang biasanya datang sesudahnya), tetapi ada di sana. Perhatikan bahwa jika Anda membuat indentasi END_SQLpengidentifikasi terakhir (yang umum, karena ini mungkin di dalam suatu fungsi atau modul), Anda harus menggunakan sintaks hyphenated (yaitu, p <<-END_SQLbukannyap <<END_SQL ). Jika tidak, spasi indentasi menyebabkan pengidentifikasi ditafsirkan sebagai kelanjutan dari string.

Ini tidak menghemat banyak pengetikan, tetapi terlihat lebih bagus daripada menggunakan tanda +, bagi saya.

Juga (saya katakan dalam edit, beberapa tahun kemudian), jika Anda menggunakan Ruby 2.3+, operator << ~ juga tersedia , yang menghilangkan lekukan ekstra dari string terakhir. Anda harus dapat menghapus .gsubdoa, dalam hal itu (meskipun mungkin tergantung pada lekukan awal dan kebutuhan akhir Anda).

EDIT: Menambahkan satu lagi:

p %{
SELECT * FROM     users
         ORDER BY users.id DESC
}.gsub(/\s+/, " ").strip
# >> "SELECT * FROM users ORDER BY users.id DESC"

2
Ini adalah pertanyaan lama TAPI ada kesalahan dalam jawaban atau telah terjadi perubahan sintaksis sejak saat itu. p <<END_SQLSeharusnya kalau p <<-END_SQLtidak, ini adalah jawabannya. opsional, Anda dapat menghapus spasi terkemuka dengan operator HEREDOC berlekuk,<<~END_SQL
jaydel

Ini hanya kesalahan jika pengidentifikasi akhir indentasi (tanda hubung memberitahu juru ruby ​​untuk memangkas spasi sebelum membuat penentuan pengenal akhir). Saya bisa menaruh catatan yang menyebutkan itu. Juga, ~ tidak diperlukan, gsub \ s + dan strip sudah menghapus spasi putih terkemuka.
A. Wilson

Menambah <<~jawaban akan menyenangkan, akhirnya meneliti itu dari sana. Secara pribadi, saya menggunakan <<~MSG.strip ... MSGyang juga strip yang terakhir \n.
Qortex

1
Ketika saya menulis jawaban ini (sembilan tahun yang lalu, sheesh!), Ruby berada di 1.9, dan << ~ (jelas) tidak diperkenalkan sampai 2.3. Pokoknya, di samping sejarah kuno, saya akan memasukkannya, terima kasih telah membawanya.
A. Wilson

Terima kasih telah menjadi salah satu dari sedikit jawaban yang tidak menambah baris baru, itulah yang saya coba hindari ketika saya menemukan pertanyaan ini.
Josh

174

Di ruby ​​2.0 sekarang Anda bisa menggunakannya %

Sebagai contoh:

SQL = %{
SELECT user, name
FROM users
WHERE users.id = #{var}
LIMIT #{var2}
}

14
Bekerja di Ruby 1.9.3 juga.
Andy Stewart

26
String yang dibuat dengan sintaksis ini akan menyertakan baris baru dan setiap indentasi yang ditambahkan ke baris berikutnya.
James

Ini bahkan lebih baik daripada << EOT ...... EOT (dokumen ini di sini)! itu juga melakukan interpolasi jika diperlukan.
Nasser

1
@Nasser A heredoc melakukan interpolasi juga.
Dana Gugatan Monica

3
Jika menggunakan Rails memohon squishpada output harus membantu.
Jignesh Gohel

167

Ya, jika Anda tidak keberatan baris baru yang dimasukkan:

 conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7
            from table1, table2, table3, etc, etc, etc, etc, etc,
            where etc etc etc etc etc etc etc etc etc etc etc etc etc'

Atau Anda dapat menggunakan heredoc :

conn.exec <<-eos
   select attr1, attr2, attr3, attr4, attr5, attr6, attr7
   from table1, table2, table3, etc, etc, etc, etc, etc,
   where etc etc etc etc etc etc etc etc etc etc etc etc etc
eos

87
Anda juga bisa menggunakan%Q(...)
BaroqueBobcat

3
@ Zombie: Baris baru biasanya diizinkan dalam pernyataan SQL dan hanya diperlakukan sebagai spasi putih biasa.
Mark Byers

2
lihat jawaban saya di bawah untuk contoh, Anda bisa menggunakan% sekarang.
Robbie Guilfoyle

4
Anda juga bisa menggunakan%(...)
zero-divisor

1
Sesuatu yang penting untuk diingat jika Anda sengaja menambahkan spasi kosong dan menggunakan salah satu solusi ini adalah bahwa editor Anda dapat secara otomatis menghapus ruang tambahan saat menyimpan file. Sementara saya biasanya lebih suka perilaku ini, itu telah menyebabkan masalah yang tidak terduga bagi saya beberapa kali. Solusinya adalah menulis string multi-line Anda seperti bagaimana OP lakukan dalam pertanyaan.
Dennis

50

Ada beberapa sintaks untuk string multi-line seperti yang sudah Anda baca. Favorit saya adalah Perl-style:

conn.exec %q{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
      from table1, table2, table3, etc, etc, etc, etc, etc,
      where etc etc etc etc etc etc etc etc etc etc etc etc etc}

String multi-baris dimulai dengan% q, diikuti oleh {, [atau (, dan kemudian diakhiri oleh karakter terbalik yang sesuai.% Q tidak memungkinkan interpolasi;% Q melakukannya Anda dapat menulis hal-hal seperti ini:

conn.exec %Q{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
      from #{table_names},
      where etc etc etc etc etc etc etc etc etc etc etc etc etc}

Saya sebenarnya tidak tahu bagaimana string multi-line ini disebut jadi mari kita sebut mereka Perl multilines.

Namun perhatikan bahwa apakah Anda menggunakan Perl multilines atau heredocs seperti yang disarankan Mark dan Peter, Anda akan berakhir dengan spasi putih yang mungkin tidak perlu. Baik dalam contoh saya dan contoh mereka, baris "dari" dan "di mana" berisi spasi putih terdepan karena lekukan mereka dalam kode. Jika spasi putih ini tidak diinginkan maka Anda harus menggunakan string gabungan seperti yang Anda lakukan sekarang.


4
dari # {table_names} tidak akan berfungsi dalam contoh ini, karena Anda menggunakan% q {}, itu akan berfungsi jika Anda menggunakan% q [] atau ()
MatthewFord

2
Favorit saya dalam nada ini adalah hanya% {string multi multiline dengan dukungan interpolasi}
Duke

string yang dihasilkan dari %qkeluarga akan mencakup baris baru yang tidak sepadan dengan kode asli.
Josh

29

Terkadang layak untuk menghapus karakter baris baru \nseperti:

conn.exec <<-eos.squish
 select attr1, attr2, attr3, attr4, attr5, attr6, attr7
 from table1, table2, table3, etc, etc, etc, etc, etc,
 where etc etc etc etc etc etc etc etc etc etc etc etc etc
eos

5
ini berdasarkan rel bukan ruby
a14m

23

Anda juga dapat menggunakan tanda kutip ganda

x = """
this is 
a multiline
string
"""

2.3.3 :012 > x
 => "\nthis is\na multiline\nstring\n"

Jika diperlukan untuk menghapus jeda baris "\ n" gunakan backslash "\" di akhir setiap baris


5
Anda dapat mencapai hasil yang sama dengan tanda kutip ganda tunggal. Tidak ada yang namanya kutip rangkap tiga di Ruby. Itu hanya menafsirkannya sebagai "" + "double quotes with some content" + "".
rakvium

Ya, tapi `" "+" \ n halo \ n "+" "
Memang

1
Ya, itu terlihat aneh, dan inilah sebabnya tidak ada alasan untuk menambahkan tanda kutip ganda ketika Anda bisa menggunakan tanda kutip tunggal tunggal dengan hasil yang sama.
rakvium

Ya, saya maksudkan tanda plus. Kutipan ganda tanpa itu terlihat baik-baik saja, mudah dibaca dan dan lebih mudah dikenali daripada kutipan tunggal, yang harus digunakan pada string baris tunggal.
juliangonzalez

1
Maksud saya itu hanya "x"terlihat lebih baik dan bekerja lebih cepat daripada """x"""(yang pada dasarnya sama dengan ""+"x"+"") atau """""x"""""(yang sama dengan "" + "" + "x" + "" + ""). Ini Ruby, bukan Python, tempat Anda menggunakan """alih-alih "saat Anda membutuhkan string multi-baris.
rakvium

15
conn.exec = <<eos
  select attr1, attr2, attr3, attr4, attr5, attr6, attr7
  from table1, table2, table3, etc, etc, etc, etc, etc,
  where etc etc etc etc etc etc etc etc etc etc etc etc etc
eos

1
menggunakan heredoc tanpa tanda '-', seperti pada '<< - eos', akan menyertakan spasi pemimpin tambahan. lihat respons Mark Byers.
ives

heredoc akan menyertakan baris baru yang tidak setara dengan kode asli.
Josh

15

Pilihan lain:

#multi line string
multiline_string = <<EOM
This is a very long string
that contains interpolation
like #{4 + 5} \n\n
EOM

puts multiline_string

#another option for multiline string
message = <<-EOF
asdfasdfsador #{2+2} this month.
asdfadsfasdfadsfad.
EOF

puts message

1
Haruskah berubah <<EOMmenjadi <<-EOM, bukan?
kingPuppy

Mungkin, sepertinya berhasil untuk <<-EOFcontoh saya . Dugaan saya adalah bahwa kedua cara bekerja.
Alex Cohen

heredoc akan menyertakan baris baru yang tidak setara dengan kode asli.
Josh

11

Baru-baru ini dengan fitur-fitur baru di Ruby 2.3 yang baru squiggly HEREDOCakan memungkinkan Anda menulis string multiline kami dengan cara yang baik dengan perubahan minimal sehingga menggunakan ini dikombinasikan dengan .squish(jika Anda menggunakan rel) akan memungkinkan Anda menulis multiline dengan cara yang bagus! dalam hal hanya menggunakan ruby, Anda dapat melakukan <<~SQL.split.join(" ")yang hampir sama

[1] pry(main)> <<~SQL.squish
[1] pry(main)*   select attr1, attr2, attr3, attr4, attr5, attr6, attr7
[1] pry(main)*   from table1, table2, table3, etc, etc, etc, etc, etc,
[1] pry(main)*   where etc etc etc etc etc etc etc etc etc etc etc etc etc
[1] pry(main)* SQL
=> "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 from table1, table2, table3, etc, etc, etc, etc, etc, where etc etc etc etc etc etc etc etc etc etc etc etc etc"

ref: https://infinum.co/the-capsized-eight/multiline-strings-ruby-2-3-0-the-squiggly-heredoc


squish adalah rel, bukan ruby
Josh

1
@ Astaga, ya kau benar, perbarui jawabannya, tepuk tangan.
Mark Jad

6
conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' <<
        'from table1, table2, table3, etc, etc, etc, etc, etc, ' <<
        'where etc etc etc etc etc etc etc etc etc etc etc etc etc'

<< adalah operator gabungan untuk string


2
+adalah operator gabungan reguler, <<adalah operator append tempat . Menggunakan efek samping pada literal kebetulan bekerja di sini (string pertama dimodifikasi dua kali dan kembali) tapi IMHO itu aneh dan membuat saya melakukan pengambilan ganda, di mana +akan sangat jelas. Tapi mungkin aku baru di Ruby ...
Beni Cherniavsky-Paskin

Ini tidak akan berfungsi jika frozen_string_literaldiaktifkan
Raido

6

Jika Anda melakukan pikiran spasi dan baris ekstra, Anda dapat menggunakan

conn.exec %w{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
  from table1, table2, table3, etc, etc, etc, etc, etc,
  where etc etc etc etc etc etc etc etc etc etc etc etc etc} * ' '

(gunakan% W untuk string interpolasi)


Saya sangat menyukai yang ini karena memungkinkan lebih banyak kombinasi penggunaan.
schmijos

1
Ini akan memadatkan beberapa ruang yang berdekatan menjadi satu. (Penindasan baris baru + lekukan berikut adalah kemenangan di sini tetapi di tengah-tengah garis itu mungkin mengejutkan.)
Beni Cherniavsky-Paskin

5

Untuk menghindari penutupan tanda kurung untuk setiap baris Anda cukup menggunakan tanda kutip ganda dengan garis miring terbalik untuk keluar dari baris baru:

"select attr1, attr2, attr3, attr4, attr5, attr6, attr7 \
from table1, table2, table3, etc, etc, etc, etc, etc, \
where etc etc etc etc etc etc etc etc etc etc etc etc etc"

Ini adalah salah satu dari sedikit jawaban di halaman ini yang sebenarnya menjawab pertanyaan!
Josh

4
conn.exec [
  "select attr1, attr2, attr3, ...",
  "from table1, table2, table3, ...",
  "where ..."
].join(' ')

Saran ini memiliki keunggulan dibandingkan dokumen-dokumen di sini dan string panjang yang dapat di-indentasi secara otomatis ke setiap bagian dari string dengan tepat. Tetapi itu datang dengan biaya efisiensi.


@ Aidan, Anda dapat mengganti koma dengan garis miring terbalik (a la C) dan tidak diperlukan join (atau array): Penerjemah akan menyatukan string pada waktu parse (saya pikir), membuatnya cukup cepat dibandingkan dengan sebagian besar alternatif. . Namun, satu keuntungan dari bergabung dengan serangkaian string adalah bahwa beberapa indentasi otomatis melakukan pekerjaan yang lebih baik daripada yang mereka lakukan dengan, misalnya, string di sini-doc atau dengan \.
Wayne Conrad

1
Satu catatan, sintaks heredoc << - akan memungkinkan indentasi yang sesuai.
A. Wilson

3

Ruby-way (TM) sejak Ruby 2.3: Gunakan HEREDOC berlekuk - lekuk<<~ untuk mendefinisikan string multi-line dengan baris baru dan lekukan yang tepat:

conn.exec <<~EOS
            select attr1, attr2, attr3, attr4, attr5, attr6, attr7
            from table1, table2, table3, etc, etc, etc, etc, etc
            where etc etc etc etc etc etc etc etc etc etc etc etc etc
          EOS

# -> "select...\nfrom...\nwhere..."

Jika indentasi yang tepat bukan masalah, maka kutipan tunggal dan ganda dapat menjangkau beberapa baris di Ruby:

conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 
           from table1, table2, table3, etc, etc, etc, etc, etc, 
           where etc etc etc etc etc etc etc etc etc etc etc etc etc"    

# -> "select...\n           from...\n           where..."

Jika tanda kutip tunggal atau ganda rumit karena itu akan membutuhkan banyak pelarian, maka persen notasi string literal % adalah solusi yang paling fleksibel:

conn.exec %(select attr1, attr2, attr3, attr4, attr5, attr6, attr7
            from table1, table2, table3, etc, etc, etc, etc, etc
            where (ProductLine = 'R' OR ProductLine = "S") AND Country = "...")
# -> "select...\n            from...\n            where..."

Jika tujuannya adalah untuk menghindari baris baru (yang menyebabkan HEREDOC berlekuk-lekuk, kutipan dan persen string literal akan menyebabkan), maka kelanjutan garis dapat digunakan dengan menempatkan backslash \sebagai karakter non-spasi putih terakhir dalam satu baris. Ini akan melanjutkan baris dan akan menyebabkan Ruby menyatukan Strings back to back (perhatikan ruang-ruang di dalam string yang dikutip):

conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' \
          'from table1, table2, table3, etc, etc, etc, etc, etc, ' \
          'where etc etc etc etc etc etc etc etc etc etc etc etc etc'

# -> "select...from...where..."

Jika Anda menggunakan Rails String.squishakan menghapus string ruang memimpin dan mengekor dan menciutkan semua spasi putih berurutan (baris baru, tab, dan semua) menjadi satu ruang:

conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 
           from table1, table2, table3, etc, etc, etc, etc, etc, 
           where etc etc etc etc etc etc etc etc etc etc etc etc etc".squish

# -> "select...attr7 from...etc, where..."

Keterangan lebih lanjut:

Sintaks Ruby HEREDOC

Notasi Dokumen Di Sini untuk karya Strings adalah cara untuk menunjuk blok panjang inline teks dalam kode. Ini dimulai dengan <<diikuti oleh String yang ditentukan pengguna (ujung terminator String). Semua baris berikut digabungkan sampai akhir Terminator String ditemukan di awal baris:

puts <<HEREDOC 
Text Text Text Text
Bla Bla
HEREDOC
# -> "Text Text Text Text\nBlaBla"

Terminator End of String dapat dipilih secara bebas, tetapi biasanya menggunakan sesuatu seperti "EOS" (End of String) atau sesuatu yang cocok dengan domain String seperti "SQL".

HEREDOC mendukung interpolasi secara default atau ketika terminator EOS dikutip ganda:

price = 10
print <<"EOS"  # comments can be put here
1.) The price is #{price}.
EOS
# -> "1.) The price is 10."

Interpolasi dapat dinonaktifkan jika terminator EOS dikutip satu kali:

print <<'EOS' # Disabled interpolation
3.) The price is #{price}.
EOS
# -> "3.) The price is #{price}."

Salah satu batasan penting <<HEREDOCadalah bahwa terminator End of String harus berada di awal baris:

  puts <<EOS 
    def foo
      print "foo"
    end
  EOS
EOS
#-> "....def foo\n......print "foo"\n....end\n..EOS

Untuk menyiasati ini, <<-sintaks telah dibuat. Ini memungkinkan terminator EOS untuk membuat indentasi membuat kode terlihat lebih bagus. Garis antara <<-terminator EOS dan masih digunakan dalam perpanjangan penuh termasuk semua lekukan:

puts <<-EOS # Use <<- to indent End of String terminator
  def foo
    print "foo"
  end
EOS
# -> "..def foo\n....print "foo"\n..end"

Sejak Ruby 2.3, kami sekarang memiliki HEREDOC berlekuk <<~menghapus spasi putih terkemuka:

puts <<~EOS # Use the squiggly HEREDOC <<~ to remove leading whitespace (since Ruby 2.3!)
  def foo
    print "foo"
  end
EOS
# -> "def foo\n..print "foo"\nend"

Baris dan garis kosong yang hanya berisi tab dan spasi diabaikan oleh << ~

puts <<~EOS.inspect 
  Hello

    World!
EOS
#-> "Hello\n..World!"

Jika tab dan spasi digunakan, tab dianggap sama dengan 8 spasi. Jika garis yang sedikit berlekuk berada di tengah-tengah tab, tab ini tidak dihapus.

puts <<~EOS.inspect
<tab>One Tab
<space><space>Two Spaces
EOS
# -> "\tOne Tab\nTwoSpaces"

HEREDOC dapat melakukan beberapa hal gila seperti mengeksekusi perintah menggunakan backticks:

puts <<`EOC`            
echo #{price}
echo #{price * 2}
EOC

Definisi HEREDOC String dapat "ditumpuk", yang berarti bahwa terminator EOS pertama (EOSFOO di bawah) akan mengakhiri string pertama dan memulai yang kedua (EOSBAR di bawah):

print <<EOSFOO, <<EOSBAR    # you can stack them
I said foo.
EOSFOO
I said bar.
EOSBAR

Saya tidak berpikir ada orang yang akan menggunakannya seperti itu, tetapi <<EOSini benar-benar hanya string literal dan dapat diletakkan di mana pun string biasanya dapat diletakkan:

def func(a,b,c)
  puts a
  puts b
  puts c
end

func(<<THIS, 23, <<THAT) 
Here's a line
or two.
THIS
and here's another.
THAT

Jika Anda tidak memiliki Ruby 2.3, tetapi Rails >=3.0 maka Anda dapat menggunakan String.strip_heredocyang melakukan hal yang sama<<~

# File activesupport/lib/active_support/core_ext/string/strip.rb, line 22
class String
  def strip_heredoc
    gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
  end
end

puts <<-USAGE.strip_heredoc # If no Ruby 2.3, but Rails >= 3.0
  This command does such and such.

  Supported options are:
    -h         This message
    ...
USAGE

Persen String Literal

Lihat RubyDoc untuk bagaimana menggunakan tanda persentase diikuti dengan string dalam kurung pasangan seperti %(...), %[...], %{...}, dll atau sepasang karakter non-alfanumerik seperti%+...+

Kata-kata terakhir

Terakhir, untuk mendapatkan jawaban atas pertanyaan awal "Apakah ada cara untuk menyiratkan penggabungan?" jawab: Ruby selalu menyiratkan penggabungan jika dua string (tunggal dan ganda dikutip) ditemukan kembali ke belakang:

puts "select..." 'from table...' "where..."
# -> "select...from table...where..."

Peringatannya adalah bahwa ini tidak bekerja lintas baris, karena Ruby menafsirkan akhir pernyataan dan garis konsekuen hanya string saja pada suatu garis tidak melakukan apa-apa.


1

Jawaban Elegan Hari Ini:

<<~TEXT
Hi #{user.name}, 

Thanks for raising the flag, we're always happy to help you.
Your issue will be resolved within 2 hours.
Please be patient!

Thanks again,
Team #{user.organization.name}
TEXT

Ada perbedaan dalam <<-TEXTdan<<~TEXT , yang pertama mempertahankan spasi di dalam blok dan yang terakhir tidak.

Ada opsi lain juga. Seperti penggabungan dll. Tapi yang ini lebih masuk akal secara umum.

Jika saya salah di sini, beri tahu saya bagaimana ...


heredoc akan menyertakan baris baru yang tidak setara dengan kode asli.
Josh

1

Seperti Anda, saya juga mencari solusi yang tidak termasuk baris baru . (Meskipun mereka mungkin aman dalam SQL, mereka tidak aman dalam kasus saya dan saya memiliki blok teks yang besar untuk ditangani)

Ini bisa dibilang sama jeleknya, tetapi Anda dapat backslash-escape baris baru dalam heredoc untuk menghilangkannya dari string yang dihasilkan:

conn.exec <<~END_OF_INPUT
    select attr1, attr2, attr3, attr4, attr5, attr6, attr7 \
    from table1, table2, table3, etc, etc, etc, etc, etc, \
    where etc etc etc etc etc etc etc etc etc etc etc etc etc
  END_OF_INPUT

Perhatikan bahwa Anda tidak dapat melakukan ini tanpa interpolasi (IE <<~'END_OF_INPUT') jadi berhati-hatilah.#{expressions}akan dievaluasi di sini, sedangkan mereka tidak akan di kode asli Anda. A. Jawaban Wilson mungkin lebih baik karena alasan itu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.