ReactでGithubのリポジトリブラウザを作ってみる (2)
前回の続き。
お題
今回はちょっとしたお題。 ユーザー名が未入力でもボタンが押せちゃうのは、ユーザービリティが良くないので改善します。 具体的には、ユーザー名未入力の場合は、Updateボタンをdisableします。
以下のように、disable={!this.state.username}
してあげるだけで実現できます。
class SearchBox extends React.Component { ... (snip) ... render() { return ( <div> <form onSubmit={this.handleSubmit}> <input type="text" onChange={this.handleChange} placeholder="Enter username" /> <input type="submit" value="Update" disabled={!this.state.username} /> // <= (*) ここ </form> </div> ); } ... (snip) ... }
ポイントは以下でしょうか。
username
を、ステートで保持する。- ユーザー名のテキストボックスの入力内容の変化を
onChange()
でハンドリングする。 onChange()
が呼び出されたら、ステートに保持しているusename
をsetState()
で更新する。
ソースコード
ReactでGithubのリポジトリブラウザを作ってみる (1)
GithubのRepositories APIは、指定したユーザーのリポジトリを取得することができます。
$ curl -s 'https://api.github.com/users/toshiyukihina/repos' [ { "id": 38911425, "name": ".emacs.d", "full_name": "toshiyukihina/.emacs.d", "owner": { "login": "toshiyukihina", "id": 1883527, "avatar_url": "https://avatars.githubusercontent.com/u/1883527?v=3", "gravatar_id": "", "url": "https://api.github.com/users/toshiyukihina", "html_url": "https://github.com/toshiyukihina", "followers_url": "https://api.github.com/users/toshiyukihina/followers", "following_url": "https://api.github.com/users/toshiyukihina/following{/other_user}", "gists_url": "https://api.github.com/users/toshiyukihina/gists{/gist_id}", "starred_url": "https://api.github.com/users/toshiyukihina/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/toshiyukihina/subscriptions", ... (snip) ...
このAPIを使って、指定したユーザーのリポジトリ一覧を取得するアプリを作ってみます。
イメージはこんな感じです。
コンポーネントの構成は以下。
- App
- SearchBox
- RepositoryList
- Repository
準備
ここからプロジェクトの雛形をダウンロードして、bundle install --path vendor/bundle && npm install
しておきます。
各コンポーネントのファイルを生成する
$ for c in app search_box repository_list repository; do bundle exec rails g react:component $c; done
Repository
を実装する
Repository
は、以下をパラメータとしてとります。
- name
- description
import React from 'react'; class Repository extends React.Component { render() { return ( <tr> <td>{this.props.name}</td> <td>{this.props.description}</td> </tr> ); } } Repository.PropTypes = { name: React.PropTypes.string.isRequired, description: React.PropTypes.string.isRequired }; export default Repository;
RepositoryList
でRepository
をレンダリングする
Repository
に渡すデータは、RepositoryList
の上位コンポーネントApp
からrepositories
というPropで渡されることにします。
import React from 'react'; import Repository from './repository'; class RepositoryList extends React.Component { render() { const repositoryNodes = this.props.repositories.map((repository) => { return ( <Repository key={repository.id} name={repository.name} description={repository.description} /> ); }); return ( <div> <table> <thead> <tr> <th>Name</th> <th>Description</th> </tr> </thead> <tbody> {repositoryNodes} </tbody> </table> </div> ); } } RepositoryList.PropTypes = { repositories: React.PropTypes.arrayOf(React.PropTypes.object) }; export default RepositoryList;
App
でRepositoryList
をレンダリングする
RepositoryList
に渡すデータは、App
でGithub API経由で取得したデータを渡すことを想定します。
ですので、App
の初期ステートとして固定データを与えておきます。
import React from 'react'; import SearchBox from './search_box'; import RepositoryList from './repository_list'; class App extends React.Component { constructor(props) { super(props); this.state = { repositories: [ { id: '1', name: 'angular-ui-router', description: 'A sample program using angular-ui-router.' }, { id: '2', name: 'angular-chat', description: 'A chat program using Angular.js.' }, { id: '3', name: 'hello-redux', description: 'My first redux.' }, { id: '4', name: 'dotfiles', description: 'My dotfiles.' } ] }; } render() { return ( <div> <SearchBox /> <RepositoryList repositories={this.state.repositories} /> </div> ); } } export default App;
ここまでの実装で、以下のような画面になりました。
SearchBox
の実装
SearchBox
は、入力されたGithubユーザー名を上位コンポーネントであるApp
にコールバックします。
ですので、Propとして渡されたonSubmit
を、Updateボタンクリック時に呼び出します。
import React from 'react'; class SearchBox extends React.Component { constructor(props) { super(props); this.state = { username: '' }; this.handleSubmit = this.handleSubmit.bind(this); this.handleChange = this.handleChange.bind(this); } handleSubmit(e) { e.preventDefault(); this.props.onSubmit(this.state.username); } handleChange(e) { this.setState({username: e.target.value}); } render() { return ( <div> <form onSubmit={this.handleSubmit}> <input type="text" onChange={this.handleChange} placeholder="Enter username" /> <input type="submit" value="Update" /> </form> </div> ); } } SearchBox.PropTypes = { onSubmit: React.PropTypes.func.isRequired }; export default SearchBox;
App
にSearchBox
を組み込む
render()
で、SearchBox
をレンダリングします。
同時に、SearchBox
のUpdateボタンがクリックされた時に呼び出してもらうhandleSubmit()
をPropで渡しています。
HTTPクライアントは、superagentを使っていますので、npm install superagent --save
しています。
あと、初期ステートとしてthis.state.repositories
に与えていたダミーデータは、空の配列にしています。
import React from 'react'; import SearchBox from './search_box'; import RepositoryList from './repository_list'; import request from 'superagent'; class App extends React.Component { constructor(props) { super(props); this.state = { repositories: [] }; this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit(username) { request.get(`https://api.github.com/users/${username}/repos`) .end((err, res) => { if (res.ok) { this.setState({repositories: res.body}); } else { console.error(err); } }); } render() { return ( <div> <SearchBox onSubmit={this.handleSubmit} /> <RepositoryList repositories={this.state.repositories} /> </div> ); } } export default App;
スクリーンキャプチャ
こんな感じです。
ソースコード
ここまでのコードは、release/0.0.1
としてコミットしています。
Material UIをためす
Material UIを色々試してみます。 react-browserify-rails-seedからSeedプロジェクトをダウンロードして色々試します。
前準備
ダウンロードしたSeedを展開します。
2016/04/21現在、material-ui
が依存するreactのバージョンは、14でなければならないみたいなので、package.json
を下記のように変更しておきます。
diff --git a/package.json b/package.json
index 60b6e7c..300d86a 100644
--- a/package.json
+++ b/package.json
@@ -14,8 +14,8 @@
"author": "Toshiyuki HINA",
"license": "MIT",
"dependencies": {
- "react": "^15.0.1",
- "react-dom": "^15.0.1"
+ "react": "^0.14.0",
+ "react-dom": "^0.14.0"
},
"devDependencies": {
"babel-plugin-add-module-exports": "^0.1.2",
以下のコマンドを実行します。
$ bundle install --path vendor/bundle && npm install
material-uiのインストール
$ npm install material-ui --save
インストール時にUNMET PEER DEPENDENCY react-tap-event-plugin@^0.2.0
と怒られますが、tapイベントは使わないので取り合えず気にしないことに。。
最初のコンポーネント
material-ui
のREADME.mdのUsageに従って作っていきます。
$ bundle exec rails g react:component app
$ bundle exec rails g react:component my_awesome_react_component
app/views/home/index.html.erb
で、App
を読み込みます。
<%= react_component 'App' %>
App
コンポーネントとMyAwesomeReactComponent
のコードはそれぞれ以下。
READMEに記載されているコードをそのままコピーするとエラーになりますね。。(コンポーネントのパスが違う。)
import React from 'react'; import getMuiTheme from 'material-ui/lib/styles/getMuiTheme'; import MuiThemeProvider from 'material-ui/lib/MuiThemeProvider'; import MyAwesomeReactComponent from './my_awesome_react_component'; export default class App extends React.Component { render() { return ( <MuiThemeProvider muiTheme={getMuiTheme()}> <MyAwesomeReactComponent /> </MuiThemeProvider> ); } }
import React from 'react'; import RaisedButton from 'material-ui/lib/raised-button' export default class MyAwesomeReactComponent extends React.Component { render() { return ( <RaisedButton label="Default" /> ); } }
ここまで出来たら、rails s
してブラウザで確認してみます。http://localhost:3000/home/index
にアクセスしましょう。
以下のような画面が表示されたらOKです。
ソースコード
browserify-railsとreact-railsでseedプロジェクトを作る
seedプロジェクトを作っておくとなにかと便利。
ということで、browserify-rails
とreact-rails
でモダンな環境をrails上に構築してみます。
browserify-rails
を採用したのは、ここの記事に影響ですね。
Railsのプロジェクトを作る
$ rails new react-browserify-rails-seed -T -B
react-rails
とbrowserify-rails
を追加する
react-rails
とbrowserify-rails
をGemfile
に追加します。
$ vi Gemfile ... (snip) ... gem 'react-rails' gem 'browserify-rails' ... (snip) ...
bundle install
します。
$ bundle install --path vendor/bundle
Nodeモジュールを追加する
package.json
を初期化します。入力は適当に。
$ npm init
react-rails
をインストールするとReact関連のライブラリもインストールされますが、それは使わず、npmでインストールした方を使います。
$ npm install --save react react-dom
$ npm install --save-dev babelify browserify browserify-incremental reactify babel-preset-es2015 babel-preset-react babel-plugin-add-module-exports
browserify-railsの設定をする
config/initializers/assets.rb
に、browserfiy-rails
のコマンドラインを設定します。
Rails.application.config.browserify_rails.commandline_options = [ '-t babelify', '-t reactify --extension=".js.jsx"' ]
同時に、.bablerc
にプリセットの設定をしておきます。
{ "presets": ["es2015", "react"], "plugins": ["add-module-exports"] }
Reactの環境をインストールする
$ bundle exec rails g react:install
app/assets/javascripts/application.js
とapp/assets/javascripts/components.js
をちょっと手直しします。
前述しましたが、react-rails
でインストールされたreactは使わないので削除したりしています。
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 0c09f83..d324746 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,7 +13,5 @@ //= require jquery //= require jquery_ujs //= require turbolinks -//= require react -//= require react_ujs //= require components //= require_tree . diff --git a/app/assets/javascripts/components.js b/app/assets/javascripts/components.js index 9ce7a4f..a541d47 100644 --- a/app/assets/javascripts/components.js +++ b/app/assets/javascripts/components.js @@ -1 +1,2 @@ -//= require_tree ./components +//= require_self +//= require react_ujs
ReactでHello Worldしてみる
ここまでで環境は整ったので、お試しのReactのコンポーネントを作ってみます。
まずは、ReactコンポーネントをレンダリングするためのViewを用意します。
$ bundle exec rails g controller home index
Reactのジェネレータを使って、コンポーネントのひな形を用意します。
$ bundle exec rails g react:component hello
app/assets/javascripts/components/hello.js.jsx
を手直しします。生成されたコードをES2015で書き換えています。
import React from 'react' export default class Hello extends React.Component { render() { return <div>Hello React</div>; } }
Helloコンポーネントにアクセスできるよう、app/assets/javascripts/components.js
からrequireします。
React
とReactDOM
をwindowに生やしているのは、react_component
ヘルパーからアクセスできるようにするためです。
diff --git a/app/assets/javascripts/components.js b/app/assets/javascripts/components.js index a541d47..3cc9443 100644 --- a/app/assets/javascripts/components.js +++ b/app/assets/javascripts/components.js @@ -1,2 +1,6 @@ //= require_self //= require react_ujs + +window.React = require('react'); +window.ReactDOM = require('react-dom'); +window.Hello = require('./components/hello');
動作確認してみます。bundle exec rails s
して、http://localhost:3000/home/index
にアクセスしてみましょう。
ソースコード
ソースコードは以下です。
mysqlに巨大なデータを流し込む時によくあるトラブルとその解決方法
はじめに
Webアプリの開発で、運用データを開発環境にインポートすることってよくあることだと思います。 この記事は、そのときに遭遇したトラブルと、その解決方法です。 環境は OS X El Capitan(10.11.4) ですが、Linuxとかでも解決方法は同じだと思われます。
ちなみに、インポートするデータのサイズ(ファイルのサイズ)は、550M程度でした。
トラブル1
トラブル内容
$ mysql -u root -p testdb < testdb.sql Enter password:**** ERROR 2006 (HY000) at line 190: MySQL server has gone away
解決方法
max_allowed_packet
のサイズを大きくします。
my.cnf
に、以下を記述して再起動しましょう。
[mysqld] max_allowed_packet=100M
トラブル2
トラブル内容
$ mysql -u root -p testdb < testdb.sql Enter password:**** ERROR 1118 (42000) at line 207: The size of BLOB/TEXT data inserted in one transaction is greater than 10% of redo log size. Increase the redo log size using innodb_log_file_size.
解決方法
innodb_buffer_pool_size
とinnodb_log_file_size
のサイズを大きくします。
my.cnf
に、以下を記述して再起動しましょう。
[mysqld] innodb_buffer_pool_size = 1024M innodb_log_file_size=512M
最後に
自分は、上記設定を~/.my.cnf
に記述しました。my.cnf
は、読み込み順序があるらしいので、お気をつけください。
$ mysql --help | grep my.cnf order of preference, my.cnf, $MYSQL_TCP_PORT, /etc/my.cnf /etc/mysql/my.cnf /usr/local/etc/my.cnf ~/.my.cnf
Webサービスでも作ろうか
きっかけ
今年も既に2月終盤。仕事に追われてなんか勉強できていないなぁ。 電車の中で、ぼんやりそんなことを考えていて、なんかWebサービスでもつくってみようかなぁと。
あと、ここ数年、Railsを仕事で使っているので、腕試ししたいですしね。
どんなサービスつくろうか?
これが一番大事なんですが、いざ作るとなるとなかなか思い浮かばないっすね(笑)。 思い付きですが、とりあえずチケットの販売価格の比較サービス作ってみます。 ユーザーにとってメリットあるの?って感じなんですが、割と価格差あるんですよね。
例えば、チケット流通センターとTicketCampで比べてみます。 佐野元春の2016/03/26 東京国際フォーラムです。
サービス | 席 | 価格 |
---|---|---|
チケット流通センター | 一階 38列60~70番 | 9,000円 |
TicketCamp | 一階 34列 | 9,500円 |
ほぼ同じ席でも一枚500円の差があるんですよね。 この不況時代、少しでも安く入手したいって人多いと思うんですよね。
仕様はどうしようか?
最初はミニマムに。
- 以下の2つのサービスからデータをスクレイピングする。(徐々に増やしていく予定。)
- 同じ日、同じ場所の公演の価格を比較して表示する。
- 価格の比較表示から、サイトへのリンクを張って、購入サイトに飛べるようにする。
これから
ぼちぼちやっていきます。 成果は、このブログやgithubで公開できればなぁと。
あ、あとこのサービス作ろうと思ったきっかけは、以前購入したRubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例が、積読状態でもったいないっていう動機もあります(笑)。
ではよろしくお願いします。
Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例
- 作者: 佐々木拓郎,るびきち
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2014/08/22
- メディア: 単行本
- この商品を含むブログ (9件) を見る
Railsのプロジェクトを新規作成する手順
はじめに
グローバルな場所には、必要最小限のgemをインストールして、Railsプロジェクトを新しく作成する手順。 いつも忘れるのでメモ。
事前準備
- rubyのバージョンは、rbenvで管理するので、予めインストールしておきます。
- rbenvは、READMEを参考にインストールしてください。
- rubyのインストールは、rbenvのプラグイン ruby-buildを利用するので、こちらもruby-buildのREADMEを参考にインストールします。
- rbenvでrubyをインストールした後、gem install bundlerで、bundlerをインストールしておきます。
- mysqlを使うことが多いので、mysql前提で手順を説明します。こちらも予めインストールしておきます。
環境
手順
作業ディレクトリを作る
作業ディレクトリwork
を作成し、その中で作業します。
$ mkdir work && cd $_
Gemfileを作る
まずは、railsをインストールするためだけの、Gemfileを作成します。このGemfileは、一時的なものです。
$ cat << EOS > Gemfile source "http://rubygems.org" gem "rails", "4.1.0" EOS
Railsをインストールする
以下のコマンドで、vendor/bundle
ディレクトリに、Railsと関連するgemがインストールされます。これで、railsのコマンド(rails new
とか)を利用することが出来るようになります。
$ bundle install --path vendor/bundle
Railsのプロジェクトを作成する
ここでは、todo
という名前のアプリケーションを作成することとします。--skip-bundle
オプションをつけてください。--skip-bundle
オプションをつけないと、rails newコマンド実行時に、グローバルな場所に、関連gemがインストールされてしまいます。
$ bundle exec rails new todo --skip-bundle --database=mysql
gemのインストール
todo
ディレクトリに移動し、関連するgemをインストールします。
$ cd todo $ bundle install --path vendor/bundle
データベースの準備
$ bundle exec rake db:create $ bundle exec rake db:migrate
Railsを起動する
ここまでで、railsのプロジェクトの作成と準備が完了しましたので、正常に起動するか確認してみます。以下のコマンドを実行した後、ブラウザから http://localhost:3000 にアクセスしてみてください。
$ bundle exec rails s
以下の画面が表示されれば成功です。
最後に
作成したプロジェクトをgitに登録します。
gitに登録するにあたり、不要ファイルの登録を防ぐために.gitignore
を作りましょう。
.gitignore
の作成には、giboというツールを使います。READMEを参照してインストールしてください。
$ gibo rails > .gitignore $ git init $ git add . $ git commit -m "initial commit"