Sassの教科書を読書中…

実はこの本を買ってからずっと本棚にしまっていたので、とりあえずざっと見てみようと思って読書中です。Sassはちょっとしか使ったことがなく、定義がネストできるくらいにしか思ってなかったのですが、色々できることがわかってきてちょっと目から鱗です。

  • @extendで定義の継承ができること
  • @mixinで定義の注入ができること
  • 変数が使えること
  • 条件分岐、繰り返し(ループ)が使えること
  • 関数が使えること

Sass用のGUI環境もありました。

http://koala-app.com/

これを使うと、Sass、CoffeeScriptのコードを見張ってくれて自動コンパイルすることができるようです。ターミナルからいろいろと打つのが苦手な人は、Koalaを使うとよさそうです(俺は入れたけれどまだ使ってない)。
プログラマの人はYeomanを使った方がいいかなーという感じはします。
Rails使いの人は特に何もしなくてもいいですね(Railsは楽!)

プログラマががんがん書いてく場合は、関数を使いこなしてもよさそうだけれど、あまりに使いすぎると読めなくなる人も現れる気がするし、ちょっと編集するのが難しくなったりもしそうなので、

  • 基本的なカラーを変数で定義
  • 単純なものを定義するときは変数とループを使ってパッと定義
  • 共通部分を@extendで。無理にmixinを使わない。

くらいのルールがいいのかなと思いました。
可読性が大事だから。

まだ実践Sassコーディングの章を読めてないので、今週中には読んでしまおうと思います。


Kindle版もあるようです。


strong_parametersには:idも追加しよう。

ちょっとハマってたのでメモ書き。rails4。
UserとAddressテーブルを作っていたとして、以下のような関係だとする。

class User < ActiveRecord::Base
  has_one :address, inverse_of: :user
  accepts_nested_attributes_for :address
end
class Address < ActiveRecord::Base
  belongs_to :user, inverse_of: :address
  validates :pref, presence: true, on: :update
  validates :city, presence: true, on: :update
  validates :section, presence: true, on: :update
end

ユーザー情報を更新するときに住所も更新したいので、accepts_nested_attributes_forを設定してある。そうなると、strong_parameterの設定であるが、以下のようだとする。

class UsersController < ApplicationController
  private
    def user_params
      params.require(:user).permit(
        :email,
        :password,
        :password_confirmation,
        {address_attributes: [
          :pref,
          :city,
          :section
        ]}
      )
    end
end

まぁこんな感じでやって、動いていたので大丈夫だと思っていたのだが、編集時に必須にしているpref, city, sectionが空でも素通りされてしまった…。フォームの表示上では必須マークも出ているので何が間違っているのかわからなかった。動作を見てみると、addressの行が毎回削除されて新しい行ができていた。なるほど、これではon: :udateが機能することはないということか…。ではどうすればいいのか?をググっていたら、見つかった。

Can’t update my nested model form for has_one association

address_attributesに:idを追加しろ、だそうだ。

class UsersController < ApplicationController
  private
    def user_params
      params.require(:user).permit(
        :email,
        :password,
        :password_confirmation,
        {address_attributes: [
          :id,
          :pref,
          :city,
          :section
        ]}
      )
    end
end

追加したところ、ちゃんとon: :updateが機能するようになった。


Yeoman+AngularJSをやろうとしたけれど

AngularJSを実践的に使ってみたくて、実家の牧場のサイトをAngularJSを使って作り直そうかと思ってYeomanを入れて書いていってたのですが、問題が発生してしまいました。
まぁなんとなくそうなんじゃないかなと思いながら作ってはいたのですが…。

AngularJSでサイトを作っていると、URLが

http://example.com/#

という感じになります。リンク先は、

http://example.com/#/about/

のようになります。
これってSEO的にどうなるのかなーと思ってぐぐったら、しょーごさんがQiitaに書いてくれていました。

AngularJS の $locationProvider.html5Mode について

Googleならばこの形式のURL(Hashbang URLというらしい)にも対応してるらしいのですが、その他の検索エンジンが対応してない可能性が高いので、コンテンツ系のサイトには向かないよということでした。html5Modeにすれば、既存の通りのURLになるけれど、サーバ側でpjaxに対応した応答をしなければならなくなるという話のようなので、今回は難しそうです。
自分の持っているサーバ上で色々できるのであれば、それでもかまわないのだけれど、今回はプロバイダのサーバ上にあるホームページスペースなので。まぁこの際だからドメインとって自分が管理しているサーバに移管してもいいような気がしてきたなぁ…。

せっかくなので、作り込んでみようかな。


AngularJSのチュートリアルをやってみた。

少しずつですが、AngularJSのチュートリアルをやってみました。
step-8までは写経して、後半は結構はしょってしまいました。

このチュートリアル、gitを使っていて、考えられてるなーと思います。
チュートリアルってよくディレクトリ毎に分けられていたりするんですが、これはgitのbranchをうまく使ってあり、わざわざディレクトリの移動をすることがありませんし、stepごとにdiff(差分)が見られるようになっているので、なにが変わったかも一目瞭然です。

また、Node.jsを使ってサーバーが提供されるので、Webサーバをインストールして云々みたいな仰々しいこともしなくてよいです(Node.jsは入れないといけないけれど)。

