NSRailsを使ってみた

7月1122日に行う 「クラウドxスマフォ時代のRuby on Rails入門」 セミナーで使うデモアプリを作るために、NSRails を使ってみました。


概要

NSRails の使い方は https://github.com/dingbat/nsrails に書かれているように、iOS側に Rails と同じモデルを用意し、 モデルクラスの 取得(remoteAll: , remoteObjectWithID: ...)やCRUD(remoteCreate: , remoteUpdate: , remoteDestroy: ...) メソッドを呼び出すだけで、Railsサーバーとのデータやり取りが出来ます。また通信は、同期、非同期をサポートしています。

使ってみて分かったこと

1. Pod は GitHub から

Railsサーバーが起動してないときにiOS側で通信するとへんなエラーで落ちますが GitHubのmasterでは修正されているのでPodfileは以下のようにした方が良いでしょう。

  pod 'NSRails', :git => 'https://github.com/dingbat/nsrails.git'
2. autogen/generate がモデルの雛形を作ってくれて超便利

NSRailsのGitHubにある autogen/generate コマンドで Railsプロジェクトのパスを指定すると、そのプロジェクト内の全モデルに対応する iOS側のモデルの雛形を作ってくれて、超便利です。

./autogen/generate RailsProject
Making directory RailsProject.gen/
Writing files to /Users/yy/tmp/nsrails/autogen/RailsProject.gen
  + Category.h
  + Category.m
  + Customer.h
  + Customer.m
   ...
3. iOS側モデルは必ずしもRailsのモデルと同じで無くても良い

Rails側とiOS側での処理分担は思案のしどころですが、必ずしも一致される必要はありません。iOS側モデルは Rails側のview (〜.json.jbuilder) と対応していれば良いので、iOS側を簡単にしたい場合はviewでたくさんの情報を作り、iOS側はそれを利用する事もできます。今回作っているデモアプリは下のように、簡単な処理も Rails 側でやって渡しています。

json.array!(@timelines) do |timeline|
  json.extract! timeline, :id, :user_id, :caption, :created_at
  json.name timeline.user.name
  json.title photo_title(timeline)
  json.photo_url full_url(timeline.photo.url)  if timeline.photo.present?
  json.photo_thumb_url full_url(timeline.photo.thumb.url)  if timeline.photo.present?
end
  • TimeLine.h
#import <NSRails/NSRails.h>

@interface Timeline : NSRRemoteObject
@property (nonatomic, strong) NSString *caption;
@property (nonatomic, strong) NSNumber *userId;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *photoUrl;
@property (nonatomic, strong) NSString *photoThumbUrl;
@property (nonatomic, strong) NSString *imageData;
@property (nonatomic, strong) NSDate   *createdAt;
@end
4. 戻さなくて良いプロパティー(インスタンス変数)が指定出来る

NSRailsのCookbookiOS側モデルに付いて色々と書かれています。 shouldSendProperty: メッソドをオーバーライトして、iOSからRailsには戻さないプロパティーを指定できます。これを使い、3. のように余計な情報をRailsから送ってもらってる場合は、それらをshouldSendProperty: に指定しておけば、通信コストを小さくできます。

  • TimeLine.m
#import "Timeline.h"

@implementation Timeline

- (BOOL) shouldSendProperty:(NSString *)property whenNested:(BOOL)nested
{
    if ([property isEqualToString:@"title"] || [property isEqualToString:@"name"] ||
        [property isEqualToString:@"photoUrl"] || [property isEqualToString:@"photoThumbUrl"])
        return NO;
    return [super shouldSendProperty:property whenNested:nested];
}

@end

その他、プロパティー名をRails側と変えたいとか、型を変えるとか・・・ 色々な事ができますので NSRailsのCookbook は一読しておくと良いと思います。

5. 画像データをJSONで送りたい

今回のアプリでは、iOS側から画像データをRails側に送る必要があるので、StackOverflow 等を調べ以下のようなコードを書きました。

