UIImageViewを丸くトリミングする
TweetBotのサムネイル表示のように、画像を丸くトリミングする方法を紹介します。
CALayerを使うと数行で実装出来るので便利です。
とりあえずサンプルコード
UIImage *circleImage = [UIImage imageNamed:@"test.png"]; UIImageView *circleImageView = [[UIImageView alloc] initWithImage:circleImage]; if (circleImage.size.width != circleImage.size.height) { CGFloat smallerSideLength = (circleImage.size.width < circleImage.size.height) ? circleImage.size.width : circleImage.size.height; circleImageView.frame = CGRectMake((circleImage.size.width - smallerSideLength) * 0.5f, (circleImage.size.height - smallerSideLength) * 0.5f, circleImage.size.width, circleImage.size.height); } else { circleImageView.frame = CGRectMake(0.0f, 0.0f, circleImage.size.width, circleImage.size.height); } circleImageView.layer.cornerRadius = circleImageView.frame.size.width * 0.5f; circleImageView.clipsToBounds = YES; [self addSubview:circleImageView]
iOS Circler masked UIImageView
上のコードで、四角形のUIImageViewを正円にトリミング出来ます。
元の画像の縦横比が違うときは、短い辺を基準に、真ん中でトリミングしています。
大切なのは
circleImageView.layer.cornerRadius = circleImage.size.width * 0.5f;
circleImageView.clipsToBounds = YES;
この2行だけです。
元の画像としてtwitter等の正方形の画像を用意すれば、面倒な分岐も必要ないので、単純に円形に切り抜きたいという時に使えるかと。
google custom search engine(CSE)を使って、検索結果をjsonで取得する
google検索結果を取得しようとした時に、すこし苦労したので手順を整理しておく。
参照したのはここ
Custom Search — Google Developers
API keyの取得
https://cloud.google.com/console/project
まず、上のURLにアクセス
次に規約に同意するので Accept をクリック
①、②、③の順にクリックして
必要な種類のkeyを選択。
今回はServer Keyを作る。
IPアドレスで制限をかけたい場合は、枠の中に書く。
今回は制限を書けないので空のままCreateをクリック
これでAPI keyの作成が完了。
Custom search engine IDの作成
Custom Search Engine
上のURLにアクセス
Addをクリックして、Custom search engineの登録をする
Custom search engineは、本来自分のサイト内検索用のAPIなため、最低ひとつのURLを入れないと作成出来ない。
ここでは適当に入れておき、あとでコントロールパネルから削除を行う。
作成後、表示されるページでコントロールパネルへ
フィルタ等はこのページで指定出来る。
google 全体を検索したい場合、
- ウェブ全体 を指定
- 検索したいサイト を空欄に
するとOK。
Custom search engine IDは、このコントロールパネル内で確認出来る。
検索を行いJSONを取得する
https://www.googleapis.com/customsearch/v1?key={API_KEY}&cx={CUSTOM SEARCH ENGINE ID}&q={SEARCH_WORDS}
このURLを使う。
これは検索を行いJSONデータを返却するURLになっている。
なので、任意の言語でjsonからdecodeをして、使用すればおk。
パラメータを少しだけ解説
- key: API key
- cx: Custom search engine ID
- p: 検索用クエリ
とりあえず、上の3つがあれば検索出来るはず。
JSONからサイトの情報を取得する
得られたJSONデータにおいて、itemsにサイトの情報が入っている。
後は実際にデータを眺めてもらえば良いと思うが、一応PHPの簡単なサンプルをおいておく。
このサンプルでは、得られたデータから、1番目に表示されるサイトの情報を取得するところまで行っている。
$cse_url = https://www.googleapis.com/customsearch/v1?key={API_KEY}&cx={CUSTOM SEARCH ENGINE ID}&q={SEARCH_WORDS}'; $search_result = file_get_contents($cse_url, true); $search_result = json_decode($search_result); $result_first = $search_result->items[0];
この先で取得出来るサイトの情報は、次の項に書いたパラメータが記載されているURLを参照してもらうと良いかと。
注意点
検索用URLはSSLを使用”しなければならない”。
次のようにhttp://...とすると、
http://www.googleapis.com/customsearch/v1?key={API_KEY}&cx={CUSTOM SEARCH ENGINE ID}&q={SEARCH_WORD}
以下のエラーを吐く
{"error":{"errors":[{"domain":"global","reason":"sslRequired","message":"SSL is required to perform this operation."}],"code":403,"message":"SSL is required to perform this operation."}}
これに関しては
error in getting userinfo from google drive api - Stack Overflow
ここにある通り。
httpではなくhttpsを使えばOK
https://www.googleapis.com/customsearch/v1?key={API_KEY}&cx={CUSTOM SEARCH ENGINE ID}&q={SEARCH_WORD}
Rails 4.0 で多対多関連
この記事内では、"timeline"モデルと,"user"モデルをどのようにして接続する事が出来るかを見ていきます。
timeline.user user.timeline
等でアクセス出来るように作っています(つもりです)。
Rubyや、Railsの経験が浅いため、もっと良い方法を知っている方、間違えを見つけた方はコメントを頂けるととてもうれしいです。
またポリモーフィズムを使用した関連付けに関しては触れていません。
あくまで導入部分だと思って参照して頂ければとおもいます。
HABTM
has_and_belongs_to_many を使った例。
モデルをいくつかの種類に分けて参照させたいときなどに便利です。
#/app/models/user.rb class User < ActiveRecord::Base has_and_belongs_to_many :timelines_member, :class_name => 'Timeline', :foreign_key => :member_timeline_id, :association_foreign_key => :member_id, :join_table => :timelines_members has_and_belongs_to_many :timelines_observer, :class_name => 'Timeline', :foreign_key => :observer_timeine_id, :association_foreign_key => :observer_id, :join_table => :timelines_observers end
#/app/models/timeline.rb class Timeline < ActiveRecord::Base has_and_belongs_to_many :members, :class_name => 'User', :foreign_key => :member_id, :association_foreign_key => :member_timeline_id, :join_table => :timelines_members has_and_belongs_to_many :observers, :class_name => 'User', :foreign_key => :observer_id, :association_foreign_key => :observer_timeine_id, :join_table => :timelines_observers end
#/db/migrate/[TIMESTAMP]_timelines_members.rb class TimelinesMembers < ActiveRecord::Migration def change create_table :timelines_members, id: false do |t| t.integer :member_id, null: false t.integer :member_timeline_id, null: false end add_index(:timelines_members, [:member_id, :member_timeline_id], :unique => true) end end
#/db/migrate/[TIMESTAMP]_timelines_obserbers.rb class TimelinesObservers < ActiveRecord::Migration def change create_table :timelines_observers, id: false do |t| t.integer :observer_id, null: false t.integer :observer_timeline_id, null: false end add_index(:timelines_observers, [:observer_id, :observer_timeline_id], :unique => true) end end
HM with relationship table
has_manyとbelongs_toで関連を作るのですが、2つのモデルの間にrelationshipテーブルを挟む方法です。
2つのモデルの間に”状態”等のステータスを持たせたい場合に有効です。
#/app/models/user.rb has_many :timeline_user_relationships has_many :timelines, :through => :timeline_user_relationships end
#/app/models/timeline.rb class Timeline < ActiveRecord::Base has_many :timeline_user_relationships has_many :users, :through => :timeline_user_relationships end
#/app/models/timeline_user_relationship.rb class TimelineUserRelationship < ActiveRecord::Base belongs_to :user belongs_to :timeline end
#/db/migrate/[TIMESTAMP]_create_timeline_user_relationships.rb class CreateTimelineUserRelationships < ActiveRecord::Migration def change create_table :timeline_user_relationships do |t| t.references :user, index: true t.references :timeline, index: true t.integer :role t.timestamps end add_index(:timeline_user_relationships, [:user_id, :timeline_id], :unique => true) end end
create_join_table
Rails 4.0から追加されたものです。
Railsドキュメントにも記載されています。
http://railsdoc.com/migration#テーブルを結合して作成(create_join_table)
手軽に作成出来るので、とりあえず関連をつけたいという時に使うと良いかと思います。
#/app/models/user.rb class User < ActiveRecord::Base has_and_belongs_to_many :timelines end
#/app/models/timeline.rb class Timeline < ActiveRecord::Base has_and_belongs_to_many :users end
#/db/migrate/[TIMESTAMP]_create_timelines_users.rb class CreateUsersGroups < ActiveRecord::Migration def change create_join_table :timelines, :users add_index(:groups_users, [:group_id, :user_id], :unique => true) end end
ざっと3種類紹介しましたが、option等は必要になった際に使ってみて追加していこうと思います。
FrozenBear v1.4.0
先日、FrozenBear最後(?)の主要バージョンとなる、バージョン1.4.0をリリースしました。
昨年冬に最初のリリースを行い、季節の巡りとともに、FrozenBearのアップデートを重ねて参りましたが、ここで一区切りとなります。
二度目の冬は、明るい陽の光がきれいです。
末永くお使いいただける事を願っております。
rspec-rails (2.14.0) + devise (3.2.1, 3.2.0) + capybara (2.2.0) でsign in判定
deviseを使っていて、rspec内でサインインしているかどうかの判定をしたいときのやり方。
バージョンごとに微妙に変化があるようで、実際に動作確認したのはタイトルのもののみです。
他のバージョンに関しては、この記事では対応出来ない可能性があるため、注意してください。
1つずつ注意点を確認して行きます。
最終的な結論は記事の下の方にコードをおいておくので、やり方だけ分かれば良いという人は下までスキップしてください。
devise
deviseを使う際に問題になる事の1つに、rspecでのsign in、sign outの検証が、deviseが提供する’sign_in’と’sign_out'メソッドでは出来ないという事があります。
そこで、以下のページで解決策が提示されています。
https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara
このページではまず、Capybaraによって提供されるメソッドを使って、Sign inフォームを直接埋めることでsign inする事を推奨しています。しかし推奨した後に、この方法ではテストの数が増えた時に時間がかかってしまうとも言っています。
そこで、warden test helpersというヘルパーを用いてこの問題を解決する方法が書かれています。
そして、なぜ’sign_in’と’sign_out’メソッドを使わないかというと、以下のような理由らしいです。
If you're wondering why we can't just use Devise's built in sign_in and sign_out methods, it's because these require direct access to the request object which is not available while using Capybara. To bundle the functionality of both methods together you can create a helper method.
翻訳(適当)すると
『なぜDeviseによって提供されている’sign_in’と’sign_out’メソッドを使わないのか疑問に思うだろう。その理由は、Capybaraを使っている時には使う事のできない”request object”に直接アクセスする必要があるから。なので、それら2つのメソッドを持っているヘルパーメソッドを自分で作ってやる必要がある。』
らしいです。
という事で、deviseの問題はwarden test helpersで解決する事が出来ます。
capybara
rails_tutorial(http://railstutorial.jp/?version=4.0#top)をやっていると、capybaraを使用しているspecが spec/requests というフォルダ内にある事に気がつくかもしれません。
そうして、capybara v2.2.0でも同じようにするとうまくいかなくてはまります。
2.2.0では仕様変更があったらしく、capybaraが動作するのは <b>spec/features</b> 内のファイルに対してとなっています。
ここを間違えていると、いくらdeviseの問題を解決してもテストが通りません。
また、Capybaraを使用するためには、rspecに Capybara::DSL をincludeする必要もあります。
結論
以上2つの問題を解決するよう定義したspec_helperが以下になります。
#spec/spec_helper.rb # This file is copied to spec/ when you run 'rails generate rspec:install' ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'rspec/autorun' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } # Checks for pending migrations before tests are run. # If you are not using ActiveRecord, you can remove this line. ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration) RSpec.configure do |config| # ## Mock Framework # # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: # # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true # If true, the base class of anonymous controllers will be inferred # automatically. This will be the default behavior in future versions of # rspec-rails. config.infer_base_class_for_anonymous_controllers = false # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = "random" # for Capybara : Capybara用のDSLをinclude config.include Capybara::DSL # for Devise : Deviseを使用している時にsign in/outを取るためのヘルパーをinclude include Warden::Test::Helpers end
さらに、capybaraは必ず spec/features フォルダー内で使用してください。
そうしないとvisitやpageが定義されていません、というようなエラーが出て、テストが赤くなります。
最後に、実際にログイン、ログアウトの処理の仕方を確認します。
login_as(user, :scope => :user) #ユーザーをadmin falseでログイン logout(:user) #ログインしているユーザーをログアウトさせる
上記2つのメソッドで、ログイン、ログアウトの処理を実行します。
下にサンプルのspecファイルをおいておいたので、照らし合わせて確認してみてください。
#spec/features/application_controller_spec.rb !!features 直下であることに注意 require 'spec_helper' describe 'ApplicationController' do let(:user) { FactoryGirl.create(:user) } describe 'header' do describe 'has menus' do before { visit root_path } specify 'whenever a user is signed in or not' do expect(page).to have_link('Home', root_path) end specify 'when a user is signed in' do login_as(user, :scope => :user) visit root_path expect(page).to have_link('Sign out', destroy_user_session_path) expect(page).not_to have_link('Sign up', new_user_registration_path) expect(page).not_to have_link('Sign in', new_user_session_path) end specify 'when a user is signed in' do logout(:user) expect(page).to have_link('Sign up', new_user_registration_path) expect(page).to have_link('Sign in', new_user_session_path) expect(page).not_to have_link('Sign out', destroy_user_session_path) end end end end