AngularJSはmodelとviewのデータバインディングが特徴で、modelを更新したら即viewに反映されるので、なんだか面白いなと思います。また、html内でループを書いたりすることができるし、説明通り、htmlを拡張した構造になっているのが、最初はとっつきにくい印象だったけれど、短く書けるしわかりやすいなと思えてきました。でもまだアプリを作ったことがあるわけではないので、これからなんですが。

jsonを取得してモデルに突っ込んで、それを元にviewをごにょごにょ…という感じだとは思うんですが、サーバサイドはjsonを出力するAPIだけを提供して、あとのレンダリングはAngularJS側で、となると、サーバサイドとクライアントサイドの分業がしやすいかもしれません。データのモックアップもjsonで準備すればいいし、見た目の変更にも強くなりそうです(クライアント側のコードだけ修正すればいいから)。

jQueryのセレクタを使わずに色々できるというのはほんまによさそう!
仕事で使ってみたい!


HerokuでKotlin動くよという話 #kotlin

Kotlin Advent Calendar 2013で空いていた枠を書くようにしていたんだけれど、
全然書けていなかったので、とりあえず書いておきます。
HerokuでKotlinは動くよ、という報告です。

HerokuはPaaSとして有名ですが、いろんな言語をサポートしています。
Javaもサポートされているので、うまいことすればKotlin動くかなと思っていました。

まず、Heroku Javaのサイトに行きます。
Containerless web app with Embedded Jettyから、Create Appを押してアプリを作ります。
すると、Javaアプリが作られます。

Herokuにログインして、gitリポジトリを確認して、そこからcloneしましょう。
heroku

cloneすると、今デプロイされている内容がDLされるので、これを修正していきます。

まず、pom.xmlを修正します。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <version>1.0-SNAPSHOT</version>
    <name>helloworld</name>
    <artifactId>helloworld</artifactId>
    <packaging>jar</packaging>

    <properties>
        <kotlin.version>0.6.1673</kotlin.version>
        <java.version>1.6</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Servlet API -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>

        <!-- Jetty -->
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>7.6.0.v20120127</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>7.6.0.v20120127</version>
        </dependency>
        <dependency>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>jsp-2.1-glassfish</artifactId>
            <version>2.1.v20100127</version>
        </dependency>

        <!-- Kotlin -->
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
    </dependencies>

    <build>
        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals><goal>copy-dependencies</goal></goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>kotlin-maven-plugin</artifactId>
                <groupId>org.jetbrains.kotlin</groupId>
                <version>${kotlin.version}</version>

                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>compile</phase>
                        <goals> <goal>compile</goal> </goals>
                    </execution>

                    <execution>
                        <id>test-compile</id>
                        <phase>test-compile</phase>
                        <goals> <goal>test-compile</goal> </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

そして、mavenでライブラリをインストールします。

$mvn clean install

buildのところでkotlinのディレクトリを設定しているので、そこにディレクトリをつくります。

$ cd src/main
$ mkdir kotlin
$ cd ../
$ mkdir -p test/kotlin

src/main/javaディレクトリの内容をsrc/main/kotlinにコピーして、Kotlinに直します。

package com.example

import org.eclipse.jetty.server.Server
import org.eclipse.jetty.webapp.WebAppContext

public fun main(args : Array<String?>?) {
    val webappDirLocation = "src/main/webapp/"
    var webPort : String? = System.getenv("PORT")
    if (webPort == null || (webPort == "")) {
        webPort = "8080"
    }
    val server = Server(webPort!!.toInt())
    val root = WebAppContext()
    root.setContextPath("/")
    root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml")
    root.setResourceBase(webappDirLocation)
    root.setParentLoaderPriority(true)
    server.setHandler(root)
    server.start()
    server.join()
}
package com.example

import java.io.IOException
import java.util.logging.Logger

import javax.servlet.ServletConfig
import javax.servlet.ServletException
import javax.servlet.ServletOutputStream
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

class HelloServlet() : HttpServlet() {
    protected override fun doGet(req : HttpServletRequest?, resp : HttpServletResponse?) {
        var out : ServletOutputStream? = resp?.getOutputStream()
        out?.write("Hello Heroku".getBytes())
        out?.flush()
        out?.close()
    }
}

その後、src/main/javaディレクトリを削除します。

あと、デフォルトのindex.jspだと、twitter bootstrapの古いやつを参照しようとしているので、なんとなくいやなのでとりあえず真っ新なhtmlにしてみます。

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Kotlin on Heroku</title>
    <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
Kotlin on Heroku.
</body>
</html>

あとは、Procfileを修正します。com.example.Mainを、com.example.ExamplePackageにします。

web:    java $JAVA_OPTS -cp target/classes:target/dependency/* com.example.ExamplePackage

準備ができたと思うので、コンパイルしてみましょう。

$ mvn clean install compile

ローカルで試してみましょう。Procfileに書いた感じで実行してみます。

java -cp target/classes:target/dependency/* com.example.ExamplePackage

http://localhost:8080/ と、http://localhost:8080/hello にアクセスして動いたら、OK!

そして、デプロイ。Herokuのリポジトリからcloneしているので、herokuがoriginになっています。

$ git add .
$ git commit -m "Kotlinで動くようにしてみた"
$ git push origin master

うまくいけば、Heroku側でも動くはず。pushしてもちゃんと動いてない場合は、一度再起動してみましょう。

$ heroku restart

無料でサーバサイドKotlinが試せる環境があるというのは非常にありがたいことですねぇ。
なんか作ってみたいけれど、どうしたものか…。