Makkalot's Place

A polyglot programmer

Monad Yapısı

| Comments

Bu aralar monadların ne olduğunu anlamaya çalışıyorum. Henüz kafamda çok boşluk var ama en azından şimdiye kadar öğrendiklerimi pekiştirmek adına not almak iyi bir fikir olabilir.

Bir yapının monad olması için bir 2 tane fonksyonu olması gerekir.

  • m-bind fonksyonu : bir değer ve bu değerleri işleyecek bir fonksyon alır.
  • m-result fonksyonu : Geriye monadic bir değer döndürmesi gerekiyor (ileride açıklanacak).

Monad anladığım kadarıyla ardışıl olarak yapılması gereken işlemleri sistematize ve abstract etmek için düşünülmüş bir yöntem. Arama motorlarında “monad” aratınca genelde Haskell dili karşımıza çıksa da bu yapıları diğer dillere uygulamak mümkün.

Çoğu incelediğim örneklerde genelde clojure’ın let yapısı örnek olarak veriliyor. Örneğin :

1
2
3
4
5
6
(let
  [a 12
   b (inc a)]
  (* a b))

156

Clojure ile pek ilgilenmeyenler için açıklayacak olursak. Öncelikle a değişkenine 12 değeri veriliyor, daha sonra b değişkenine a ‘nın bir fazlası veriliyor, en sonunda da 2 değer çarpılıp sonuç geri veriliyor.

Bu tür bir yapıyı let olmadan yapmaya çalışsak nasıl olur ?

1
2
3
4
5
6
7
8
(defn m-bind [value func]
  (func value))

(m-bind 12 (fn [a]
              (m-bind (inc a) (fn [b]
                  (* a b)))))

156

Öncelikle yukarıdaki örnek biraz karmaşık görünebilir, biraz zaman verin gözünüz alışacaktır. Bu anlamda baktığımızda let işimizi oldukça kolaylaştırıyor gerçekten. Aslında let bir karar verme mekanizması gibi. [a 12] ifadesinde 12’yi alıp drek olarak a’ya atadı, bunun aksine 12 ile başka birşey de yapabilirdi. Örneğin bir listeye koymak gibi veya daha farklı işlemler. Pseudocode olarak şöyle görünebilir. Belki javascript syntaxı daha iyi gösterebilir :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function mbind(value, func){
  return func(value);
}

function mresult(v){
  return v;
}

var a = 12;
mbind(a, function(a){
  return mbind(a + 1, function(b){
          return mresult(a * b);
  });
});

Buradaki mbind ve mresult fonksyonları oldukça önemli. Bu 2 fonksyon monadların oluşması için gereken ana yapıları teşkil ediyor. Bir de monadların kuralları var ama kendi monadınızı yazmadığınız sürece pek gerekli olmuyorlar(zaten henüz açıklayacak kadar da bilmiyorum :) ).

Bir monadın diğerinden farkı bu mbind ve mresult fonksyonlarının implementasyonundan kaynaklanıyor. Yukarıda gösterdiğim monad identity monad diye geçiyor. Fakat hangi monad olursa olsun üzerinde yapılan işlemler aynı oluyor. Yani üzerinde sürekli m-bind fonksyonu çağırılıyor ve en sonunda m-result fonksyonu çağırılıp sonuç geriye döndürülüyor. Bu kısmın anlaşılması oldukça önemli.

Şimdi bir diğer monada bakalım maybe monadı. Maybe monadı, art arda yapılması gereken işlemlerden dönen sonucun geçersiz olması problemini çözüyor. Örnek olarak şöyle bir kaç işlemimiz olsun :

1
2
3
var user = get_user(user_id);
var profile = get_profile(user);
var tweets = get_tweets(profile);

Yukarıdaki kodda şöyle bir sıkıntı var. Eğer dönen değerlerden herhangi biri null ise sonucu işleyen fonksyonun kendi içinde bunu kontrol etmesi veya bunu biraraya getiren kodun buna bakması gerekiyor. Yani :

