Forum: Projeler turna RSS
Yeni Bir Başlangıç: newHtmlHelper
Kadir Can #1
Üye Haz 2010 tarihinden beri · 413 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: Yeni Bir Başlangıç: newHtmlHelper
Merhaba arkadaşlar;
Daha önce söylediğim üzere helper yazımına yeniden başladım.Önceki kodlarla pek bir farkı yok, sadece biraz daha kontrollü ve hataları en aza indirmeyi amaçlıyorum.
module system.helper.newHtmlHelper;
 
class XmlElement
{
    const (char)[] _htmlTag;
    const (char)[][ const (char)[] ] _attributes;
    const (char)[] _content;
    
    this( const (char)[] tag)
    in
    {
        assert( tag.length > 0, "Tag can't be empty.");    
    }
    body
    {
        _htmlTag = tag;
    }
    
    this(const (char)[] tag, const(char)[][ const (char)[] ] attributes, const (char)[] content)
    in
    {
        assert( (tag.length > 0), "Tag can't be empty." );
    }
        
    body{
        _htmlTag = tag;
        _attributes = attributes;
        _content = content;
    }
    
    @property void setAttributes( const (char)[][] keys, const (char)[][] values )
    in
    {
        assert( (keys.length == values.length ), "Length of the keys and values must be same." );
    }
    body
    {
        foreach(counter, key; keys ) {
    _attributes[key] = values[counter];
    }
    }
    
    @property setContent( const (char)[] content )
    {
        _content = content;
    }
    
    override string toString()
    {
        string result;
    result ~= "<" ~ _htmlTag ~ " ";
    foreach(key, value; _attributes) {
            result ~= key ~ "=" ~ value;
    }
    result ~= ">" ~ _content ~ "</" ~ _htmlTag ~ ">";
    return result;
    }
}

Kodlar yukarıdaki gibi.Şimdilik pek bir değişiklik yok ama önceki kodları silip baştan başladım.En kısa zamanda yorum satırlarını ve birim testleri de ekleyeceğim.Şu haliyle kontrol ettim, bir hata ile karşılaşmadım.Yorumlarınızı bekliyorum.


Bir de aklıma bir şey takıldı.Daha önce XmlElement sınıfı içinde bir dinamik dizi tanımlayıp elemanları orada topluyorduk. Şimdi ise tasarımı tamamen ayırmak için başka bir modulde HtmlPage isimli bir sınıf oluşturup elemanları orada toplamayı düşünüyorum.Böylece hem daha ayrık olacak, hem de mümkün olduğunca gerçeğe daha yakın olacak.
acehreli (Moderatör) #2
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4391 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
O zaman, tekrar iyi eğlenceler! :)

Kadir Can:
    const (char)[] _htmlTag;
// ...
    this( const (char)[] tag)

Ben o const kullanımlarının doğru olduklarını düşünüyorum. Bu sınıf string türündeki bir tag'le de kullanılabilir, dinamik olarak oluşturulan dizgilerle de kullanılabilir. Buna rağmen bizden başka D programcıları öyle yapmadıkları için göze garip geliyor.

(Yukarıdaki paragrafı yazarken biraz daha düşündüm ve nedenini anladım (veya hatırladım?).)

Kurucuda const(char) dizisi alması tamam: Bu sınıf, "bana istediğiniz değişmezlikte dilim verebilirsiniz; karakterlerini değiştirmeyeceğim" demiş oluyor. Yani hem char dilimi hem de immutable(char) dilimi alabilir. Güzel...

Ancak, aldığı dilimi üyesi olarak saklama konusunda bir karar vermeli:

  • Verilen dilim immutable(char) ise kendimiz de değiştirmeyeceğize göre parametrenin const(char) olmasında sorun yok.

  • Verilen dilim değişebilen char ise bize verenin de değiştirmeyeceğinden emin olamıyoruz. O yüzden ya kurucu parametresi olarak immutable(char) türünde ısrar etmeliyiz, ya da aldığımız dilimi üyeye kendimiz kopyalamalıyız. İşte ancak onu yaptığımız zaman sağlamdayız.

