inicio mail me! sindicaci;ón

Archive for Ruby

Zero-config Rails und PHP mit puma-dev

Mit puma-dev lassen sich beliebig viele Rack/Rails-Anwendungen durch Erstellen von Symlinks verfügbar machen.
Allerdings ist damit Port 80 blockiert und steht nicht mehr für Apache zur Verfügung.

Zum Glück erlaubt puma-dev nicht nur das Erstellen von Symlinks auf Rack-Apps sondern auch das Erstellen von Files innerhalb von ~/.puma-dev/, die als Inhalt einen alternativen Port enthalten.
Damit kann man alle Applikationen, die statt von puma-dev von Apache behandelt werden sollen, z.B. PHP-Apps und statische Seiten, als Files anlegen, die nur 81 enthalten.
Außerdem muss man Apache auf Port 81 lauschen lassen.

Die erforderlichen Schritte sind:

Apache-Konfiguration /etc/apache2/httpd.conf

Apache soll zukünftig auf Port 80 lauschen:

Listen 81

Die Module für virtuelle Hosts, Rewrite und PHP sollen verwendet werden:

LoadModule vhost_alias_module libexec/apache2/mod_vhost_alias.so
LoadModule rewrite_module libexec/apache2/mod_rewrite.so
LoadModule php7_module libexec/apache2/libphp7.so
Include /private/etc/apache2/extra/httpd-vhosts.conf

Virtual Hosts Konfiguration /etc/apache2/extra/httpd-vhosts.conf

<VirtualHost 127.0.0.1:81>
  ServerName any.dev
  ServerAlias *.dev
  UseCanonicalName Off
  VirtualDocumentRoot "/Users/{username}/Sites/%1"
  <Directory "/Users/{username}/Sites/*">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
    Require all granted
  </Directory>
</VirtualHost>

Angenommen die gewünschte PHP-Applikation liegt im Verzeichnis /Users/{username}/Sites/meine-app/.
Erstellt man eine Datei ~/.puma-dev/meine-app mit dem Inhalt 81, dann wird diese unter http://meine-app.test durch Apache verfügbar gemacht.

Happy hacking!

Updating postgres from 9.3 to 9.4 on MacOSX

Working with Heroku I recently wanted to pull the Heroku production DB to my local DB. Using pg:pull I got the following error message:
pg_dump: aborting because of server version mismatch
because Heroku uses PostgreSQL 9.4 and my local version was 9.3.
So I had to update my local DB; this is what I did:

1) shut down db and remove launch agent plists:
launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
rm ~/Library/LaunchAgents/homebrew.mxcl.postgresql*

2) remove links to old binaries, install new version (links automatically):
brew unlink postgresql93
brew install postgres

Note that I do not yet uninstall the old binaries, we need them to run pg_update later!

3) Link the new launch agent plist
ln -sfv /usr/local/opt/postgresql/*.plist ~/Library/LaunchAgents

4) create a new empty cluster with the same locale as the old one:
initdb /usr/local/var/postgres9.4 --locale=en_US.UTF-8

5) run the upgrade script (using the binary of the new version)
pg_upgrade -v \
-d /usr/local/var/postgres \
-D /usr/local/var/postgres9.4 \
-b /usr/local/Cellar/postgresql93/9.3.5/bin \
-B /usr/local/Cellar/postgresql/9.4.5_2/bin

6) start new server and uninstall the old version:
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
brew uninstall postgresql93

7) optional: re-install the pg rem for my ruby app
gem uninstall pg
bundle

Hope this helps somebody!

Expressions as ruby default method arguments

When you use an expression as a ruby default argument like in

def delay(time=2.minutes.from_now)
  ... # stuff that needs to be done later
end

you might wonder, when exactly the expression gets evaluated, at parse time or at execution time. The difference, especially for time based expressions, can be huge!
Let’s find out:

puts 'A'
def delay(time=puts('B'))
end
puts 'C'
delay()

The output is

A
C
B

So it’s at execution time, which is much more useful anyway.

Using throw and catch to tidy up our code

Let’s say we want a simple controller action that checks, if a given code is valid.
It should return true and false and, if the code is invalid, give the reason (whether that is because it is unknown or because it has been used already). The action could look like this:

[gist id=2235608 bump=1]

Now this deep nesting effectively hides the underlying algorithm, a simple one in this case.
Expressing this using throw and catch straightens the code a bit:

[gist id=2235614 bump=1]

We are down to one respond_with line but the has merging and the throws at the beginning of the line are not particularly pretty.

Let’s introduce a little Ruby mixin for the Hash class that provides it with a compact method that its friend Array has had all along:

[gist id=2235631]

Now using this to clean up the response hash and moving the throw statements to the end so the steps of the algorithm are visible we have our final version of the action:

[gist id=2235618]

Saving local text files to S3 using paperclip

Sometimes you need to save a locally created file to S3 instead of an uploaded file (the standard). Here is how:

has_attached_file :tagged_text_file,  STORAGE_OPTIONS.merge({
  :processors   => []
})

...

def save_tagged_text_file
  file = File.open("#{RAILS_ROOT}/tmp/tagged_text_#{id}.txt", 'w+')
  file << tagged_text
  self.tagged_text_file = file
  save!
  file.close
end

Push local branch to specific heroku app

You use two heroku apps as staging and production for your project. You added both as git remotes, e.g. “production” and “staging”.

Now you want to push your local branch “poster” to remote “staging”, use
git push staging poster:master

Note, that you can only push to “master” on herokus side. This is just git syntax, but I keep forgetting it, so here it is for future reference.

Ruby: map Array to Hash

Sometimes you may wish to map an Array to a Hash in Ruby, like say, you got the output of I18n.available_locales

locales = [:en, :de, :fr]

and want to transform that into a Hash to fill a select element, like so:

{:en => 'EN', :de => 'DE', :fr => 'FR'}

How do you do that in the most concise way?
First, there is always inject:

locales.inject({}) {|hsh, sym| hsh[sym] = sym.to_s.upcase; hsh}

But I like the following approach way better, mainly because it emphasizes my intention more clearly (and, btw. is faster too):

Hash[locales.map{|sym| [sym, sym.to_s.upcase]}]

Remember: Hash[[[:a,:b],[:c,:d]]] (as well as Hash[:a,:b,:c,:d])
produces {:a => :b, :c => :d}.

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.

Next entries »