1
2
3
4
5
6
7
user = get_user(user_id);
if(!user)
  return null;
profile = get_profile(user);
if(!profile)
  return null;
tweets = get_tweets(profile);

Herhangi bir yerde bunu unutacak olursak, undefined hatası alacağız. Maybe monadı ile bu işlemi çözebiliriz :

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
function get_user(user_id){
  if (user_id === 1)
      return {name:"ali", profile:true};
  else if(user_id === 2)
      return {name:"osman", profile:false};
  else
      return null;
}

function get_profile(user){
  if(!user.profile)
      return null;
  if(user.name === "ali")
      return {tweets:["fp is fun"]};
  else
      return {tweets:null};
}

function get_tweets(profile){
  return profile.tweets;
}


function mbind(value, func){
  if(!value)
      return null;
  return func(value);
}

function mresult(value){
  return value;
}

Yukarıda mbind fonksyonun içeiriğini biraz değiştirdik. Eğer kendisine geçirilen değer null ise (geçersiz) işlemi durduruyoruz. Şimdi yukarıdaki kodu test edelim:

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
var user_id = 1;
user = mbind(user_id, get_user);
profile = mbind(user, get_profile);
tweets = mbind(profile, get_tweets);

> { name: 'ali', profile: true }
> { tweets: [ 'fp is fun' ] }
> [ 'fp is fun' ]

var user_id = 2;
user = mbind(user_id, get_user);
profile = mbind(user, get_profile);
tweets = mbind(profile, get_tweets);

> { name: 'osman', profile: false }
> null
> null

var user_id = 3;
user = mbind(user_id, get_user);
profile = mbind(user, get_profile);
tweets = mbind(profile, get_tweets);

> null
> null
> null

Görüldüğü gibi yukarıda bahsettiğim undefined hatalarından bu şekilde arınmış olduk. İlk örnekteki akışa uyduracak olursak :

1
2
3
4
5
6
7
8
9
mbind(1, function(id){
  return mbind(get_user(id), function(user){
      return mbind(get_profile(user), function(profile){
          return mbind(get_tweets(profile), function(tweets){
              return mresult(tweets);
          });
      });
  });
});

Bu örnekte de yine not edilmesi gereken ana akışın aynı olduğudur(mbind zinciri ve en sonunda mresult).

Şimdi mresult’un da içinde olduğu başka bir örneğe bakalım. Clojure’da for comprehension yapısı for ile sağlanıyor. Örneğin :

1
2
3
4
5
(for [a (range 5)
    b (range a)]
  (* a b))

 (0 0 2 0 3 6 0 4 8 12)

Yukarıdaki yapı iç içe girmiş for dongüsünü temsil ediyor. İçteki b dongüsü dıştaki a dongüsünden hızlı gidiyor. Buna benzer bir olayı monadlarla nasıl gerçekleştiririz ?

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
function range(n){
  var result = [];
  for(var i = 0; i < n; i++){
      result.push(i);
  }

  return result;
}

function mbind(value, func){
  var result = []
  for(i in value){
      result.push(func(value[i]));
  }

  //flatten etmek lazım burada
  return result.reduce(function(a, b)
  {
      return a.concat(b);
  }, []);
}

function mresult(value){
  return [value];
}


mbind(range(5), function(a){
  return mbind(range(a), function(b){
  return mresult(a * b);
  });
});

> [ 0, 0, 2, 0, 3, 6, 0, 4, 8, 12 ]

