Sebastians Blog http://sgaul.de Neues aus den Softwareminen... Thu, 07 Jul 2016 15:48:15 +0000 de-DE hourly 1 https://wordpress.org/?v=4.6 Verwischte Font-Awesome-Icons in Phantom JS http://sgaul.de/2016/07/07/verwischte-font-awesome-icons-in-phantom-js/ http://sgaul.de/2016/07/07/verwischte-font-awesome-icons-in-phantom-js/#respond Thu, 07 Jul 2016 15:48:15 +0000 http://sgaul.de/?p=2874 Weiterlesen ]]> Wir reichern unsere auf Capybara und Phantom JS basierenden Feature-Tests gerne mit Screenshots an. Hierbei kam es regelmäßig zu einem Problem mit Font Awesome, wodurch der automatische Abgleich fehl schlug. Bei einzelnen Specs tritt das Problem nicht auf, erst wenn mehrere Tests mit unterschiedlichen Seiten abgelichtet werden zeigte sich folgendes Phänomen:

font-awesome-phantomjs

Alle Icons, wie hier das Such-Icon rechts, wurden wie links zu sehen verzerrt. Den Lösungsansatz brachte dieser Eintrag auf Stackoverflow: Ist die Schriftart nativ auf dem System vorhanden, tritt die Verzerrung nicht mehr auf. Unter Ubuntu ist das schnell gemacht:

sudo apt-get install fonts-font-awesome
sudo fc-cache -f -v
]]>
http://sgaul.de/2016/07/07/verwischte-font-awesome-icons-in-phantom-js/feed/ 0
Herausfinden, wo eine Methode definiert wurde http://sgaul.de/2016/04/26/herausfinden-wo-eine-methode-definiert-wurde/ http://sgaul.de/2016/04/26/herausfinden-wo-eine-methode-definiert-wurde/#respond Tue, 26 Apr 2016 10:37:57 +0000 http://sgaul.de/?p=2870 Weiterlesen ]]> In manchen Situationen ist es alles andere als offensichtlich, welche Klasse oder welches Modul eine Methode bereitstellt. Wer implementiert die Get-Methode für Rspec-Controller-Tests? Gerade in solch zusammengewürfelten Umgebungen ist das nur schwer nachvollziehbar. Ruby hilft hierbei: Die Metamethode method gibt Auskunft, woher eine Methodendefinition stammt:

require "rails_helper"

RSpec.describe MyController do
  it do
    puts method(:get)
  end
end

# => #<Method: RSpec::ExampleGroups::MyController(ActionController::TestCase::Behavior)#get>

Das Beispiel zeigt nicht nur die Klasse, sondern auch das includete Modul, welches für die Methode verantwortlich ist. Dies hilft beim Googeln oder man verwendet Ri:

ri ActionController::TestCase::Behavior#get

 

]]>
http://sgaul.de/2016/04/26/herausfinden-wo-eine-methode-definiert-wurde/feed/ 0
Factory-Girl-Daten im Development-Modus http://sgaul.de/2016/03/30/factory-girl-daten-im-development-modus/ http://sgaul.de/2016/03/30/factory-girl-daten-im-development-modus/#respond Wed, 30 Mar 2016 12:20:55 +0000 http://sgaul.de/?p=2867 Testdaten aus Factory-Girl lassen sich in einer Rails-Konsole auch für den Development-Modus (und entsprechend jeden anderen Modus) bereitstellen:

require 'factory_girl'; FactoryGirl.find_definitions
FactoryGirl.create :my_model
]]>
http://sgaul.de/2016/03/30/factory-girl-daten-im-development-modus/feed/ 0
Rails-Konfiguration in Engine auslagern http://sgaul.de/2016/01/30/rails-konfiguration-in-engine-auslagern/ http://sgaul.de/2016/01/30/rails-konfiguration-in-engine-auslagern/#respond Sat, 30 Jan 2016 09:01:17 +0000 http://sgaul.de/?p=2854 Weiterlesen ]]> Für die meisten Rails-Projekte verwende ich die gleichen Gems mit ähnlichen Konfigurationen. Um den Projektstart und den Update-Prozess zu vereinheitlichen, möchte ich eine Engine, die diese Abhängigkeiten und Konfigurationen übernimmt.