Kimse aşağıdaki gibi kod yazmaz herhalde ama aşağıdaki kodda sorun var (bütün XmlElement'lar için aynı dizgiyi kullanıyor):

import std.conv;
import std.stdio;
 
void main()
{
    XmlElement[] elemanlar;
 
    char[] tag = "tag_".dup;
 
    foreach (i; 0 .. 3) {
        tag[3] = to!char('A' + i);
        elemanlar ~= new XmlElement(tag);
    }
 
    writeln(elemanlar);
}

Bütün _htmlTag'ler aynı çıkıyor:

[<tagC ></tagC>, <tagC ></tagC>, <tagC ></tagC>]

O yüzden buradaki kurucu işlev gibi işlevlerde yine de string almak en doğrusu. Böylece üye de string yapılır ve kod daha temiz görünür:

    string _htmlTag;
// ...
    this(string tag)

Şu bölümün sonundaki "Nasıl kullanmalı" ve "Özet" başlıklarındaki ilkelere yine de katılıyorum:

  http://ddili.org/ders/d/const_ve_immutable.html

Ama şöyle bir ek yapmak istiyorum:

  • Referans türündeki bir parametre bir üyeye bağlı olacaksa ve parametrenin değişmesi sorun oluşturacaksa parametreyi immutable referans olarak alın. (Yani dizgiler 'string' yazmak en iyisi.)

Aslında şu da olur:

  • Daha kullanışlı olsun diye parametreyi const(char) yapın ama kendiniz .idup ile kopyalayın:

    string _htmlTag;
// ...
    this(const(char)[] tag)
    {
        _htmlTag = tag.idup;    // <-- Bazen gereksizce YAVAŞ
    }

Bu da iyi gibi görünüyor ama bu hız açısından daha yavaş olabilir çünkü çağıranın elinde zaten string olduğunda bile .idup ile kopyalamış oluyoruz. O string parametre ve string üye en iyisi. Hatta pure dizgilerin immutable(char) olmayan dönüş değerleriyle bile kullanılabiliyor:

    string _htmlTag;
// ...
    this(string tag)    // <-- önerilen
 
// ...
 
char[] tagYap(size_t i) pure    // <-- pure çok yararlı
{
    char[] tag = "tag_".dup;
    tag[3] = to!char('A' + i);
    return tag;
}
 
// ...
 
        elemanlar ~= new XmlElement(tagYap(i));

tagYap() daha kullanışlı olsun diye dönüş türünü char dilimi yaptığı halde bizim string'de ısrarlı kurucu işlevimizle kullanılabiliyor. Bunun nedeni, pure işlevlerin dönüş türleri otomatik olarak immutable'a dönüşebiliyor çünkü pure bir işlevin dış dünyayla parametreleri dışında ilişkisi olmadığı için döndürdüğü dizginin taze bir dizgi olduğundan herkes emin. Yani döndürülen dizgi başkası tarafından sonradan değiştirilemez.

Kadir Can, bu konuyu daha da iyi anlamama yardım ettiğin için teşekkürler! :) (Şaka değil: bunu tam olarak anlayanlar çok değil. Başka forumlarda da hep string yazıyorlar ama sorgulayarak ve anlayarak yaptıklarını sanmıyorum.)

XmlElement sınıfı içinde bir dinamik dizi tanımlayıp elemanları orada topluyorduk.

Ama XML elemanları başka elemanlar içerebilirler. Daha önceki tasarımdaki dizi bu elemanın alt elemanları içindi.

Ali
Kadir Can #3
Üye Haz 2010 tarihinden beri · 413 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
acehreli:
Aslında şu da olur:
  • Daha kullanışlı olsun diye parametreyi const(char) yapın ama kendiniz .idup ile kopyalayın:

Aslında bana en mantıklı bu göründü.Daha önceki tartışmalarımızdan hatırlayacağınız gibi kullanıcıyı string vermek zorunda bırakmayı istememiştik.Ben de burada onu dikkate aldım.Aslında _htmlTag değişkeninin değişmez olması gerektiğinde hem fikiriz, çünkü dalgınlıkla değiştirilmesi hataya sebep olabilir.const parametrede değişmezliğin garantilendiğini, her tür char[] dizisini kabul edeceğini belirtiyor.

acehreli:
Bu da iyi gibi görünüyor ama bu hız açısından daha yavaş olabilir çünkü çağıranın elinde zaten string olduğunda bile .idup ile kopyalamış oluyoruz. O string parametre ve string üye en iyisi. Hatta pure dizgilerin immutable(char) olmayan dönüş değerleriyle bile kullanılabiliyor.