Yukarıdaki örnek biraz complex görünebilir, ama aslında oldukça kolay(biraz inceledikten sonra). Burada mresult geriye bir liste döndürüyor. Yukarıdaki örneklerde aldığı değeri aynı şekilde geri döndürüyordu, burada durum biraz daha farklı. Bu noktada biraz tanım yapmak gerekiyor.

  • mresult tarafından dönen değer bizim monad değerimiz oluyor (monad value).
  • mbind‘in her zaman monad değeri döndürmesi gerekir. maybe monad değerin aynısını döndürüyordu, list monadı geriye liste döndürdü.
  • mbind fonksyonunun ilk parametresi de monad değeri olmak zorunda. Kısacası mbind monad alıp monad veriyor geriye. Bunun faydası, bu tür yapıların iç içe (chain) geçirilip tek fonksyon gibi kullanılıyor olması (başka bir blog yazısı).

Girdinin fazla uzamaması açısından burada kesiyorum. Bir diğer blog yazısında da state monadı hakkında öğrendiklerimi paylaşacağım.

İlgilenenler için bulduğum monad kaynakları :

Videolar :

  • Python ile Monadlar : link
  • Clojure ile Monadlar : link
  • Şekilli Monad Anlatımı : link

Diğer :

  • Clojure’da Monadlar : bu ve bu
  • Clojure’da monad kütüphanesi : link

Octopress’e Geçiş

| Comments

Bu hafta sonu Wordpress’ten static blog aracı olan Octopress’e geçtim. Bu değişimin en büyük nedeni, octopressin basit olması ve blog girdisi yazmak istediğimde bana zaman kaybettirmemesi. Ayrıca benim bloglarım genelde kod odaklı oluyor. O yüzden de, sevdiğim editörü blog yazarken terk etmemek çok önemli hale geliyor. Şimdilik memnunum bakalım ilerde ne olacak …

Not : Wordpress’teki girdilerinizi Octopress’e geçirmek isterseniz exitwp aracını deneyebilirsiniz. Bende 1 girdi dışında sorunsız çalıştı. Ayrıca eski girdilerinizin aynı şekilde korunması için de _config.yml içererisindeki permalink ayarını eski sitenze göre ayarlamanız gerekir.

C Ve Virgül Operatörü

| Comments

C programlama dilini günlük işlerimde pek fazla kullanmıyorum.Ama zaman buldukça hep onunla alakalı bilgilerimi genişletmeye çalışıyorum. Genelde C öğretilirken, sıra operatör önceliklerine gelince hatırlayamazsanız parantez kullanın diyorlardı(hocalarımız). Fakat büyük ve profesyonel projelerdeki C kodlarını yazan insanlar bu kuralları çok iyi bilip yuttukları için, bu tür bilgisizlikten doğan parantez kullanımını göremezsiniz. Bu durumda da bizim gibi dili tam bilmeyenler kodu bazen anlamakta güçlük çekebiliyor.

Bazı projelerde virgül operatörünün değişik kullanımlarını görüyorum. C’de virgül operatörünün değişik anlamları olabiliyor. Örneğin bir fonksyon çağırırken :

1
important(a, b, c);

Buradaki kullanımda virgülün pek bir anlamı yok, sadece parametreleri birbirinden ayırmak için kullanılıyor. C’de virgül operatörü önceliği en düşük olan operatör. Önceliği =‘den bile düşük. Ayrıca, virgül operatörü binary bir operatör. Peki bu ne demek? Bunun anlamı, kullanmak için 2 tane operandınızın olması gerekir demek. (Yani 3 + 5 ‘teki + gibi). Çalışma şekli de şöyle; önceki soldaki operandın değeri alınıp atılır, sonra sağdaki operandın değeri alınır ve geriye sağdakinin değeri döndürülür. Örnek verecek olursak :

1
2
int a;
a = (3 + 5, 4 * 5);

Yukarıda verdiğimiz tanıma göre a’nın değeri 20 olacak. Peki neden parantez içine koyduk tüm ifadeyi ? Dediğimiz gibi virgül’ün önceliği = operatöründen düşük.Şöyle olsaydı :

1
2
int a;
a = 3 + 5, 4 * 5;

a’nın değeri 8 olacaktı.