Für den einfacheren Einstieg erzeuge ich innerhalb einer bestehenden Rails-App eine neue Engine:

rails plugin new m3 --full

Diese wird zu Testzwecken im Gemfile des Elternprojekts verlinkt:

gem 'm3', path: 'm3'

Gem-Abhängigkeiten im Gemspec-File

Im Gemspec-File m3.gemspec steht bereits eine Rails-Abhängigkeit, die ich um die gewünschten Gems erweitere:

s.add_dependency "rails", "~> 4.2.4"
s.add_dependency "simple_form", "~> 3.2.0"
s.add_dependency "draper", "~> 2.1.0"
s.add_dependency "cancancan", "~> 1.13.1"

Damit diese beim Start der Elternapplikation zur Verfügung stehen, müssen diese vom Gem geladen werden. Dies sollte in der Engine-Datei gemacht werden:

# <gem>/lib/<gem>/engine.rb

require 'cancan'
require 'draper'
require 'simple_form'

module M3
  class Engine < ::Rails::Engine
    # ...

Die Elternapplikation kann nun wie gewohnt auf die Gems zugreifen.

Initializer

Erweiterte Konfigurationen für Simple-Form werden in zugehörigen Initializern festgelegt, die man ebenfalls einfach in der Engine anlegen kann:

# <gem>/config/initializers/simple_form.rb

SimpleForm.setup do |config|
  # ...
# <gem>/config/initializers/simple_form_bootstrap.rb

SimpleForm.setup do |config|
 # ...

Controller-Erweiterungen

Grundlegende Controller-Konfigurationen habe ich in einen Application-Controller mit Namespace gepackt:

# <gem>/app/controllers/m3/application_controller.rb

class M3::ApplicationController < ActionController::Base
  # ...

Hier kann man auch wie gewohnt Concerns verwenden, diese werden ebenfalls automatisch gefunden.

Die fertigen Konfigurationen können dann vom Application-Controller der Elternapplikation verwendet werden:

# <app>/app/controllers/application_controller.rb

class ApplicationController < M3::ApplicationController
  # ...

Module und Klassen aus lib bereitstellen

Auch in einer Engine landet unter lib so einiges, was nicht vom Autoloading abgedeckt wird. Diese könnten wie Gem-Abhängigkeiten explizit in der Engine-Datei geladen werden. Diese Lösung ist jedoch ein unschöner Workaround und sei hier nur für den Notfall aufgeführt.

# <gem>/lib/<gem>/engine.rb
# Workaround
require_dependency File.expand_path("../../../app/models/m3", __FILE__)
require_dependency File.expand_path("../../../lib/m3/router", __FILE__)

Wie man sieht lade ich hier auch den Namespace M3, da Autoloading an dieser Stelle noch nicht funktioniert und die Lib-Klasse Routes entweder aufgrund des fehlenden Namespaces knallt oder diesen als leeres Modul anlegt. Letzteres kann zu einem schwer zu findenden Fehler führen, da die Modul-Datei im Verzeichnis app dann gar nicht mehr geladen wird.

Wie angedeutet ist dieser Ansatz wenig elegant. Die Require-Statements sollten besser zu einem Zeitpunkt ausgeführt werden, an dem Autoloading bereits zur Verfügung steht. Dann passiert vieles automatisch und übrige Requires können ohne File-Path-Expansions notiert werden:

# <gem>/lib/m3/router.rb

require_dependency 'm3'

class M3::Router
  # ...
# <gem>/config/routes.rb

require_dependency 'm3/router'

Rails.application.routes.draw do
  M3::Router.draw_routes(self)
end
]]>
http://sgaul.de/2016/01/30/rails-konfiguration-in-engine-auslagern/feed/ 0
Bundler überspringt Gems mit Todo-Beschreibung http://sgaul.de/2015/12/12/bundler-ueberspringt-gems-mit-todo-beschreibung/ http://sgaul.de/2015/12/12/bundler-ueberspringt-gems-mit-todo-beschreibung/#comments Sat, 12 Dec 2015 13:43:56 +0000 http://sgaul.de/?p=2851 Weiterlesen ]]> Komisches Verhalten von Bundler, welches bei mir vorher nie aufkam:

$ bundle install
...
Source does not contain any versions of 'gem (>= 0) ruby'

