inicio mail me! sindicaci;ón

Archive for Ruby

Copying Files between S3 buckets

Building on this article here is a simple ruby script, that copies files between two buckets of the same S3 account, omitting files already present (by name).
This variant adds a list of path prefixes, so you can selectively copy only certain directories of your buckets.
Furthermore it copies the original buckets ACLs for each key.


require 'rubygems'
require 'right_aws'

aws_access_key_id     = 'YOUR AMAZON ACCESS KEY'
aws_secret_access_key = 'YOUR AMAZON SECRET ACCESS KEY'
source_bucket         = 'SOURCE BUCKET NAME'
target_bucket         = 'TARGET BUCKET NAME'
prefixes              = [PATH_PREFIX1, PATH_PREFIX2, ...]

s3 = RightAws::S3Interface.new(aws_access_key_id, aws_secret_access_key)

copied_keys = Array.new
(prefixes || ['']).each do |prefix|
  s3.incrementally_list_bucket(target_bucket, {:prefix => prefix}) do |key_set|
    copied_keys << key_set[:contents].map{|k| k[:key]}.flatten
  end
end
copied_keys.flatten!

(prefixes || ['']).each do |prefix|
  s3.incrementally_list_bucket(source_bucket, {:prefix => prefix}) do |key_set|
    key_set[:contents].each do |key|
      key = key[:key]
      if copied_keys.include?(key)
        puts "#{target_bucket} #{key} already exists. Skipping..."
      else

        puts "Copying #{source_bucket} #{key}, setting acl"

        retries=0
        begin
          s3.copy(source_bucket, key, target_bucket)
          acl = s3.get_acl(source_bucket, key)
          s3.put_acl(target_bucket, key, acl[:object])
        rescue Exception => e
          puts "cannot copy key, #{e.inspect}\nretrying #{retries} out of 10 times..."
          retries += 1
          retry if retries <= 10
        end
      end
    end
  end
end

Vor kurzem dazugelernt:

Suche nach Wort unter dem Cursor in vim: #.

jssh ist eine JavaScript Shell, die den Firefox per Port 9997 fernsteuerbar macht.
Download z.B. hier.

y erzeugt einen YAML dump auf der Rails console, mehr dazu hier.

=3D ist ein escaptes “=” in quoted_printable.

sudo /usr/libexec/locate.updatedb aktualisiert unter MacOSX sofort die locate Datenbank.

rake db:migrate:redo führt unter rails die letzte Migration rückwärts und sofort wieder vorwärts aus, so dass sich die Vorwärts-Action korrigieren läßt

ack -Q bringt ack dazu, literal, also ohne RegExp zu suchen.

What ist this?

Laut PHP-Doku ist das Verhalten der Pseudo-Variablen $this wie folgt:

$this is a reference to the calling object (usually the object to which the method belongs, but can be another object, if the method is called statically from the context of a secondary object).

Schauen wir uns folgendes Beispiel an:

class A{
  function __construct(){
    $this->name = 'A';
  }

  function echoThisName(){
    echo "My Name is {$this->name}.\n";
  }
}

Jetzt rufen wir die Methode mal als Instanzmethode und mal statisch auf:
$a = new A();
$a->echoThisName();
A::echoThisName();

My Name is A.
PHP Fatal error:  Using $this when not in object context in 
/Users/aljoscha/test.php on line 9
Fatal error: Using $this when not in object context in 
/Users/aljoscha/test.php on line 9

Das ist vernünftig. (ausser: Warum muss sich PHP eigentlich immer wiederholen? Ist eine Fehlermeldung zu subtil?)
Jetzt rufen wird diese Methoden aus einer anderen Klasse B heraus auf:
class B{
  function __construct(){
    $this->name = 'B';
  }

  function echoAsName(){
    $a = new A();
    $a->echoThisName();
    A::echoThisName();
  }
}

$b = new B();
$b->echoAsName();

Und das gibt:
My Name is A.
My Name is B.

Wir haben die Methode echoThisName der Klasse A statisch aufgerufen, aber $this ist darin trotzdem gesetzt, und zwar als wären wir in der Instanz $b der Klasse B.
$b hat sich die statische Methode gekapert.

Was sagt Ruby dazu?


class A
  def initialize
    @name = 'A';
  end

  def echoThisName
    puts "My Name is #{@name}.\n";
  end
end

$a = A.new;
$a.echoThisName;
A::echoThisName;

ergibt:

My Name is A.
test.rb:13: undefined method `echoThisName' for A:Class (NoMethodError)

Rufen wir A::echoThisName aus einer Instanz von B auf:

class B
  def initialize
    @name = 'B';
  end

  def echoAsName
    $a = A.new;
    $a.echoThisName;
    A::echoThisName;
  end
end

$b = B.new;
$b.echoAsName;

Ist das Ergebnis entsprechend:
My Name is A.
test.rb:23:in `echoAsName': undefined method `echoThisName' 
for A:Class (NoMethodError) from test.rb:28

Besser.
Dank an Stephan für den Hinweis auf die Dokumentation des Verhaltens in PHP.

Fixing the Prototype Enumeration Mixin

The following article describes ticket #3592 in the RoR Trac and explains a proposed solution.

Prototypes Enumerable mixin class does not properly respect the mixees internal format. For example the reject and findAll methods, operating on Hashes, return Arrays instead of Hashes.

var a = $H({a:1, b:2, c:1, d:3}); 
document.writeln(a.inspect().escapeHTML());

=>#<Hash:{'a': 1, 'b': 2, 'c': 1, 'd': 3}>

var b = a.reject(function(val){ return (val[1]==1) });
document.writeln(b.inspect().escapeHTML());

=> [['b', 2], ['d', 3]]   // !!! should be hash

var c = b.findAll(function(val){ return (val[0]=='d') });
document.writeln(c.inspect().escapeHTML());

=> [['b', 2], ['d', 3]]   // !!! should be hash

In Ruby (from which the inspiration for the Enumerable Mixin stems), the reject method does return a hash, not an array, when operating on an hash:

 
irb(main):001:0> a={:a => 1, :b => 2, :c => 1, :d => 3}  

=> {:b=>2, :c=>1, :a=>1, :d=>3}  

irb(main):002:0> b=a.reject{|k,v| v==1}

=> {:b=>2, :d=>3} 

The patch requires all classes that want to mixin Enumerable to define two more methods similar to _each:
_new, which returns an empty object of that class and
_add, that adds an element. Enumerable mixin thus needs to make no more assumptions about the internal structure of its mixee class.

After the fix the methods return correctly:

=> #<Hash:{'b': 2, 'd': 3}>