Peki bu tür bir kullanımın ne yararı var bize ? Yararını pek anlayamasam da bazen bu kullanımı makrolarda görüyorum. Örneğin aynı satırda 2 fonksyon çağırıp 2.‘nin değerini geri döndürmek istersek bu şekilde kullanabiliriz. Örneğin :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define evalme(INPUT) (logger(INPUT), inc(INPUT))

void logger(int input){
    printf("Log Value is : %d n",input);
}

int inc(int input){
    return ++input;
}

int main(int argc, char *argv[]){
    int a;
    a = evalme(5);
    printf("Value is : %dn", a);

}

//Output :
//Log Value is : 5
//Value is : 6

Evet, virgül operatörünün neden bu şekilde kullanıldığını anlayamazsam da nasıl kullanıldığını öğrenmiş oldum.

Güncelleme : Yorumlardan, bu tür kullanımın aslında for döngülerinde birden fazla ifade çalıştırmak için yararlı olabileceğini öğrendim.

Python Defaultdict Maceraları

| Comments

Python”da hash(dictionary)“lerle çalışırken bir değer dict içinde değilse ona önceden tanımlı bir değer vermemiz gerekebilir. Örneğin bir yazı içeresinde geçen sözcüklerin kaç defa tekrarlandığını bulmak istiyorsak, şöyle birşey yaparız :

1
2
3
4
5
6
d = {"one":1, "two":2}

if not d.has_key("three"):
    d["three"] = 0

d["three"] +=1

Bunun yerine şöyle birşey yapabiliyor olsak daha güzel olurdu :

1
d["three"] += 1

İşte bu tür bir kullanımı mümkün kılan defaultdict var :

1
from collections import defaultdict

Önceki örneği defaultdict ile yaparsak şöyle birşey olur :

1
2
3
4
5
d = defaultdict(int, one=1, two=2)
d["three"] += 1

print d
defaultdict(, {"three": 1, "two": 2, "one": 1})

defaultdict aynen normal bir dictionary gibi davranıyor. Fakat, ondan farklı olarak bir değeri alırken eğer o değer dictionary içeresinde yoksa, ona baştan verdiğimiz fonksyonu çağırıp ondan dönen değeri set ediyor.

Nasıl mı ? Örnek verecek olursak :

1
2
3
4
5
6
7
8
9
10
In [8]: d = defaultdict(lambda:"", {"name":"ali", "age":22})

In [9]: d
Out[9]: defaultdict( at 0x93658ec>, {"age": 22, "name": "ali"})

In [10]: d["age"]
Out[10]: 22

In [11]: d["surname"]
Out[11]: ""

Burada defaultdict”e ilk parametre olarak lambda verdik ve içeresinde olmayan bir değer istendiğinde bize “missing_value” değerini döndürdü.

defaultdict”in koduna bakmadım, ama büyük ihtimalle şöyle birşey yapıyordur.

Bir müşteri için çalıştığım bir projede bir ayar dosyasını(json) okuyup normal bir objeymiş gibi erişmek istiyordum. Örnek olarak :

1
2
3
4
5
6
7
conf_dict = {
    "logf":"/home/some/path",
    "dbsettings":{
        "port":5001,
        "address":"localhost"
        }
    }

Gibi bir Python dictionary olsun ve biz buna normal bir objeymiş gibi erişebillim, şöyle ki :

1
2
conf_dict.logf
conf_dict.dbsettings.port = 5002

Bu tür bir kullanımı mümkün kılmak için defaultdict kullanıp __setattr__ ve __getattr__ fonksyonlarını ezdim(override) :

1
2
3
4
5
6
7
8
9
10
11
12
class DefDict(defaultdict):
    def __setattr__(self, name, value):
        self[name] = value
    def __getattr__(self, name):
        return self[name]
    def __repr__(self):
        def _dicts(d):
            if getattr(d,"iteritems",None):
                return {k: _dicts(v) for k,v in d.iteritems()}
            else:
                return d
        return str(_dicts(self))

Sadece bu şekilde kullanırsak pek işimize yaramayacak çünkü iç içe girmiş
yapılara erişim sağalayamacak. Yani bir değer bulunamadığında tekrar DefDict objesi döndürmemiz gerekir. Şöyle:

1
2
def attrdict(*args, **kw):
    return DefDict(attrdict, *args, **kw)

Fonksyon recursive, yani her değer bulamayışında bulamadığı değere DefDict objesi atayacak.(Başta bakınca biraz karışık görünüyor, ama biraz zaman verin gözünüz alışacaktır.)

Şimdi bu yapıyı bu şekilde kullanabiliriz :

1
2
3
4
5
6
7
8
9
10
11
if __name__ == "__main__":
    d = attrdict()
    d.name = "Brave"
    d.last_name = "Heart"
    print d
    d.friends.best = "Linux"
    print d
#Output:
#defaultdict(, {"book_1": 12, "book_2": 111})
#{"last_name": "Heart", "name": "Brave"}
#{"last_name": "Heart", "friends": {"best": "Linux"}, "name": "Brave"}

Gerçekten kullanım olarak çok kolaylık sağlıyor, ayrıca isterseniz normal dictionary gibi de kullanmaya devam edebilirsiniz.

Bu tür bir yapının şöyle bir de dezavantajı var. Her bulamadığı değer için yeni bir DefDict yapısı oluşturduğu için, siz bulunamayan bu değere atama yapmasanız da o alt tarafta oluşturuluyor :

1
2
3
4
5
6
7
8
9
10
11
if d.enemies.worst:
    print "The enemy is :",d.enemies.worst

#The problem ?
print "Problem : ",d

#Output:
Problem :  {"last_name": "Heart",
            "friends": {"best": "Linux"},
            "name": "Brave",
            "enemies": {"worst": {}}}

Bu sorunu çözmeyi başaramadım, bulan varsa lütfen yorumlara yazsın.

Javascript Acayiplikleri - Tip Çevrimleri

| Comments

Javascript’in en kafa karıştırıcı ve acayip özelliklerinden bir tanesi tip çevirimleri konusu. “Javascript : Definitive Guide” kitabını okurken “==” karşılatırma operatörü söz konusu olduğunda, eğer karşılaştırılan objeler aynı tipten değilse birtakım tip çevirimleri yapılıyor. Bu javascript dizayn edildiği yıllarda geliştiricilere kolaylık olsun diye yapılmış bir özellik Fakat kolaylıktan çok, bence baş ağrıtan bir özellik olmuş. Gerek var mı tip çevrimlerine ? Bence yok, bu şekilde çalışan bi sürü dil var ve hiçbir zaman tip çevriminin ihtiyacını duymadım. Peki neden

1
2
true == 1
true

cevabı dönüyor? İlk bakıldığında çok saçma gelse de, bu saçmalığı yaratan bir dizi kurallar var :

  • Eğer karşılaştırılan tipler aynı ise bire bir karşılaştırılma yapılır(string, number vs). Diğer diller öntanımlı olarak sadece bunu yapıyor.
  • Eğer tipler farklı ise :
  • Eğer biri “null” diğeri “undefined” is true
  • Eğer ikisi de true true is true, false false is false’dir(doğal olarak)
  • Eğer değerlerden biri NaN ise false döner
  • Eğer biri string diğeri number ise, string number’a çevrilip deneme yapılır.
  • Eğer biri true is 1’e, false ise 0’a çevrilip kontrol o şekilde yapılır.
  • Eğer biri object ise önce daha basit bir tipe dönüşüm yapılır. Önce varsa valueOf fonksyonu çağrılır, ondan dönen değer basit ise onunla kontrol yapılır. Eğer valueOf yoksa, toString fonksyonu çağırılır ve ondan dönen string değeri kullanılır.

Bu durumda

1
2
"1" == true
true

ifadesinde true, önce 1’e çevrilir :

1
"1" == 1

durumuna geldikten sonra, “1”‘de number’a çevrilir ve :

1
1 == 1

olur, bu duurmda da istenen cevap ortaya çıkar. Bu tür bir tip dönüştürme işlemi <, <=, >, >= operatörlerinde de geçerli oluyor.

Bu karşılaştırma işlemlerinde string ve number kalınca, hepsi number yapılmaya çalışılıyor. + ve * operatörlerinde ise sonuç genelde string odaklı oluyor.

1
2
"1" + 2
"12"

Artık bu tür ifadeleri gördüğümde şaşırmayacağım :

1
2
3
4
1 + {}
"1[object Object]"
true + true
2

Peki gerek var mı ? Bence kafa karıştırması dışında bir faydası yok. Bunun gibi acayip kuralları akılda tutmak istemiyorsanız === operatörünü kullanabilirsiniz. Bu operatör ==‘ın aksine tip dönüşümü yapmıyor, yani diğer dillerdeki gibi çalışıyor ki, bu durumda bu çok güzel birşey.

Javasript Acayiplikleri

| Comments

Bildiğimiz gibi javascript şu anda en çok kullanılan dil. Bu duruma çok mutlu olmasam da durum bu şekilde. Javascript ile ilk olarak web developer olarak çalışmaya başladığım şirketlerde tanışmıştım.

“Javascript biliyor musun ?” dediklerinde

“Hayır”

cevabımın arkasında

“Çok kolay bir dil Java’nın aynısı”

ifadesini sürekli duyuyordum(Java ile tek benzerliği sadece ismi olsa da). Yani kısacası Javascript’i öğrenmek için hiç zaman ayıramadım. Drek koda dalıp o şekilde birşeyler yapmaya çalışıyordum. Tabi bunun çok acısını çektim; closure yapısının yan yetkileri, “var” olarak tanımlanmayan değişkenlerin otomatik global olması, tip çevirimleri gibi acayip özellikler beni çok şaşırtmış ve  bir sürü zamanımı almıştı. Daha sonra kendimi web işlerinden çektim ve daha çok sistem programlama ve gömülü sistemler ile uğraşmaya başladım. Böylece javascripten kurtulmuş, onun acayipliklerinde uzak kalmıştım. Şimdi tekrar web ile uğraşmaya başladım, ama daha çok arka taraf(backend) işleri ile uğraşıyorum. Görsellik ve cancanlı işler bana uzak olduğu için oralarda daha iyi olan arkadaşlarda yardım alıyorum. Fakat bazen aklıma küçük küçük projeler geliyor ve ön tarafı kendim yapamadığım için vazgeçip kenara koyuyorum veya sadece arka tarafını yazıp bırakıyorum. Bu durumu çözmek için javascript’i gerçekten öğrenmeye karar verdim. İlk seferki gibi aynı hatalara düşmeden, acayipliklerin nedenlerini öğrenmek istiyorum.  Bu dili öğrenirken de bana acayip gelen bazı özellikleri buraya not etmeyi düşünüyorum.

Evet bakalım bu satır neden bunu veriyor :

1
2
true == 1
true

Virtualenv Ile Django-nonrel Bootstrap Script

| Comments

Bu aralar işim dolayısıyla Google App Engine ve Django-nonrel projesiyle ilgileniyorum. Çok farklı şeyle uğraştığımdan dolayı, birkaç hafta sonra tekrar bu işlere baktığımda büyük ihtimalle hiçbişey hatırlamayacağım. Şu anda django-nonrel ve appengine’yi birlikte kurmak için şuradaki blog yazısına bakıyorum : Link Kurulumu biraz uzun ve hiç de pratik değil. Ben djangoappengine uygulaması geliştirmek istediğimde tekrar tekrar bu yazıya bakmayayım diye bu işlemi yapan virtualenv bootstrap betiği yazdım. Betiği şuradan bulabilirsiniz : Link Betiğin düzgün çalışması için appengine sdk’sının sisteminizde kurulu olması gerekir. Aslında script bunu da yapabilirdi ama üşendim … Betik django-nonrel+appengine kurulumu için gereken bütün paketleri indiriyor ve küçük bir test django-appengine uyugulaması yaratıyor. Geriye kalan sadece dizin içeresindeki app.yaml dosyasını kendimize göre düzenlemek.