Das Problem scheint hier tatsächlich zu sein, dass die Gem-Spezifikation noch Todos enthält:

Gem::Specification.new do |s|
 # ...
 s.summary = "TODO: Summary of Gem."
 s.description = "TODO: Description of Gem."
end

Entferne ich diese, so läuft die Installation durch.

Ironischerweise warnt Bundler sogar, dass das Gem Todos oder Fixmes in der Spezifikation enthält. Dass es das Verzeichnis deshalb nicht ordnungsgemäß ausliest erschließt sich aber nicht. Hier wäre eine Fehlermeldung statt der Warnung geeigneter. Möglicherweise ist es auch nur ein Bug, eine fehlende Beschreibung rechtfertigt kein Ignorieren eines Gems.

]]>
http://sgaul.de/2015/12/12/bundler-ueberspringt-gems-mit-todo-beschreibung/feed/ 2
Hacker-Serie Mr. Robot http://sgaul.de/2015/11/20/hacker-serie-mr-robot/ http://sgaul.de/2015/11/20/hacker-serie-mr-robot/#respond Fri, 20 Nov 2015 14:42:55 +0000 http://sgaul.de/?p=2842 Weiterlesen ]]> Mir fällt es nicht leicht, mich an neue Serien zu gewöhnen. Die ersten Folgen muss ich mich meist zwingen, um den Einstieg zu finden. Das ist auch bei Mr. Robot nicht anders, die es jetzt bei Amazon Prime gibt. Ein paar Sachen haben mich dann aber doch positiv gestimmt. „Ich habe kein Facebook.“ und „Ich hasse Facebook.“, ein erstes Kennenlernen bei dem die Gnome-oder-KDE-Frage geklärt wird und recht deutliche Kritik an der gesellschaftlichen Überbewertung von Steve Jobs – nette Details die mich durchaus ansprechen. Ich bin gespannt was die ersten Folgen noch so bringen…

]]>
http://sgaul.de/2015/11/20/hacker-serie-mr-robot/feed/ 0
Ubuntu-Update kränkelt nach Owncloud-Installation http://sgaul.de/2015/11/07/ubuntu-update-kraenkelt-nach-owncloud-installation/ http://sgaul.de/2015/11/07/ubuntu-update-kraenkelt-nach-owncloud-installation/#comments Sat, 07 Nov 2015 11:25:34 +0000 http://sgaul.de/?p=2839 Weiterlesen ]]> Nach der Installation des Owncloud-Clients über eine zusätzliche Paketquelle erhalte ich vom Ubuntu-Update-Manager Fehlermeldungen. Ein Update in der Konsole jammert:

sudo apt-get update
...
Paketlisten werden gelesen... Fertig
W: GPG-Fehler: http://download.opensuse.org Release: Die folgenden Signaturen konnten nicht überprüft werden, weil ihr öffentlicher Schlüssel nicht verfügbar ist: NO_PUBKEY 977C43A8BA684223

Das Problem wurde bereits auf Ubuntuusers diskutiert und die vorgeschlagene Lösung funktioniert problemlos:

wget -q http://download.opensuse.org/repositories/isv:/ownCloud:/community/xUbuntu_14.04/Release.key -O- | sudo apt-key add -

Die Updates laufen wieder durch.

]]>
http://sgaul.de/2015/11/07/ubuntu-update-kraenkelt-nach-owncloud-installation/feed/ 3
Neue Star-Trek-Serie 2017 http://sgaul.de/2015/11/06/neue-star-trek-serie-2017/ http://sgaul.de/2015/11/06/neue-star-trek-serie-2017/#respond Fri, 06 Nov 2015 18:28:46 +0000 http://sgaul.de/?p=2837 Weiterlesen ]]> Eine Meldung die trotz großem Interesse fast an mir vorbeiging: Für das Jahr 2017 hat das amerikanische Medienunternehmen CBS eine neue Star-Trek-Serie angekündigt. Der verantwortliche Produzent Alex Kurtzman war allerdings schon bei den letzten beiden Pseudo-Star-Trek-Actionfilmen beteiligt. Misstrauen ist wie beim neuen Star-Wars-Film also angebracht. Zudem sollen die Folgen in den USA weitgehend über einen vergleichsweise wenig relevanten Streaming-Dienst angeboten werden. Auch das klingt nicht wirklich erfolgversprechend.