Burada asıl sorun hız ile işlevin parametre rahatlığı arasındaki kararsızlık oluyor.Eğer rahatlık o kadar da önemli değilse pure ile birlikte string üye ve string parametre kullanmak daha pratik olur.


acehreli:
Ama XML elemanları başka elemanlar içerebilirler. Daha önceki tasarımdaki dizi bu elemanın alt elemanları içindi.

Bir an aklımdan çıkmış. :) Bence değişken adı olarak başka bir isim kullanmalıyız.Çünkü elements denilince aklıma bütün html sayfasını bir nesnede tutmak geliyor.
acehreli (Moderatör) #4
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4391 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Kadir Can:
değişken adı olarak başka bir isim kullanmalıyız.

subElements veya children olabilir.

Ali
Kadir Can #5
Üye Haz 2010 tarihinden beri · 413 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Birim testleri ekledim; şu an için sorunsuz geçiyor. :)
const (char)[] ifadelerini string ile değiştirdim.
Şu an addSubElements() işlevinin içinde sadece _subElements'e ekleme yapılıyor, burası için farklı planlarım var, o yüzden birim testlere eklemedim.
İşte kodumuz:
module system.helper.htmlHelper;
import std.conv;
class XmlElement
{
    string _htmlTag;
    string[ string ] _attributes;
    string _content;
    char[] _firstPiece;
    char[] _lastPiece;
    XmlElement[] _subElements;
    
    this( string tag)
    {
        _htmlTag = tag;
    createFirstPiece();
    _lastPiece ~= to!(char[])("</" ~ tag ~ ">");
    }
    
    this(string tag, string[string] attributes, string content)
    in
    {
        assert( (tag.length > 0), "Tag can't be empty" );
    }
        
    body{
        _htmlTag = tag;
    createFirstPiece();
    _lastPiece ~= "</" ~ _htmlTag ~ ">";
        _attributes = attributes;
        _content = content;
    }
    
    void createFirstPiece()
    {
        string result ="<" ~ _htmlTag ~ " ";
        foreach( key, value; _attributes ){
            result ~= key ~ "=" ~ value ~ " ";
    }
    result ~= ">";
    _firstPiece = to!(char[])(result);
    }
    
    @property void setAttributes(string[] keys, string[] values)
    in
    {
        assert( (keys.length == values.length ), "Length of the keys and values must be same." );
    }
    body
    {
        foreach(counter, key; keys ) {
    _attributes[key] = values[counter];
    }
    createFirstPiece();
    }
    
    @property setContent( string content )
    {
        _content = content;
    }
    
    void addSubElement( XmlElement[] subElements )
    {
        _subElements ~= subElements;
    }
    
    override string toString()
    {
    return (_firstPiece ~ _content ~ _lastPiece).idup;
    }
}
 
unittest
{
    auto newElement = new XmlElement("a");
    assert(newElement._firstPiece == "<a >");
    assert(newElement._lastPiece == "</a>");
    newElement.setAttributes(["href"], ["www.ddili.org"]);
    assert(newElement._firstPiece == "<a href=www.ddili.org >");
    assert(newElement._lastPiece == "</a>");
    newElement.setContent("Ddili.org");
    assert(newElement._firstPiece == "<a href=www.ddili.org >");
    assert(newElement._lastPiece == "</a>");
    assert(newElement._content == "Ddili.org");
    assert(newElement.toString() == "<a href=www.ddili.org >Ddili.org</a>");
}

Git'e de gönderiyorum.İsterseniz pull request de atabilirim.
acehreli (Moderatör) #6
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4391 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Biraz tasarım sohbeti olsun diye :): _firstPiece ve _lastPiece'i en başından başlayarak hep doğru olarak hazırda bulunduruyorsun. Nitelik filan eklendiğinde nesnenin tutarlı olması için değerlerini tekrar hesaplıyorsun, vs.