Eğer appengine sdk’nın 1.6.4 versiyonunu kullanıyorsanız, şuradaki yazıma da bakabilirsiniz : Link

Gae 1.6.4 Ve Django-nonrel Problemi

| Comments

Gae 1.6.4 SDK’sını django-nonrel projesiyle kullanırken,

python manage.py createsuperuser 

gibi komutların çalıştırıldığını fakat, geliştirme ortamında herhangi bir etkisinin olmadığını gördüm. Ayrıca

python manage.py console 

komutunda da consolda kaydettiğim objelerin de herhangi bir etkisi yoktu. Bunun nedeni GAE geliştiricilerinin geliştirme sunucusuna (development server)gerçek ortama daha çok benzemesi için yaptıkları bir değişiklikmiş. Biraz araştırma yaptıktan sonra,küçük bir patchle sorunu çözmeyi başardım.

Bu sorunun bildirildiği tartışma : Tartışma .

Bu sorunu çözümü :  Çözüm

Bu da googleappengine modülünüe uygulanması gereken patch (boot.py) : Patch

Vim Ile Konsol

| Comments

Dün emacs ile alakalı bir video seyrettim. Üst tarafta kod alt tarafta konsol üstte yazdıkları lisp komutlarını altta hemen test ediyorlardı. Ondan sonra emacs’e heves ettim biraz uğraştım, tutorialına baktım ama galiba emacs öğrenmek için biraz yaşlıyım. Şu ana kadar hep vim çıktı önüme, komutlarına da biraz aşınayım, bu tür birşeyi vim’de naıl yaparım diye araştırdım. Ama anladığım kadarıyla vimciler bu tür vim’in içeresine konsol açma olayına biraz karşılar. Birtakım pluginler var ama onların hakkındaki yorunmlar pek hoşuma gitmedi. Ben de “screen” denen programla buna benzer birşey yapabileceğimi öğrendim. Screen ile konsol ekranınızı istediğiniz kadar parçaya bölebiliyorsunuz. Örneğin(önce screen komutunu çalıştırın) :

CTRL-a S

komutu ile ekranı yatay olarak ortadan 2’ye bölebliyorsunuz. Bundan sonra birinden diğerine geçmek için

CTRL-a TAB

Geçtiğiniz kısımda eğer açık shell yoksa, yaratmak için :

CTRL-a c

Konsolun ortadan 2’ye ayrılması vim ile geliştirme yapacaksanız pek bir işe yaramıyor. Konsolun daha küçük olması daha mantıklı, çünkü zamanın çoğunda vim’i kullanacaksınız zaten. Bunun için daha küçük olmasını istediğiniz kısma giderek :

CTRL-a :resize 10

Ayrıca yazının başında da söylediğim gibi vim’den konsola satırları seçip konsola göndermenin yolunu da buldum. Yine screen programı ve bir vim plugini ile bunu yapabiliyorsunuz. Nasıl yapıldığını anlatan yazı şurada : Link

Emacs öğrenmekten şimdilik kurtuldum bakalım ilerde ne olacak …

Rails Bundler Ile Alakalı

| Comments

Rails in Action 3 kitabını okurken, 

bundle install --binstubs 

komutuna rastladım. —bintstubs seçeneği kurulacak olan gemlerden, çalıştılabilir dosyası olanları bin klasörünün altına koyuyor. Bu şekilde örneğin kendi projenize özgü rspec komutunu çağıracağınız zaman

bundle exec rspec spec

demek yerine

bin/rspec spec 

diyebiliyoruz. Çok güzel bir özellik…