iOS側は画像データをBase64エンコードして送ります。

    ....
    Timeline *newTimeline = [[Timeline alloc] init];
    newTimeline.caption = post.caption;
    newTimeline.imageData = [self base64FromImage:post.image];
    [newTimeline remoteCreateAsync:^(NSError *error) {
        if (error) {
            NSLog(@"+++ err %@", error);
        } else {
            NSLog(@"+++ ok : id=%@", newTimeline.remoteID);
        }
    }];
    ....

- (NSString *)base64FromImage:(UIImage *)image
{
    return [UIImagePNGRepresentation(image) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
}


Rails側は、image_dataの送られて来たBase64エンコードされた画像データをデコードし StringIOで受け取り、その IO Stream を file_field に対応する photo フィールド に代入しています。この後の処理は Carrierwave で行っています。Carrierwaveの処理では IO Stream に path メソッドが必要なようなので、特異メソッドとして追加してます。

ネット上にあるサンプルコードでは Tempfileクラスを使って、テンポラリーファイルに書くコードを多く見かけましたが、それほど大きな画像は送られてこないので、ファイルを作らない StringIO を使ってみました。

  def create
    @timeline = Timeline.new(timeline_params_with_image_data)
    if @timeline.save
      .....

    def timeline_params_with_image_data
      parameters = timeline_params
      if params[:timeline][:image_data]
        parameters[:photo] = parse_image_data(params[:timeline][:image_data])
      end
      parameters
    end

    def parse_image_data(image_data)
      tempfile = StringIO.new(Base64.decode64(image_data))

      def tempfile.path
        "/tmp/upload.png"
      end

      ActionDispatch::Http::UploadedFile.new(tempfile: tempfile, content_type:'image/png', filename:'iphone.png')
    end
6. Basic認証

Rails側でiOSアプリを認証するの部分は、今回は実装が簡単な Basic認証を使ってみました。生パスワードを送っているので実際のアプリで使う場合は SSL 通信が必要ですね。

NSRailsには Basic認証用ヘッダーを送る機能を持っているのでNSRConfigに ユーザー、パスワードを設定するだけです。

    [NSRConfig defaultConfig].appUsername = login.email;
    [NSRConfig defaultConfig].appPassword = login.password;

Rails側は 定番の devise を使っています。コードは deviseのHow To にあったものです。

class TimelinesController < ApplicationController
  before_action :authenticate

  ....

  private
    ....
    def authenticate
      if from_iphone?
        authenticate_or_request_with_http_basic do |username,password|
          resource = User.find_by(email: username)
          if resource && resource.valid_password?(password)
            sign_in :user, resource
          end
        end
      else
        authenticate_user!
      end
    end
end
7. ログの制御

デフォルトではコンソールに通信内容も表示されます、画像を送ったりすると大変な量のログが表示されるので、ログを制限した方が良いと思います。手っ取り早いのは NSRailsの NSRails.h ファイルの以下の定義を変える事です

//					As undefined, NSRails will log nothing
// #define NSRLog 1	//As 1, NSRails will log HTTP verbs with their outgoing URLs, as well as any server errors
#define NSRLog 2	//As 2, NSRails will also log any JSON going out/coming in
8. 通信中表示

NSRConfigのmanagesNetworkActivityIndicator を YES に設定すると通信中にステータスバーに 通信中のグルグルを表示してくれるので便利

[NSRConfig defaultConfig].managesNetworkActivityIndicator = YES;


どんな物が出来たか興味のあるかたは 「クラウドxスマフォ時代のRuby on Rails入門」 セミナー にご参加下さい。転職・人材系会社でのセミナーですが・・・あまり気にしなくて良いと思います。

GitLab API (gitlab gem)を使ってみました

今年の新人研修で課題の提出場所に GitLabを使ってみました。生徒の人数分 x 課題数のリポジトリーが作られるので、課題結果を自分の環境にcloneするのを手動で行っているとたいへんです。そこで、GitLab APIを使い、簡単なツールを作ってみました。

GitLab APIには gitlab gem があり、簡単に使う事ができます。しかし、いざ自分のやりたいプログラムを書こうと思ったら・・・・

  1. APIドキュメントはあるが、内容は今ひとつ
  2. サンプルコードが少ない
  3. ネット上 (英語の情報を含め)にも情報が少ない

という事で、gitlab gem の RSpecコードなどを見ながら勉強しました。また、APIの設計があまりオブジェクト指向的でなく、最初は戸惑いました。

以下のコードは、 あるグループに所属する全員の レポジトリーの一覧を表示し、そのリポジトリをアクセスするために自分のアカウントを管理者として登録し、全リポジトリーを clone するコードです。

PRIVATE_TOKEN は管理者(root)アカウントの PRIVATE_TOKEN を指定して下さい。

#!/usr/bin/env ruby
require 'gitlab'

GUEST_LEVEL       = 10
MASTER_LEVEL      = 40
GITLAB_SERVER_URL = 'http://GITLAB_SERVER'
GITLAB_API_URL    = "#{GITLAB_SERVER_URL}/api/v3"
PRIVATE_TOKEN     = 'xxxXXXXXXXXXXxxXXXXn'
GROUP_NAME        = 'student'
MY_NAME           = 'yuumi3'


def get_group_members(gitlab_root, group_id)
  gitlab_root.group_members(group_id).select{|e| e.access_level == GUEST_LEVEL}.map(&:username)
end

def add_member_to_projects(gitlab_member, user_id)
  gitlab_member.projects.each do |project|
    unless gitlab_member.team_members(project.id).find {|m| m.id == user_id}
      gitlab_member.add_team_member(project.id, user_id, MASTER_LEVEL)
      puts " add users to #{project.path_with_namespace}"
    end
    
  end
end

def list_all_projects(gitlab_member)
  gitlab_member.projects.each do |project|
    puts "  #{project.path_with_namespace}"
  end
end

def clone_all_projects(gitlab_member)
  gitlab_member.projects.each do |project|
    unless File.exists?(project.path_with_namespace)
      cmd = "git clone git@#{GITLAB_SERVER_URL}:#{project.path_with_namespace} #{project.path_with_namespace}"
      puts "  #{cmd}"
      system cmd
    end
  end
end


gitlab_root = Gitlab.client(endpoint: GITLAB_API_URL, private_token: PRIVATE_TOKEN)
username_to_id  = Hash[*gitlab_root.users.map {|u| [u.username, u.id]}.flatten]
groupname_to_id = Hash[*gitlab_root.groups.map {|u| [u.name, u.id]}.flatten]

members = get_group_members(gitlab_root, groupname_to_id[GROUP_NAME])

# 生徒のrepository一覧
members.each do |member|
  gitlab_member = Gitlab.client(endpoint: GITLAB_API_URL, private_token: PRIVATE_TOKEN, sudo: member)
  list_all_projects(gitlab_member)
end

# 生徒のrepositoryに私を管理者として追加
members.each do |member|
  gitlab_member = Gitlab.client(endpoint: GITLAB_API_URL, private_token: PRIVATE_TOKEN, sudo: member)
  add_member_to_projects(gitlab_member, username_to_id[MY_NAME])
end

# 生徒のrepositoryをclone
members.each do |member|
  gitlab_member = Gitlab.client(endpoint: GITLAB_API_URL, private_token: PRIVATE_TOKEN, sudo: member)
  clone_all_projects(gitlab_member)
end

Cocos2d-xのサンプルコードを試すさいに注意すること!

わけあって Cocos2d-xを勉強しています、入門書を買いサンプルコードをダウンロードしたのですが、コンパイルエラーや画像(Sprite)の大きさがおかしい問題に2日間も悩まされましたが、やっと解決出来たので書いておきます。

Cocos2d-xのバージョンは v2.2.3 、開発環境は Mac OS 10.9.2です。

http://www.cocos2d-x.org/images/orgsite/logo.png

サンプルコードを動かすまでの一般的な手順

  1. Xcode をインストール (Xcode5.1 を使いました)
  2. Cocos2d-x (v2.2.3) を http://www.cocos2d-x.org/download からダウンロード、適当なフォルダーに展開
  3. 下のように create_project.py でプロジェクトを作成、このプロジェクトは HelloWorld画像を表示できる雛形です
  4. Xcode出来たプロジェクトをオープンしビルド、 HelloWorldが表示される事を確認
  5. 入門書のサンプルコードをダウンロード
  6. 上で作ったプロジェクトの Classes内のファイルを消し、書籍のサンプルコードの Classes 内のファイルをコピー
  7. 上で作ったプロジェクトの Resources に書籍のサンプルコードの Resources 内のファイル、フォルダーをコピー
% cd cocos2d-x-2.2.3
% cd tools/project-creator
%  ./create_project.py -project SampleGame -package com.MyCompany.SampleGame -language cpp

ところが・・・・

  • 4. でコンパイルエラー! → Classes内のヘッダーファイルが見つからないエラーなので、ヘッダーサーチーのパスを追加
  • 5. でXcode上でファイルがコピーできない!→ ターミナル上で cp でコピー
  • サンプルーコードが動き出したのですが、なぜか画像やSpriteの大きさが本来の画像より大きかったり小さかったり・・・・ ???

これから2日弱の格闘が・・・

注意点

Macのディスクのフォーマットが 大文字/小文字を区別する場合の注意

Macのディスクはフォーマット時に Mac OS 拡張(大文字/小文字を区別、ジャーナリング を選択すると unixのように大文字と小文字を区別します。Unix系のツールを多く使う人はこの方が幸せになれます。たぶんデフォルトは区別しないファイルシステムです(ただしMac OS が工夫して一見 区別してるように振る舞っています)。

create_project.py が作ったプロジェクトのClassesグループは実は下の画像のように 小文字 classes フィルダーリンクしています!

viなどで SampleGame.xcodeproj/project.pbxproj を直接開き、../classes を ../Classes に書き換えたら 4, 5 の問題が解決できました。
project.pbxproj は テンプレートの template/multi-platform-cpp/proj.ios/HelloCpp.xcodeproj/project.pbxproj を変更してしまうのが良いと思います。

追加: pull request がマージされたので次ぎのバージョンでは直ってると思います。

Resources(リソース)のコピーは Create folder reference for any added folders を使う!

画像やSpriteの大きさが本来の画像より大きかったり小さかったりする問題の解決方法は、Cocos2d-x:絶対にわかるマルチ解像度(マルチディスプレイ)対応 に書かれていました。

cocos2d-x はいろいろな解像度のデースプレーに対応出来るように複数の解像度のリソースを用意し解像度により切り換える仕組みを持っています。サンプルコードにも複数の解像度用のリソース(画像)がフォルダー(例えば hd, sd)を分けて用意されています。

7. の Resources のコピーの際には何気なくXcodeの Create groups for any added folders を使っていました。この設定の場合、解像度別のフォルダーはXcode上ではグループになります(ディスク上ではフォルダーになっていますが)。これをiOSシュミレータにインストールするとフォルダーは無くなり、1つの解像度のファイルのみコピーされてしまいます (>_<)ゞ

まとめ

ということで、一般的な手順で無事にサンプルコードが動くようになりました。

今回参考にした書籍は

cocos2d-xによるiPhone/Androidアプリプログラミングガイド (for Smartphone Developers)

cocos2d-xによるiPhone/Androidアプリプログラミングガイド (for Smartphone Developers)

Cocos2d-x by Example Beginner's Guide

Cocos2d-x by Example Beginner's Guide

とても良い本です。Cocos2d-x by Example Beginner's Guideのサンプルは2.0用ですが 2.1用のアップデート情報が http://rengelbert.com/blog/cocos2d-x-book-update/ ここにあり、2.2.3 でも動きます(全て試したわけではありませんが)。

Feature specs で、たくさんの手順があるWebアプリの受け入れテストを書いてみた

ログインして、販売伝票ボタンを押し、商品追加ボタンを押し、表示された商品ページでカテゴリーを選択し、商品の一覧を表示し選択ボタンを押し・・・ ・・・ ・・・ 確定ボタンを押す。

のようなたくさんの手順で一つの作業が完結するようなWebアプリの受け入れテスト(顧客テスト、総合テスト…)を RSpec + Capybara の Feature specs を使って書いてみました。

http://imagery.pragprog.com/products/140/achbd_xlargecover.jpg

通常このようなテストには Cucumber や Turnip が使われる事が多いですが、どちらも feature(テスト記述) と sptep(コード)に分かれていています。 顧客や開発者以外の人が feature の作成やレビューに関わる場合はメリットがありますが、開発者だけがテストに関わる場合は面倒なだけです。
そこでプログラマーの大好きな RSpec だけで、受け入れテストを書ける Feature specs なのですが、Feature specs には最初に書いたようなたくさんの手順を踏むテストを書くのは苦手です。

RSpec は テストの it "..." do ... end に簡潔なテストを書くのが基本で、通常は DBの値やセッション(Feature specsやController specsなど)は it 単位で初期化されます。rspec:install でセットアップすると it は乱数で毎回違う順に実行されます。
したがって、 受け入れテストの手順を it (Feature specs では scenario) 単位で書いて並べる分けにはいきません。長い手順を一つの it に書かないと行けません。

しかし、長い手順を一つの it に書いてはメンテナンスが極度に下がります・・・・・ そこで、ネットを探していたら RSpec Steps というものを見つけましたが、まだ RSpec 3.0 には対応してないようです。

そこで、設定やパッチ(!?)を駆使して、手順を scenario (= it ) 単位で書けるようにしてみました。 この際に問題になったのは

  1. scenario がランダムに実行される
  2. DBの値が scenario 単位で初期化される
  3. セッションが scenario 単位で初期化される

1. は設定で config.order = "defined" とすれば解決
2. は設定 config.use_transactional_fixtures = false とし before(:all) で FactoryGirlを実行すれば問題ありません。ただしrspec終了後も DBが残るの、 after(:all) でDB をクリアすれば解決しました。
3. は stackoverflow のコメントにあったヒントから Capybara のソースを見て、以下のようなパッチを書き解決しました :-)

def Capybara.reset_sessions!
end

ちなみに、環境は Ruby2.1.1, Ruby on Rails 4.1.0rc2, RSpec 3.0.0.beta2, RSpec Rails 3.0.0.beta2, Capybara 2.1.1, Poltergeist 1.5.0, Factory_girl_rails 4.4.1 です (他の環境では全く試していません)。

specファイル

長いので後半は省略しました

require 'features_helper'

feature "販売伝票作成", js: true  do
  include_context "seed_data"

  scenario "伝票入力ページが表示される" do
    login
    click_menu('販売伝票')
    expect(page.text).to match "販売伝票"
    expect(find_button("商品追加")).to be_truthy
  end

  scenario "商品追加をクリックすると商品選択が表示される" do
    click_on("商品追加")
    expect(find("h4", text: "商品選択").visible?).to be_truthy
  end

  scenario "商品分類を選択すると商品が表示される" do
    within ".modal-content" do
      select "キュロット・女性用", from: "商品分類"
      wait_css("table")

      data, count = parse_data(return_row_count: true)
      expect(count).to eq 5
      expect(data[0][3]).to eq "カバロダービー F マリーン 40"
    end
  end

  scenario "商品を2つ選択でき、商品選択を閉じる" do
    within ".modal-content" do
      click_within_table('選択', row:1)
    end
    wait_css("#slip_line_list tbody tr")
    within ".modal-content" do
      click_within_table('選択', row:2)
     end
    wait_css("#slip_line_list tbody tr:nth-child(2)")
    within ".modal-content" do
      click_within_table('選択', row:3)
     end
    wait_css("#slip_line_list tbody tr:nth-child(3)")
    click_on("閉じる")
    wait_not_css(".modal-content")
  end

  scenario "選択した商品が伝票に表示されている" do
    ・・・・
  end

  scenario "選択した商品の個数を変更できる" do
    ・・・・
  end

  scenario "選択した商品を削除できる" do
    ・・・・
  end

  scenario "お客様、値引、備考を入力し、確認画面へ" do
    ・・・・
  end

  scenario "確認画面に商品一覧と合計金額等が表示されている" do
    ・・・・
  end

  scenario "確定ボタンを押すと、伝票一覧ページが表示される" do
    click_on "確定"

    data = parse_data()
    expect(data.map{|e| [e[1], e[3]]}).to eq [
      ["販売", "山田次郎様 むけ ¥47,000"],
      ["販売", "山田太郎様 むけ ¥57,000"],
      ["発注", "C0016-0000 など 5件"]]
  end

end

設定ファイル features_helper

モデル等の rspec には影響が出ないように Features spec 用の設定ファイルを作って、その中でいろいろとやっています。
データの作成 setup_seed_data は FactoryGirl で初期データを作るメソッドで、cleanup_seed_dataは 全テーブルのデータを削除するメソッドです。

require 'spec_helper'
require 'capybara/poltergeist'
require_relative 'features_seed_data'

Capybara.javascript_driver = :poltergeist

# Force the ActiveRecord to use the same transaction for all threads monkey patch
# https://github.com/jnicklas/capybara/blob/master/README.md#transactions-and-database-setup
#
class ActiveRecord::Base
  mattr_accessor :shared_connection
  @@shared_connection = nil

  def self.connection
    @@shared_connection || retrieve_connection
  end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection

RSpec.configure do |config|
  config.order = "defined"
  config.use_transactional_fixtures = false
end

def Capybara.reset_sessions!
  # ワイルドだろ〜
end

shared_context "seed_data" do
  before(:all) do
    setup_seed_data

  end
  after(:all) do
    cleanup_seed_data
  end
end

#
# helpers
#

def wait_css(css)
  until has_css?(css)
    sleep 0.5
  end
end

def wait_not_css(css)
  while has_css?(css)
    sleep 0.5
  end
end


def login(email: "xxxx@yyyy.com", password: "ppppp")
  visit root_path
  fill_in "メール", with: email 
  fill_in "パスワード",     with: password
  click_button "ログイン"
  page.text
end

def click_menu(label)
  within("ul.nav") do
    click_link(label)
  end
end

def screenshot(suffix = "")
  save_screenshot("/tmp/PhantomJS#{suffix}.png", :full => true)
end

def parse_data(return_row_count:false)
  row_count = page.all('table tbody tr').count
  col_count = page.all("table thead th").count
  all_td = page.all("table tbody td").map {|e| e.text()}

  table = []
  for row in 0..(row_count - 1)
    table << all_td.shift(col_count)
  end
  return_row_count ? [table, row_count] : table
end

def click_within_table(label, row:0)
  within("table tbody tr:nth-child(#{row})") { click_on(label) }
end

さくらのクラウドでサーバーのIPアドレスを変えずにサーバーを入れ替える方法

クラウドでサービスインしているサーバーのOSやサーバー環境(RDB、Webサーバー、言語・・・)を更新するには、新たにサーバーを準備し更新した環境を作りサーバーを入れ替えるのがダウンタイムが少なく良いですよね。Amazon EC2 には Elastic IP アドレス という機構があり、保持しているIPアドレスに任意の EC2サーバーを割り当てられるので、同一IPでのサーバー入れ替えは簡単です。
しかし、さくらのクラウド にはElastic IP アドレスのような機構は無く、同一IPでのサーバー入れ替えは簡単には出来ませんが、短時間のダウンタイムで入れ替える事が出来る事が分かりました。


方法

さくらのクラウドは、サーバーを作成すると(グロバールな)IPアドレスが付いてきますが、既に使っているサーバーのIPアドレスを再割り当ては出来ません。サーバーを作成する際にストレージ(ディスク、SSD)も作成しますが、ストレージはサーバーから切り離し別々に管理で来ます。
また、ストレージ管理メニューの「ディスク修正」でストレージ内のOSに設定されているホスト名、IPアドレス等を書き換える事が出来ます。この「ディスク修正」を使う事で新たなサーバー用に作ったストレージのIPアドレスを書き換え、従来のサーバーにつなぎ替える事で同一IPで新しいOS/サーバー環境に入れ替える事が出来ます。

手順

1. 新しいサーバーを作成し、新しい環境を構築する

クラウドの管理画面でサーバーを作成し、起動したら Chef などで新しい環境を構築します。

2. 新しいサーバーを止めて、ストレージを外す

クラウドのサーバー管理メニューでサーバーからストレージを外します


3. ディスク修正でIPアドレス等を変更する

クラウドのストレージ管理メニューのディスク修正で旧サーバーと同じ値を設定する


4. サービスインしているサーバーを止め、ストレージを付け替える

クラウドの管理画面で現在サービスインしているサーバーを止め、 2.と同様にストレージを外し、下の画像のように 新サーバーに付いていたストレージを割付け、サーバーを起動。
この作業の間はサービスが停止しますが、予め練習しておけば時間は数分だと思います。

以上です。

おまけ、さくらのクラウドの感想

お客様のサービスをさくらの専用サーバーからクラウドに移行して運用していますが、Amazon EC2と比べた感想は

良い点

  • 値段が安い。一時的な利用ではなく、基本的に動かしたままの場合はサーバーの利用料が安い
  • 同一価格で比較すると、サーバーが早くてインストール等が早く済む。CPUやメモリー容量も多い上、SSDも使えるので早いです。
  • 質問にはちゃんとした回答がある (Amazonでは質問したことが無いので比較はわかりません)

悪い点

  • 管理コンソールが使いにくい。Webベースの管理コンソールはバージョンアップし、使いやすさは向上していますが、まだまだAmazonの比べると使い勝手が良くない
  • 技術情報が圧倒的に少ない! 管理コンソールのヘルプは画面操作のみですし、FAQも簡単な解説のみです。ちゃんとした解説はさくらのナレッジ ぐらです(?)
  • Linuxの知識がより必要。Amazonにくらべ管理コンソールで出来ない事もあり、Linuxのシステム管理の知識がAmazonより必要になります。

最後に、今のところ大きな問題もなく安くて快適なさくらのクラウドには満足しています。

あけましておめでとうございます、EY-Officeはこんなお客様を探しています。

あけましておめでとうございます。娘の書いた年賀状の絵が素晴らしいのでお年賀の挨拶にはっておきます。


EY-Officeはこんなお客様を探しています

いよいよ本題です。昨年は全力で教育をやってきましたが、今年は教育に加え開発も力を入れていきたいと思います。
そこで、お客様の募集です。みなさまの近くに、こんなお客様はいらっしゃらないでしょうか?

  • 新規の事業(サービス)を始めたいが、事業に必要なシステムの開発者が見つからない
  • SIerに見積もりを取ったけど高すぎる
  • システムの仕様を書けと言われるがどうしら良いのか判らない
  • 開発期間が長すぎて、すぐに事業が始められない

EY-Officeでは、このようなお客様に、システムを比較的安価で、なるべく早く事業を開始できるような形で提供できます。

EY-Officeで提供出来るシステム

  • お客様にお伺いし、事業(サービス)内容をお話いただき、プロトタイプを作る事でシステムの仕様を決めて行きます
    • お客様は事業(サービス)のイメージ、重要な点、事業計画をお話下さい
    • お伺いした結果から、こちらで重要な部分の一部(プロトタイプ)を作ります。それを元に、お客様とさらに仕様を詰めて行きます。
  • 事業(サービス)に必要な最低限の機能のみを作り早くサービスを開始できるようにします
    • 実際にサービスを始めてみないと、判らない事も多いと思います。その場合は開発をフェーズ分けし、サービスに必要な部分のみ作り、サービスを早く開始できるようにします
  • 適当なタイミングで見積もりを差し上げます
    • お話をお伺いしただけでは、漠然とした見積もりしか出せない場合もありますが、なるべく早いタイミングでお見積もり差し上げます
    • 開発をフェーズ分けした場合は、フェーズ毎に見積もります
  • 多少の仕様変更はかまいません
    • 実際に作ってみないと判らない事は多くあります、プロトタイプは仕様を決めるためのものですので、プロトタイプを見てからの変更は問題ありません
    • また、開発が始まってからも多少の変更はお受けします
    • ただし、開発にとって変更の影響が大きい場合は、スケジュール調整・再見積をお願いする場合があります
  • サービスを動かす、サーバーはクラウドVPS等から適切なものを提案いたします
    • サーバーの設定、ソフトのインストール等も行います
    • また、サーバーの基本的な監視やトラブル対応も行います
  • デザインに付いてはご相談させて下さい
    • EY-Officeにはデザイナーはおりませんので、お客様で用意して頂くか。または、適切なデザイナーを探します
    • お客様で用意して頂く場合には、PhotoshopIllustrator で作成したものでかまいません
  • 開発成果・運用情報を引き継ぎ可能なように準備いたします
    • EY-Officeは私一人で開発している会社です。私が万一開発や運用が続けれない場合は、他の開発会社にプログラム・運用情報を引き継げるようにしておきます
    • プログラム(ソースコード)はgitで管理し、またサーバーの管理情報もChef等を使い引き継ぎ可能な状態にします
  • お客様とは長くお付き合いしたいです
    • サービスのリリース後、一定期間は不具合の対応や簡単な変更に応じます
    • 一定期間終了後は、一定のメンテナンス料を頂き、不具合の対応や簡単な変更を行います
    • メンテナンス料の範囲内で出来ないような変更の場合は、別途お見積もりさしあげます
    • 技術的な件でのご相談はお受けいたします

少し条件があります

  • システムはインターネット(クラウド)上で動くもの(例えばネットショップやネットを使った新たなビジネスなど)
    • お客様の会社内のみで動くシステムは残念ながら出来ません
  • 打ち合わせなど必要があれば、お客様の会社にお伺いしますが、開発は基本的にEY-Office社内で行います
  • 大規模なシステム
    • どのくらいからが大規模なのかの定義は難しいですが 100画面ぐらのシステムは問題ないです
  • プログラミング言語フレームワーク等はEY-Officeで決めます

実績

いままでに、EY-Officeで作ってきたシステムの例です

ネットショップ

http://www.boro.co.jp

  • ネットショップ
  • ショッピングカート・カード決済
  • 各種割引、ポイント管理
  • 商品、在庫、顧客の管理
  • 外部在庫管理システムとの連携
  • 特徴:管理画面の充実、長年の機能追加に耐える設計・実装
  • フレームワークRuby on Rails
タレントさんとSkypeでお話ができるサービスの予約管理システム


  • お話の予約管理
  • 会員管理
  • タレントさんの予定管理
  • 入金・ポイントの管理
  • サービスは、お客様の都合で終了しました
  • 特徴:お客様の事業計画にあわせ、短納期で開発
  • フレームワークRuby on Rails
iPhoneアプリのアプリ内課金コンテンツの管理システム

https://itunes.apple.com/jp/app/kotobanku-guo-yu-ci-dian-ying/id417674208?mt=8&uo=4&at=10l3IF

  • 有料コンテンツの管理
  • ダウンロード状況のレポート
  • アプリ内広告の管理
  • 注:初期版の設計・開発・運用サポート
  • 特徴:高負荷に備えてのサーバーの分散
  • フレームワークRuby on Rails
スポーツ競技の成績集計システム

http://www.alhockey.jp

  • 試合スコアーの登録
  • 選手の各種成績の集計
  • チームの各種成績の集計
  • チーム・選手・試合の管理
  • 注:サーバー管理は別会社
  • 特徴:アイスホッケーの複雑な集計ルールに対応
  • フレームワーク:独自のPerlフレームワーク
SNS


  • プロフィール
  • 日記
  • コミュニティー・掲示板
  • メッセージ
  • コミュニティーなどの管理機能
  • サービスは、お客様の都合で終了しました
  • 注:初期版の設計・開発・運用サポート
  • 特徴: Javaで作ったわりには短納期
  • フレームワーク: Seaser2

墓頭(ボズ)

墓頭

墓頭を読みました。内容はAmazonの内容紹介にあるようにドキドキしながら一気に読みたくなる小説です。

1955年。頭に、双子の片割れの死体が埋まったこぶを持って生まれ、周りの人間を次々と死に追いやる宿命を背負った男―ボズ。異能の子供ばかりを集めた福祉施設「白鳥塾」に収容され育つが、そこで出会った少年少女―ヒョウゴ、シロウ、ユウジン、アンジュらによって、ボズの運命は大きく変わっていく―。70年代の香港九龍城、80年代のカンボジア内戦を経て、インド洋の孤島での大量殺戮事件にいたるまで―底なしの孤独と絶望をひきずって、戦後アジアの50年を生きた男の壮大な神話が、いま開幕する。

壮大な物語ですが、巧妙な最後にはなぜか爽快に気持ちになれる良い物語でした。
それから映画で見てみたいです、原作を完全に再現すにはかなりお金がかかるでしょうけど、ややB級な作りでもスリリングな感じが出ていれば是非みてみたいです。