Oysa onlar yalnızca en sonda ve toString() içinde gerekiyorlar. Şu iki seçeneği de gözden geçirmişsindir:

  • _firstPiece ve _lastPiece üyelerini boşverip toString() her çağrıldığında bütün dizgiyi hesaplamak. Böylece XmlElement nesneleri biraz daha küçülmüş olurlar.

  • _firstPiece ve _lastPiece'i boş olarak tutmak ve toString() çağrıldığında boş iseler hesaplamak. Yani, boş olmaları bir anlamda "değerlerinin hesaplanması" gerekiyor demek oluyor. Her setAttributes() yapıldığında da bu üyeleri boşaltabilirsin.

Hmmm... aslında _lastPiece hiç değişmeyeceğine göre bu söylediklerim _firstPiece için geçerli. Yani onu tembel olarak hesaplamayı düşünebilirsin. Dediğim gibi, o kadar önemli bir konu değil; amaç sohbet... :)

addSubElements() de bana D'nin variadic function olanağını hatırlattı. Parametrenin isminden sonra ... eklenirse o işlev XmlElement dizisiyle de çağrılabilir, tek XmlElement ile de, ayrı  XmlElement parametreleriyle de:

void foo(int[] parametreler...)
{
    // ...
}
 
void main()
{
    foo([1, 2]);   // Dizi
    foo(3);        // Tek parametre
    foo(4, 5);     // Birden fazla parametre
}

Oradaki bütün çağrılar derlenir. Tabii tek XmlElement gönderilirse addSubElements()'in ismi çoğul olduğundan ufak bir uyuşmazlık oluyor. Ne yapalım... :)

Ali
Kadir Can #7
Üye Haz 2010 tarihinden beri · 413 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
_firstPiece ve _lastPiece'i tanımlamaktaki amacım aslında biraz daha farklı.Şu an için o aşamaya yeni geldim, bugün yorulduğum için de kodlayamadım.Şöyle anlatayım:
Ana elemanın _firstPiece üyesi
+++>1.Alt elemanın _firstPiece üyesi
+++++>2. Alt elemanın _firstPiece üyesi
------>2. Alt elemanın _lastPiece üyesi
--->1. Alt elemanın _lastPiece üyesi
Ana elemanın _lastPiece üyesi

Dediğiniz gibi sürekli doğru halde bulundurmak verimsizliğe neden olabilir.Ben biraz farklı düşünmüştüm.Şu anki toString() işlevi alt elemanları desteklemiyor.Çok sayıda alt eleman olursa hepsini bir anda işlemeye çalışmak yavaş olur diye kuruldukları andan itibaren o kısımlar hazır olursa toString() daha hızlı çalışabilir gibi geliyor.Aslında createFirstPiece() işlevini sadece ilgili üyelerde değişiklik olduğu zaman o işlevleri çağırmak gibi bir şansımız varsa daha güzel olur.Ama sanırım bu istek biraz fazla çünkü getireceği yük daha fazla olacaktır.

Düzeltme:Bu mesajdaki kodda hata var: 2. alt eleman aslında 1. alt elemanın alt elemanı, o yüzden Ali Bey'in dediği şekle getirerek daha düzenli olmasını sağlayacağım.
Bu mesaj Kadir Can tarafından değiştirildi; zaman: 2012-06-24, 09:25.
Avatar
Salih Dinçer #8
Üye Ock 2012 tarihinden beri · 1880 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Pratik bir kodlama, eline sağlık Kadir...

Bu arada, gerek XML'de, gerekse HTML'de bazı tag'lerin _lastPiece olmadığını belirtmeliyim. Örneğin sıklıkla kullandığımız satır başı için <br /> kullanırız. Ayrıca <img>'deki istisnadan dolayı sonu bu şekilde bitirilmediği gibi gördüğüm kadarıyla _lastPiece'i de yoktur. Hatta bazı platformlarda (örn. .aspx'de) <br /> şeklinde bitirmek XML içeriğe ters düştüğünden sayfa render olmayabiliyor.

Şimdi aklıma geldi paragraf tag'i olan <p> de tek başına kullanılabiliyor. Tabi bu W3 standartlarına uyduğundan mı, yoksa browser'ların kabülenmesi/anlaması mevzusu mu emin değilim. Şimdi kodlara baktım, şunlarda da _lastPiece yok ve büyüktür işaretininde önce taksim işareti olabiliyor:
  • base
  • doctype
  • meta
  • link

