Por algum motivo, pensei se isso seria mais uma das “coisas que você nunca quis fazer com Ruby”, mas acho que não, até porque estou num projeto para usar tal funcionalidade. Mas, por via das dúvidas, vamos fazer algo BEM estranho:

Para quem não conhece, o FUSE (Filesystem in Userspace), uma extensão do Kernel do Linux (e do MacOSX, se não me engano), serve principalmente para criar sistemas de arquivo. Isso significa, se você sempre teve vontade de acessar sua base de dados como se fosse uma sequência de diretórios, por exemplo, esse pacote é para você. Mas, como já existem exemplos demais assim, vamos fazer algo mais bizarro (para variar): integrar o Twitter com FUSE.

Para tal tarefa, você precisará das gems “twitter” e “fusefs”. Basta instalá-las com “gem install fusefs twitter”, arregaçar as mangas, e partir para o próximo passo.

No meu caso, resolvi fazer um sistema assim (nota: todas as funcionalidades são válidas para Linux, não sei se no MacOSX é diferente): eu monto o sistema na pasta “dir”, por exemplo, e por padrão ele vem vazio. Ele não permite que eu crie arquivos (afinal, isto é um exemplo), mas permite que eu crie diretórios-cada diretório criado será uma busca a ser feita no Twitter, que retornará os twits mais recentes sobre o assunto. Para começar, resolvi fazer uma classe para me auxiliar nas buscas:

require "twitter"

class TwitterClient
  include Enumerable

  def initialize(search)
    @search = Twitter::Search.new search
    @results = @search.fetch_next_page
  end

  def each(&b)
    @results.each do |r|
      b.call(r.text)
    end
  end

  def [](index)
    to_a[index]
  end

  def to_a
    @array ||= super
  end
end

Até aí, nenhum segredo. Os códigos mais difíceis, a meu ver, são o “@array ||= super” (usado para que eu não precise fazer a busca cada vez que eu faça “ls” no diretório do twitter), e o “include Enumerable” (que eu uso, particularmente, para não ter que implementar minha própria versão do “to_a”.

Depois, resolvi finalmente implementar minha versão do FuseFS. Esta biblioteca do Ruby pede que você crie uma classe com determinados métodos. Por exemplo, o método “contents” deve retornar um Array com todos os elementos que o diretório contém. Para simplificar, resolvi só listar o número do tweet. Antes de passar a classe, umas explicações básicas: a maioria dos métodos recebe um parâmetro (path), que contém o caminho do arquivo ou diretório operado (“/algo”, “/algo/foo”, etc). Eu herdei da classe FuseFS::FuseDir para ganhar o método “split_path”, que retorna um array com todos os diretórios. Agora, ao código:

require "twitter_client"
require "fusefs"

class Mount < FuseFS::FuseDir
  def initialize
    @twitters = {}
  end

  #Este método é chamado sempre que é necessário saber o conteúdo de uma pasta
  def contents(path)
    return @twitters.keys if path == '/'
    search = path[1..-1]
    (0...@twitters[search].to_a.size).map { |n| n.to_s.rjust(4, '0') }
  end

  #Este método é chamado quando for necessário ler um arquivo.
  def read_file(path)
    search, tweet = split_path path
    @twitters[search][tweet.to_i]
  end

  #O caminho é um arquivo?
  def file?(path)
    true unless root_node? path
  end
  
  #O caminho é um diretório?
  def directory?(path)
    true if root_node?(path) && @twitters.keys.include?(path[1..-1])
  end

  #Qual o tamanho do arquivo? Este método é mais importante do que parece,
  #porque o FuseFS vai disponibilizar EXATAMENTE o número que aparece
  #neste método de bytes para o arquivo. Ou seja, se o "read_file" retorna um
  #texto de 30 bytes, mas este método retorna 10, somente 10 bytes serão lidos.
  def size(path)
    read_file(path).size
  end

  #Se é possível criar um diretório no caminho atual
  def can_mkdir?(path)
    true if root_node? path
  end

  #Quando for pedida a criação de um arquivo, o que fazer?
  def mkdir(path)
    search = path[1..-1]
    @twitters[search] = TwitterClient.new search
  end

  #Função que me ajuda a identificar se é a raiz ou não.
  def root_node?(path)
    path.scan('/').size == 1
  end
  private :root_node?
end

#Define os parâmetros para o FuseFS
twitter = Mount.new
FuseFS.set_root( twitter )

#CTRL+C desmonta e sai do programa.
trap 'INT' do
  FuseFS.unmount
  exit 0
end

#Montar sobre o diretório que for passado como primeiro argumento
FuseFS.mount_under ARGV.shift
FuseFS.run

Para rodar, basta usar “ruby mount.rb “. As possibilidades são infinitas (e possivelmente, mais úteis do que este exemplo simples), mas mostram o poder de uma linguagem de altíssimo nível (como Ruby) para produzir resultados de baixíssimo nível (como sistema de arquivos).


1 Comment

Tweets that mention Sistemas de Arquivos em Ruby | Maurício Szabo -- Topsy.com · 2010-08-10 at 11:43

[…] This post was mentioned on Twitter by Garoto que programa, Maurício Szabo. Maurício Szabo said: Alguém já quis criar sistemas de arquivos completos em Ruby? Com o FuseFS, é possível: http://bit.ly/bcMSGg […]

Leave a Reply

Your email address will not be published. Required fields are marked *