Viel mehr ist derzeit nicht bekannt. Wann und in welchem Paralleluniversum welches Schiff unterwegs sein wird darf also spekuliert werden.

]]>
http://sgaul.de/2015/11/06/neue-star-trek-serie-2017/feed/ 0
Match API output with YAML fixture http://sgaul.de/2015/10/12/match-api-output-with-yaml-fixture/ http://sgaul.de/2015/10/12/match-api-output-with-yaml-fixture/#respond Mon, 12 Oct 2015 13:23:50 +0000 http://sgaul.de/?p=2834 Weiterlesen ]]> A nice and simple approach to test you JSON APIs:

# spec/features/api/user_spec.rb

require 'rails_helper'

RSpec.feature 'User', type: :feature do
  it 'lists users' do
    user = create(:user, :as_admin)
    visit api_user_path(user, format: :json)
    expect(page).to match_yaml_fixture('api/user')
  end
end

This checks if the page source matches the data given in the fixture file. To make it more readable we format the expected JSON as YAML:

# spec/fixtures/api/user.yaml

---
user:
- id: 1
  first_name: Max
  last_name: Power
  role:
    id: 1
    name: Admin

Add match_yaml_fixture to RSpec

Drop this in your support directory:

require 'rspec/expectations'

module FixtureHelper
  def read_fixture(*path_elements)
    IO.read(Rails.root.join("spec", "fixtures", *path_elements))
  end
end

RSpec::Matchers.define :match_yaml_fixture do |expected|
  match do |actual|
    expected = prepare_expected(expected)
    actual = prepare_actual(actual)
    actual.strip == read_fixture(expected).strip
  end
  failure_message do |actual|
    expected = prepare_expected(expected)
    actual = prepare_actual(actual)
    "expected the following to match fixture file #{expected}:\n\n#{actual}"
  end

  private

  def prepare_expected(expected)
    expected += '.yaml' unless expected.end_with?('.yaml')
    expected
  end

  def prepare_actual(actual)
    actual = JSON.parse(actual.source) if actual.is_a?(Capybara::Session)
    actual = actual.to_yaml if actual.is_a?(Hash)
    actual.to_s
  end
end

Then include the helper in your spec_helper.rb or rails_helper.rb file:

# spec/rails_helper.rb

require File.expand_path('../support/fixture_helper', __FILE__)
RSpec.configure do |config|
  config.include FixtureHelper
end
]]>
http://sgaul.de/2015/10/12/match-api-output-with-yaml-fixture/feed/ 0
Overwriting and Overriding with define_method http://sgaul.de/2015/09/29/overriding-vs-overwriting/ http://sgaul.de/2015/09/29/overriding-vs-overwriting/#respond Tue, 29 Sep 2015 15:06:38 +0000 http://sgaul.de/?p=2827 Weiterlesen ]]> Recently we stumbled upon this inheritance issue, which seemed very weird at the first:

class A
  def talk
    'A'
  end
end

class B < A
  def self.define_talk
    define_method :talk do
      super() << 'B'
    end
  end
end

class C < B
  define_talk
 
  def talk
    super << 'C'
  end
end

> C.new.talk
 => "AC"

The talk addition from class B doesn’t appear, even though define_talk is triggered by Class C. C’s super call seems to ignore its direct parent B.

Overriding vs Overwriting

The reason for that is the delayed execution of define_talk, which adds the talk method to C rather than B. The previous definition of C is equivalent to this:

class C < B
  # calling define_talk is equivalent to
  def talk
    super << 'B'
  end
 
  def talk
    super << 'C'
  end
end

The second definition of talk doesn’t override the first one, it overwrites it: The first definition is completely gone and therefore no longer available as super.

Overriding with Anonymous Modules

To fix this we need to ensure that B defines the method on B or another untouched inheritance layer. This layer can be an anonymous module created and included by the define_talk method:

class B < A
  def self.define_talk
    mod = Module.new do
      define_method :talk do
        super() << 'B'
      end
    end
    include mod
  end
end

Now C’s definition of talk only overrides the one of the anonymous module, which is therefore still available as super.

> C.new.talk
 => "ABC"
]]>
http://sgaul.de/2015/09/29/overriding-vs-overwriting/feed/ 0