Ayrıca tek satırlık style ile script'lerde ve listeleme tag'lerinde de olmayabiliyor...
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Kadir Can #9
Üye Haz 2010 tarihinden beri · 413 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Teşekkürler Salih.
Bildiğim kadarıyla o tag'lerin _lastPiece sahibi kullanımları da yasal.Ancak ileride kullanımı pratikleştirmek adına söylediğin şekle getireceğim.

Şu anda form kısmını yazmaya başladım.Kodları bittiğinde göndereceğim.
Bu mesaj 2 defa değişti; son değiştiren: Kadir Can; zaman: 2012-06-24, 09:24.
acehreli (Moderatör) #10
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4391 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Kadir Can:
_firstPiece ve _lastPiece ile ilgili

Değindiğimiz karmaşıklıkları göze alınca bence gerek yok.

Ali
Kadir Can #11
Üye Haz 2010 tarihinden beri · 413 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Fikriniz için teşekkürler Ali Bey.Ben en baştan beri kafamda yanlış kurmuşum.İki önceki mesajımda nedenini açıkladım.Aslında toString() özyinelemeli olmalı; çünkü alt elemanların da alt elemanları olabilir.Şimdi düzeltip gönderiyorum.
Kadir Can #12
Üye Haz 2010 tarihinden beri · 413 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Dediğim şekilde düzelttim.Birim testleri geçiyor. :)

module system.helper.htmlHelper;
import std.conv;
class XmlElement
{
    string _htmlTag;
    string[ string ] _attributes;
    string _content;
    char[] _firstPiece;
    char[] _lastPiece;
    XmlElement[] _subElements;
    
    this( string tag)
    {
        _htmlTag = tag;
    createFirstPiece();
    _lastPiece ~= to!(char[])("</" ~ tag ~ ">");
    }
    
    this(string tag, string[string] attributes, string content)
    in
    {
        assert( (tag.length > 0), "Tag can't be empty" );
    }
        
    body{
        _htmlTag = tag;
    _lastPiece ~= "</" ~ _htmlTag ~ ">";
        _attributes = attributes;
    createFirstPiece();
        _content = content;
    }
    
    void createFirstPiece()
    {
        string result ="<" ~ _htmlTag ~ " ";
        foreach( key, value; _attributes ){
            result ~= key ~ "=" ~ value ~ " ";
    }
    result ~= ">";
    _firstPiece = to!(char[])(result);
    }
    
    @property void setAttributes(string[] keys, string[] values)
    in
    {
        assert( (keys.length == values.length ), "Length of the keys and values must be same." );
    }
    body
    {
        foreach(counter, key; keys ) {
    _attributes[key] = values[counter];
    }
    createFirstPiece();
    }
    
    @property setContent( string content )
    {
        _content = content;
    }
    
    void addSubElement( XmlElement[] subElements ... )
    {
    foreach(element; subElements) {
            _subElements ~= element;
    }
    }
    
    override string toString()
    {
    string result;
        result ~= _firstPiece;
    if(_subElements is null) {
        result ~= _content ~ _lastPiece;
    } else {
        foreach(element; _subElements) {
        result ~= element.toString();
        }
        result ~= _lastPiece;
        }
    return result;
    }
}
 
unittest
{
    auto newElement = new XmlElement("a");
    assert(newElement._firstPiece == "<a >");
    assert(newElement._lastPiece == "</a>");
    newElement.setAttributes(["href"], ["www.ddili.org"]);
    assert(newElement._firstPiece == "<a href=www.ddili.org >");
    assert(newElement._lastPiece == "</a>");
    newElement.setContent("Ddili.org");
    assert(newElement._firstPiece == "<a href=www.ddili.org >");
    assert(newElement._lastPiece == "</a>");
    assert(newElement._content == "Ddili.org");
    assert(newElement.toString() == "<a href=www.ddili.org >Ddili.org</a>");
    auto anotherElement = new XmlElement("Parent");
    auto SubElement_1 = new XmlElement("FirstSubElement");
    auto SubElement_2 = new XmlElement("SecondSubElement");
    anotherElement.addSubElement(SubElement_1, SubElement_2);
    assert(anotherElement.toString() == 
  "<Parent ><FirstSubElement ></FirstSubElement><SecondSubElement ></SecondSubElement></Parent>");
}
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Forum: Projeler turna RSS
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-04-26, 12:36:15 (UTC -07:00)