Commit 14aaf278 by tadyjp

Merge pull request #2 from tadyjp/wip/feat/tree-structure

[WIP] タグを木構造に
parents ce745a30 7b70e1ba
......@@ -71,3 +71,6 @@ group :production do
gem 'rails_12factor'
gem 'pg'
end
# tree structure
gem 'ancestry'
......@@ -32,6 +32,8 @@ GEM
multi_json (~> 1.3)
thread_safe (~> 0.1)
tzinfo (~> 0.3.37)
ancestry (2.0.0)
activerecord (>= 3.0.0)
arel (4.0.1)
atomic (1.1.14)
bcrypt-ruby (3.1.2)
......@@ -161,6 +163,7 @@ PLATFORMS
ruby
DEPENDENCIES
ancestry
better_errors
binding_of_caller
coderay
......
module PostsHelper
# @param {ActiveRecord::Relation} node
def h_display_tree(node)
_html = '<ul>'
_html << %Q{<li><a href="#{ root_path(q: '#' + node.name) }">#{node.name}<a/></li>}
node.children.each do |_child|
_html << h_display_tree(_child)
end
_html << '</ul>'
_html.html_safe
end
def sample_body
text = <<-'EOF'
昨日、[RailsでOmniauthを使ってTwitterログインする](http://qiita.com/hilotter/items/628fd54785d3c892d048)方法をまとめました。
......
class Tag < ActiveRecord::Base
has_many :post_tags
has_many :posts, through: :post_tags
has_ancestry
end
......@@ -14,7 +14,7 @@
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<form id="app-search-form" class="navbar-form navbar-left" role="search" action="/">
<form id="app-search-form" class="navbar-form navbar-left" role="search" action="<%= root_path %>">
<div class="input-group">
<input type="text" name="q" class="form-control" value="<%= params[:q] %>" placeholder="Search">
<span class="input-group-btn">
......
......@@ -14,6 +14,13 @@
<a href="#" data-post-id="<%= post.id %>" class="list-group-item post-list"><%= post.title %></a>
<% end %>
</div>
<div class="list-group">
<% Tag.roots.each do |root| %>
<%= h_display_tree(root) %>
<% end %>
</div>
</div><!--/span-->
<div class="col-xs-12 col-sm-6 col-md-8">
......
class AddAncestryToTags < ActiveRecord::Migration
def change
add_column :tags, :ancestry, :string
add_index :tags, :ancestry
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20131130184007) do
ActiveRecord::Schema.define(version: 20131213005609) do
create_table "post_tags", force: true do |t|
t.integer "post_id", null: false
......@@ -35,8 +35,11 @@ ActiveRecord::Schema.define(version: 20131130184007) do
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.string "ancestry"
end
add_index "tags", ["ancestry"], name: "index_tags_on_ancestry"
create_table "users", force: true do |t|
t.string "name"
t.string "image_url"
......
......@@ -8,25 +8,57 @@
# Tag
_tag_tree = [
['Language', 'JavaScript'],
['Language', 'HTML'],
['Language', 'CSS'],
['Language', 'iPhone'],
['Language', 'PHP'],
['Language', 'Ruby'],
['Language', 'Android'],
['Language', 'Qiita'],
[nil, '日報'],
['インフラ', 'ネットワーク'],
['インフラ', 'サーバ'],
['インフラ', 'OS'],
['OS', 'CentOS'],
['OS', 'Ubuntu'],
['OS', 'MacOS'],
['OS', 'Windows'],
]
tags = []
%w(JavaScript HTML CSS iPhone PHP Ruby Android Qiita Java Objective-C Vim Python ShellScript C++ C CoffeeScript Emacs git Perl jQuery
).each do |tag|
puts "[Seed Tag] #{tag}"
tags << Tag.find_or_create_by(name: tag)
_tag_tree.each do |_parent, _child|
puts "[Seed Tag] #{_parent} => #{_child}"
parent = Tag.find_or_create_by(name: _parent) if _parent
child = Tag.find_or_initialize_by(name: _child)
child.parent = parent if _parent
child.save!
tags << child
end
# User
user = User.find_or_initialize_by(name: '山田太郎')
user.update_attributes!(email: "#{Devise.friendly_token[0,20]}@example.com", password: Devise.friendly_token[0,20])
User.find_or_create_by(name: '山田太郎') do |_u|
_u.email = "#{Devise.friendly_token[0,20]}@example.com"
_u.password = Devise.friendly_token[0,20]
end
user = User.find_or_initialize_by(name: '鈴木二郎')
user.update_attributes!(email: "#{Devise.friendly_token[0,20]}@example.com", password: Devise.friendly_token[0,20])
User.find_or_create_by(name: '鈴木二郎') do |_u|
_u.email = "#{Devise.friendly_token[0,20]}@example.com"
_u.password = Devise.friendly_token[0,20]
end
user = User.find_or_initialize_by(name: '田中三郎')
user.update_attributes!(email: "#{Devise.friendly_token[0,20]}@example.com", password: Devise.friendly_token[0,20])
User.find_or_create_by(name: '田中三郎') do |_u|
_u.email = "#{Devise.friendly_token[0,20]}@example.com"
_u.password = Devise.friendly_token[0,20]
end
# Post
users = User.all
Dir.glob(Rails.root.join('db', 'seeds').to_s + '/*').each do |file_name|
puts "[Post Tag] #{file_name}"
title = file_name.split('/').last
......
# 1行概要
rubyとHTTP通信ライブラリ`faraday`を使ってChatworkの`マイチャット`に投稿するサンプル。
# 犯行動機
ChatはGETできて半人前、POSTできて一人前。
Skypeとか遠距離恋愛で使ってください。エンジニアの使うものではありません。
# 方法
## 1. faradayのインストール (5秒)
``` shell
$ gem install faraday
Successfully installed faraday-0.8.8
Parsing documentation for faraday-0.8.8
Installing ri documentation for faraday-0.8.8
1 gem installed
```
## 2. ChatworkのマイチャットのグループIDを調べる (5秒)
![Screen Shot 2013-12-10 at 11.03.00 PM.png](https://qiita-image-store.s3.amazonaws.com/0/10272/ff3371de-c62f-52bb-2c57-e5649e6d9f2a.png "Screen Shot 2013-12-10 at 11.03.00 PM.png")
## 3. Chatwork APIのトークンを調べる (5秒)
![Screen Shot 2013-12-10 at 11.06.08 PM.png](https://qiita-image-store.s3.amazonaws.com/0/10272/2ada8651-d3a9-9374-11c5-8c014955f948.png "Screen Shot 2013-12-10 at 11.06.08 PM.png")
## 4. 投稿! (15秒)
``` ruby:main.rb
require 'faraday'
ROOM_ID = '<ここに2で取得したマイチャットIDを入れる>' # my chat
CHATWORK_TOKEN = '<ここに3で取得したトークンを入れる>'
conn = Faraday::Connection.new(url: 'https://api.chatwork.com') do |builder|
builder.use Faraday::Request::UrlEncoded
builder.use Faraday::Response::Logger
builder.use Faraday::Adapter::NetHttp
end
response = conn.post do |request|
request.url "/v1/rooms/#{ROOM_ID}/messages"
request.headers = {
'X-ChatWorkToken' => CHATWORK_TOKEN
}
request.params[:body] = "Hello World!" # => ここに入れる文字が投稿される
end
```
そして実行!
```
$ ruby main.rb
```
# 1行概要
rubyのgem anemonを使って指定した正規表現のURLだけクロールし続けるサンプル
# 犯行動機
友人がよからぬ事をしようとしていたので援護射撃
# 方法
```ruby:crawl.rb
require 'anemone'
Anemone.crawl('http://example.com/start_page.html') do |anemone|
# クロールするごとに呼び出される
anemone.focus_crawl do |page|
# 条件に一致するリンクだけ残す
# この `links` はanemoneが次にクロールする候補リスト
page.links.keep_if { |link|
link.to_s.match(/detail/)
}
end
# ここがメインの部分
anemone.on_every_page do |page|
# クロールした結果をごにょごにょ
p page.doc.at('title').inner_html
end
end
```
# 結論
anemoneすごい。
Blogはこちら: [blog.tady.jp](http://blog.tady.jp)
![Screen Shot 2013-11-20 at 12.23.19 AM.png](https://qiita-image-store.s3.amazonaws.com/0/10272/474c4435-35b5-60ee-18ef-dd11e2bbce3c.png "Screen Shot 2013-11-20 at 12.23.19 AM.png")
### 最終更新日
- 2013-12-10
# 1行概要
Skypeの会話内容を準リアルタイム(遅延10秒)でChatworkの「マイチャット」に片方向転送するRubyのデーモンスクリプト。
ChatworkAPIを利用するように変更。
# 犯行動機
会社でskypeからchatworkへの移行をすすめるにあたって、その途中の「両方使ってる」微妙な段階の苦しみを取りぞぞくために。
# 覚書
- 12/10時点でmac限定
# コード
```ruby:sk2ch.rb
require 'fileutils'
require 'sequel'
require 'faraday'
# Skypeのアカウント
SKYPE_NAME = 'skype.id'
# ChatworkのAPIトークン
CHATWORK_API_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# Chatworkに投稿するグループID(マイチャットなど)
CHATWORK_ROOM_ID = '12345678' # charwork my chat
module LastId
DATA_PATH = File.join(File.expand_path(File.dirname(__FILE__)), 'last_id.txt')
# 前回のSkypeのMessageIDを取得、なければSkypeDBから最新のものを取得
def self.get
if File.exist?(DATA_PATH)
open(DATA_PATH).read
else
Skype::copy_db!
Skype::get_last_id
end
end
# ファイルにSkypeのMessageIDを保存
def self.set(last_id)
puts "last_id: #{last_id}"
open(DATA_PATH, 'w') { |f| f.write last_id.to_s }
end
end
module Skype
TMP_DB_PATH = '/tmp/skype-main.db'
# SkypeのDBをコピー
def self.copy_db!
FileUtils.copy("#{ENV["HOME"]}/Library/Application Support/Skype/#{SKYPE_NAME}/main.db", TMP_DB_PATH)
rescue => err
p err
retry
end
# Skypeの最後のMessageIDを取得
def self.get_last_id
connection = Sequel.connect("sqlite://#{TMP_DB_PATH}")
dataset = connection.fetch("SELECT id FROM Messages ORDER BY id DESC LIMIT 1")
dataset.first[:id]
end
# Skypeの最近の更新の差分を取得
def self.get_message
last_message_id = LastId::get
connection = Sequel.connect("sqlite://#{TMP_DB_PATH}")
dataset = connection.fetch("SELECT Messages.id, Messages.from_dispname, Conversations.displayname, Messages.body_xml FROM Messages LEFT JOIN Conversations ON Messages.convo_id = Conversations.id WHERE Messages.id > ?", last_message_id)
new_last_message_id = last_message_id
dataset.each do |r|
puts "[#{r[:from_dispname]}] #{r[:displayname]}\n\t#{r[:body_xml]}"
yield r
new_last_message_id = r[:id]
end
LastId::set(new_last_message_id)
end
end
module Chatwork
# ChatworkにPOST
def self.post(text)
conn = Faraday::Connection.new(url: 'https://api.chatwork.com') do |builder|
builder.use Faraday::Request::UrlEncoded
builder.use Faraday::Adapter::NetHttp
end
response = conn.post do |request|
request.url "/v1/rooms/#{CHATWORK_ROOM_ID}/messages"
request.headers = {
'X-ChatWorkToken' => CHATWORK_API_TOKEN
}
request.params[:body] = text
end
end
end
loop {
Skype::copy_db!
Skype::get_message do |message|
Chatwork::post("#{message[:displayname]} (#{message[:from_dispname]})\n#{message[:body_xml]}")
sleep(1)
end
sleep(1)
}
```
## 細かい内容
- SkypeのDB(sqlite)は排他制御で読み取りすら出来ないので、強引にコピーしSQLを実行して最新の情報を取得
## 参考
[30秒でChatwork APIを使ってマイチャットに投稿する方法](http://qiita.com/tady/items/ac6de448e228a2f631db)
# 1行概要
# 1行概要
例えば社内で自分のPCに開発サーバを立てたけど、IPではなくホスト名で他のメンバーにURLを教えたいときに使う方法
# 犯行動機
- 社内DNSの再設定がめんどくさい
- Google APIのOAuth2のredirect_urlがグローバルでないとエラーになるため、社内ローカルでの開発が面倒
# 想定読者
- Railsの開発がいくつか並行している
- ローカルにDNSが無い OR 設定する程ではない
- Google OAuth2の `invalid redirect url` エラーで詰まった
# 方法
## 1. xip.ioを使ったDNS解決
外部のパブリックDNSサービス [xip.io](http://xip.io) を使います。
![Screen Shot 2013-12-04 at 8.33.14 AM.png](https://qiita-image-store.s3.amazonaws.com/0/10272/20efc664-3921-1a34-de41-a89fec2fef55.png)
アカウント登録の必要はなく、自分のPC(開発機)のローカルIP(例: 192.168.1.123)の場合、
` some-project.192.168.1.123.xip.io `
をブラウザに入力するだけ。
すると、 `xip.io ==> 192.168.1.123` と名前解決されます。
## 2. nginxのNamed virtuel Host設定
xip.ioは名前解決しかしてくれないので、自分のPC(開発機)に複数のプロジェクトが走っているとポートを使い分ける必要があり、いろいろ面倒です。
そんな時は、nginx(Apacheでも可)のNamed virtuel Host機能を使います。
``` nginx.conf
server {
listen 80;
server_name some-project.*; # <= ここがミソ!
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://localhost:3000; #<= ポートをサービスごとに変える
break;
}
}
}
server {
listen 80;
server_name another-project.*; # <= ここがミソ!
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://localhost:3001; #<= ポートをサービスごとに変える
break;
}
}
}
```
## 3.アプリケーションの起動(Railsの場合)
あとは、Railsのプロジェクトディレクトリで、
```sh:/path/to/some-project
bundle exec rails s -p 3000
```
```sh:/path/to/another-project
bundle exec rails s -p 3001
```
と実行するだけ。
これで、
[some-project.192.168.1.123.xip.io](http://some-project.192.168.21.123.xip.io)
[another-project.192.168.1.123.xip.io](http://another-project.192.168.21.123.xip.io)
からアクセスできます。
以上
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment