エンジニアになりたい文系大学生

プログラミングを始めてからもうすぐ1年。iOSアプリ開発が中心。最近Webサービスの開発をやりたいのでRuby on Railsに触れ始めてます。

【Ruby】正規表現

今まで避けてきた正規表現

避けられない事態になってやっておけばよかったと後悔...

基本

今回使った表現法を一覧に。

表現法 意味
^ 開始位置
$ 終了位置
[abc] abcの1文字
[a-z] aからzの1文字
[a-zA-Z] aからzAからZの1文字
[0-9] 0から9の1文字
[2-7] 2から7の1文字
. 何でも1文字
\d 数字1文字
\D 数字以外の1文字
(...) ...を1つにまとめる
(a|b) aまたはb
a{3} aが3つ(aaa)
a{3,5} aが3~5つ(aaaaaaaaaaaa)
#{variable} variable変数の埋め込み

実際に使う時には、上記の一覧から組み合わせて/で挟んで使います。

実践

Stringという文字列が与えられた場合

str = 'String'
if str =~ /./
  puts "一致"
else
  puts "一致しない"
end

これがどうなるかというと、

結果 => "一致"

一致します。(知らずに苦労しました。)

完全に一致した時だけtrueを返したい時は、

str = 'String'
if str =~ /^.$/
  puts "一致"
else
  puts "一致しない"
end

今回の結果は、

結果 => "一致しない"

^$で挟めば、完全に一致という条件が付きます。

IPアドレス

やりたかったことは、IPアドレスかの判断。

こんな感じになるかと思います。

/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/

【Rails】Grapeで最速API開発

最近、grapeを用いてapiを作っています。
最速と言っても他のを試した事はないんですが、かなり早くできたので...

初期設定のところから記載してきます。
jbuilderを使ってresponseのjson生成してます。

設定

1. Gemfile

# Gemfile
gem 'grape'
gem 'grape-jbuilder'

Gemfileに記入後、いつも通りインストール。

$ bundle install

2. application.rb

apiのフォルダを読み込んでもらうためにパスを追加。

# config/application.rb
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
config.middleware.use(Rack::Config) do |env|
  env['api.tilt.root'] = Rails.root.join 'app', 'views', 'api'
end

3. API

今回は、/app/api/api.rbに作っていきます。 モデルには、親モデルarticle.rbと子モデルcomment.rbを使います。

# /app/api/api.rb
class API < Grape::API

  prefix 'api'

  version 'v1', :using => :path

  format :json
  formatter :json, Grape::Formatter::Jbuilder

  resources "articles" do

    # GET /api/v1/articles
    get '', jbuilder: 'articles/index' do
      @articles = Article.all
    end

    # GET /api/v1/articles/:id
    params do
      requires :id, type: Integer
    end
    get ':id', jbuilder: 'articles/show' do
      @article = Article.find(params[:id])
    end

    # POST /api/v1/articles
    params do
      optional :title, type: String
      optional :body, type: String
    end
    post '', jbuilder: 'articles/new' do
      @article = Article.create(title: params[:title], body: params[:body])
    end

    # ネストを生成
    route_param :id do

      resources "comments" do

        # GET /api/v1/articles/:id/comments
        params do
          requires :id, type: Integer
        end
        get '', jbuilder: 'comments/index' do
          @comments = Article.find(params[:id]).comments.all
        end
      end
    end
  end
end

4. Jbuilder

viewsフォルダ配下に作っていきます。 パスは、/views/api/<モデル名>/<メソッド名>です。

# /views/api/articles/index
json.set! :articles do
  json.array! @articles do |article|
    json.id article.id
    json.title article.title
    json.body article.body
  end
end

テスト

今回は、POSTを試してみる。 まずはコマンドラインからサーバーを起動。

$ rails s

POSTを試す時には、curlを使います。

$ curl -X POST -d <data> <URL> -H "Content-Type: application/json"

-H "Content-Type: application/json"でデータ型を指定しないとサーバー側で読み取ってくれないみたいです... なので、実際にはこんな感じです。

$ curl -X POST -d '{"title": "Test Title", "body": "Test Body"}' http://localhost:3000/api/v1/articles -H "Content-Type: application/json"```

基本的な部分はこれで実装できると思います。 認証とかトークンとかはこれからやっていきます。。。

【iOS】UITableViewにプルリフレッシュ機能を付ける

UIRefreshControl

テーブルビューを引っ張って、データを更新する機能を付けたいと思って調べていたのですが、ライブラリを使わなくても簡単に実装できることが判明!
そこで見つけたのが、iOS6から使えるようになったUIRefreshControlでした!(知らなかった...)

UIRefreshControl Class Reference

実装自体はすぐにできたので、リファレンスは見なくてもいいかもしれない。

手順

UITableViewにUIRefreshControlをaddSubView
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self,action:"refreshControlAction:",forControlEvents:UIControlEvents.ValueChanged)
refreshControl.tintColor = UIColor.redColor()
myTableView.addSubview(refreshControl)
処理を実装
func refreshControlAction(refreshControl: UIRefreshControl) {
    /* なんらかの処理
     */
    //処理後にぐるぐる回っているのを消す
    refreshControl.endRefreshing()
}

これだけでできるんですね。感動しました...

【Rails】Wheneverを使ってバックグラウンド処理をしたい

github

https://github.com/javan/whenever

Wheneverでできること

Railsでcronのバッチを作成して、以下の4つのコマンドを定期的に走らせることができる。

  • command
    • bashコマンドの実行
  • rake
    • rakeコマンドの実行
  • runner
  • script
    • scriptの実行

ターミナルでよく使うコマンド一覧

/config/schedule.rbを作成

$ wheneverize .

cronの設定の確認

$ bundle exec whenever

cronに処理を登録

$ bundle exec whenever --update-crontab

cronに登録した処理の削除

$ bundle exec whenever --clear-cron

手順

Gemfile
gem 'whenever', :require => false
ターミナル
$ wheneverize .
> [add] writing `./config/schedule.rb'
> [done] wheneverized!
/config/application.rb

lib/tasks/***.rbを読み込む場合、lib配下をロードするように設定する。

module xxx # railsアプリ名
  class Application < Rails::Application
    config.autoload_paths += %W(#{config.root}/lib)
    config.autoload_paths += Dir["#{config.root}/lib/**/"]
  end
end
/config/schedule.rb
# 実行環境の設定
set :environment, :development

# 実行処理の内容
every 2.hours do
  command "/usr/bin/some_great_command"
  runner "MyModel.some_method"
  rake "some:great:rake:task"
end

every 4.days do
  runner "AnotherModel.prune_old_records"
end

【Rails】ActiveAdmin

ActiveAdminをインストール

手順

①Gemfileに記入

gem 'devise'
gem 'activeadmin', github: 'activeadmin'

②プロジェクトにインストール

$ bundle install
$ rails g active_admin:install

③インストール時に出た警告を修正

config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
config/routes.rb
root to: "home#index
app/views/layouts/application.html.erb
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
db/migrate/201501222120_devise_create_admin_users.rb
## コメントアウトする

## Recoverable
# t.string   :reset_password_token
# t.datetime :reset_password_sent_at

...

add_index :admin_users, :email,                unique: true
# add_index :admin_users, :reset_password_token, unique: true

...

マイグレーション

$ rake db:migrate

⑤モデルを作成

$ rails g model Article title:string content:text
$ rake db:migrate

⑥ActiveAdminで管理する

このコマンドをやらなければ、adminには表示されません。

$ rails g active_admin:resource Article

⑦ストロングパラメータの許可

app/admin/article.rb
ActiveAdmin.register Article do
  controller do
    def permitted_params
      params.permit article: [:title, :content]
    end
  end
end

⑧articleと1対多のモデルを新たに作成

$ rails g model Comment commenter:string body:text article:references
$ rake db:migrate
$ rails g active_admin:resource Comment

⑨ストロングパラメータの許可

  controller do
    def permitted_params
      params.permit comment: [:commenter, :body, :article_id]
    end
  end

※例外的に必要な処理

今回、モデルにcommentを作成したため、ActiveAdminで元々使用されているcommentと被ってしまった。以下のようなエラーが出てしまいました...

`raise_if_mismatched!': You're trying to register ActiveAdmin::Comment as Comment, but the existing ActiveAdmin::Resource config was built for Comment! (ActiveAdmin::ResourceCollection::ConfigMismatch)
config/initializers/active_admin.rb
config.comments_registration_name = 'AdminComment'

ここまでやれば一通り動かせると思います。
管理画面はRailsAdminと比較してどっちを使うか決める感じになりそうです。
久しぶりの投稿、、これからは継続して投稿していきたい...
Cocos2d-xを2月から触っていくことになったのでそっちも書いていきたいです。

【iOS】デリゲート

デリゲートの設定

今回は完全に忘れないためのメモ。

Objective-C

Sample.h

#import <UIKit>

@class Sample;

@protocol SampleDelegate <NSObject>

- (void)sampleDelegateMethod;

@end

@interface Sample : NSObject

@property (nonatomic, weak) id<SampleDelegate>delegate;

@end

Sample.m

[self.delegate sampleDelegateMethod];

Swift

sample.swift

protocol SampleDelegate: class {
    func sampleDelegateMethod()
}

class Sample: NSObject {
    weak var delegate: SampleDelegate? = nil

    func callDelegateMethod() {
        delegate?.sampleDelegateMethod()
    }
}

最近のプロジェクトではSwiftを使ってるのですが、Swift好きになりますね。
まだ重かったりするみたいですけど、早くメインになってほしいかもしれないです。
Swiftの"?"とか"!"についてまだよくわかっていないので調べないとです...

【iOS8】PhotoKit

Photos Framework

iOS8から追加されたAssets Library Frameworkの代わりのフレームワーク
イメージピッカーを作る際に使用したのでまとめてみます。

アセットの編集など機能はたくさんあるようですが、今回はデータの取得と表示だけです。

主に使うクラス

・PHAsset
・PHAssetCollection
・PHCollection
・PHFetchResult
・PHImageManager

流れ

  1. カメラロール、アルバムの取得
  2. アルバム内の画像を表示
  3. 選択した画像のPHAssetを返す

これぐらいでイメージピッカーは作れるかな、と。

1. カメラロール、アルバムの取得

iOS7ではALAssetsLibraryを使って取得してたと思います。

  • enumerateGroupsWithTypes:usingBlock:failureBlock:

このメソッド使って取得してましたねー。
非同期なのが個人的には好きじゃなかったです...
Photos Frameworkではこんな感じでやりました。

NSMutableArray *results = @[].mutableCopy;

// Camera Roll
PHFetchOptions *options = [PHFetchOptions new];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
PHFetchResult *cameraRoll = [PHAsset fetchAssetsWithOptions:options];

[results addObject:cameraRoll];

// Other Albums
PHFetchResult *albums = [PHCollection fetchTopLevelUserCollectionsWithOptions:nil];
for (PHCollection *collection in albums) {
    PHAssetCollection *assetCollection = (PHAssetCollection *)collection;
    PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];
    [results addObject:result];
}

テーブルビューに表示したかったので配列に入れておきました。
カメラロールとアルバムを別々に取得しないといけないっぽいのが面倒です...
まとめて取ってくる方法がないかなー、と探しています。

アルバムの一覧を表示したいので、サムネイルとタイトルが必要。
-サムネイル取得

__block UIImage *image = nil;
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
// 同期処理にする場合にはYES (デフォルトはNO)
options.synchronous = YES;
[[PHImageManager defaultManager] requestImageForAsset:result.lastObject
                                           targetSize:CGSizeMake(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
                                          contentMode:PHImageContentModeAspectFill
                                              options:options
                                        resultHandler:^(UIImage *result, NSDictionary *info) {
                                            image = result;
                                        }
 ];

-タイトル取得

// PHCollectionから取得
NSString *albumName = collection.localizedTitle;

この辺のを使って表示までできると思います。

2. アルバム内の画像を表示

-PHAssetの取得

NSMutableArray *assets = @[].mutableCopy
for (PHAsset *asset in result) {
    [assets addObject:asset];
}

-サムネイル表示

__block UIImage *image = nil;
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
// 同期処理にする場合にはYES (デフォルトはNO)
options.synchronous = YES;
[[PHImageManager defaultManager] requestImageForAsset:asset
                                           targetSize:CGSizeMake(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
                                          contentMode:PHImageContentModeAspectFill
                                              options:options
                                        resultHandler:^(UIImage *result, NSDictionary *info) {
                                            image = result;
                                        }
 ];

3. 選択した画像のPHAssetを返す

3と書きましたが、UICollectionViewで選択されたPHAssetを返してあげれば終わりですね。

iOS7/8に対応したイメージピッカーを作っていたのでごちゃごちゃしていましたが、iOS8だけで見ればそんなに難しいことはやってないです。次回からは全体像がつかめるようにサンプルとか載せれるようにしたいです。