diff options
author | re4k <re4k@re4k.info> | 2013-03-29 23:00:11 +0900 |
---|---|---|
committer | re4k <re4k@re4k.info> | 2013-03-29 23:00:11 +0900 |
commit | b1cd0b45b761132e47cbc01e99acd728f047b9ae (patch) | |
tree | 9bedd48e7a5c53056721fde3987b7fb27499fbc6 /app | |
parent | 61087e2460e60b08b554b600cdebc6efb2456dd6 (diff) | |
download | aclog-b1cd0b45b761132e47cbc01e99acd728f047b9ae.tar.gz |
Refactor
Add search (partial)
Diffstat (limited to 'app')
25 files changed, 295 insertions, 92 deletions
diff --git a/app/assets/javascripts/errors.js.coffee b/app/assets/javascripts/errors.js.coffee deleted file mode 100644 index 24f83d1..0000000 --- a/app/assets/javascripts/errors.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/i.js.coffee b/app/assets/javascripts/i.js.coffee deleted file mode 100644 index 7615679..0000000 --- a/app/assets/javascripts/i.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/main.js.coffee b/app/assets/javascripts/main.js.coffee deleted file mode 100644 index 7615679..0000000 --- a/app/assets/javascripts/main.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/sessions.js.coffee b/app/assets/javascripts/sessions.js.coffee deleted file mode 100644 index 7615679..0000000 --- a/app/assets/javascripts/sessions.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/javascripts/users.js.coffee b/app/assets/javascripts/users.js.coffee deleted file mode 100644 index 7615679..0000000 --- a/app/assets/javascripts/users.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/stylesheets/_sidebar.css.sass b/app/assets/stylesheets/_sidebar.css.sass index a61fd50..5734004 100644 --- a/app/assets/stylesheets/_sidebar.css.sass +++ b/app/assets/stylesheets/_sidebar.css.sass @@ -7,6 +7,15 @@ .avatar :text-align center .records - .data - :text-align right - + :list-style none + :margin 0 0 20px + li + @include clearfix + :border-top 1px solid #dddddd + span + :display block + :float left + :line-height 20px + :padding 4px 5px + .data + :float right diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c65c21c..ef1106f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,8 @@ +# -*- coding: utf-8 -*- class ApplicationController < ActionController::Base protect_from_forgery before_filter :set_format, :get_include_user, :get_include_user_stats - after_filter :set_content_type + after_filter :xhtml def set_format unless request.format == :json || request.format == :html @@ -9,9 +10,12 @@ class ApplicationController < ActionController::Base end end - def set_content_type + def xhtml if request.format == :html response.content_type = "application/xhtml+xml" + + # remove invalid charactors + response.body = response.body.gsub(/[\x0-\x8\xb\xc\xe-\x1f]/, "") end end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb new file mode 100644 index 0000000..b5933d7 --- /dev/null +++ b/app/controllers/search_controller.rb @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +require "shellwords" + +class SearchController < ApplicationController + def search + @show_search = true + # TODO: OR とか () とか対応したいよね + unless params[:query] + render_tweets(Tweet.where(:id => -1)) + return + end + p words = Shellwords.shellwords(params[:query]) + + result = words.inject(Tweet.order_by_id) do |tweets, word| + case word + when /^-?[a-z]+:.+$/ + # 特殊 + key, value = word.split(":", 2) + case key.downcase + when /^user$/ + user = User.cached(value) + tweets.where(:user_id => user ? user.id : -1) + when /^-?fav/ + search_unless_zero(tweets, "favorites_count", key[0], value) + when /^-?re?t/ + search_unless_zero(tweets, "retweets_count", key[0], value) + when /^-?reactions?$/ + search_unless_zero(tweets, "favorites_count + retweets_count", key[0], value) + when "order" + case value + when "old", /^asc/ + tweets.order("id ASC") + when "reaction" + tweets.order_by_reactions + when /^fav/ + tweets.order_by_favorites + when /^re?t/ + tweets.order_by_retweets + else + tweets + end + when "text" + sourcetext = word.split(":", 2).last.gsub("%", "\\%").gsub("*", "%") + sourcetext = "%#{sourcetext}%".gsub(/%+/, "%") + op = key[0] == "-" ? " NOT LIKE " : " LIKE " + tweets.where("text #{op} ?", sourcetext) + when /^-?source$/ + sourcetext = word.split(":", 2).last.gsub("%", "\\%").gsub("*", "%") + op = key[0] == "-" ? " NOT LIKE " : " LIKE " + tweets.where("source #{op} ? OR source #{op} ?", "<url:%:#{sourcetext.gsub(":", "%3A")}>", "<url:%:#{CGI.escape(sourcetext)}>") + else + # unknown command + tweets + end + when /^20[0-9]{6}\.\.20[0-9]{6}$/ + since, to = word.split(/\.\./) + since = Time.utc(since[0...4].to_i, since[4...6].to_i, since[6...8].to_i) - 9 * 60 * 60 + to = Time.utc(to[0...4].to_i, to[4...6].to_i, to[6...8].to_i + 1) - 9 * 60 * 60 + + tweets.where(:id => first_id_of_time(since)...first_id_of_time(to)) + else + # TODO: ツイート検索 + tweets + end + end + + render_tweets(result) + end + + private + def first_id_of_time(time) + p (time.to_i * 1000 - 1288834974657) << 22 + end + + def search_unless_zero(tweets, column, flag, value) + num = Integer(value) rescue 0 + n = flag == "-" + + unless num == 0 + tweets.where("#{column}#{n ? "<" : ">="} ?", num) + else + tweets + end + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index cc0a9a6..d16175a 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -3,13 +3,15 @@ require "socket" class SessionsController < ApplicationController def callback auth = request.env["omniauth.auth"] + account = Account.find_or_initialize_by(:user_id => auth["uid"]) account.oauth_token = auth["credentials"]["token"] account.oauth_token_secret = auth["credentials"]["secret"] account.consumer_version = Settings.consumer_version account.save! + + session[:account] = account session[:user_id] = account.user_id - session[:screen_name] = auth["info"]["nickname"] begin UNIXSocket.open(Settings.register_server_path) do |socket| diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 0e82b89..e8ce485 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -58,16 +58,12 @@ class UsersController < ApplicationController end def info - account = Account.where(:user_id => @user.id).first - raise Aclog::Exceptions::UserNotRegistered unless account + raise Aclog::Exceptions::UserNotRegistered unless @user.account @title = "@#{@user.screen_name} (#{@user.name})'s Profile" - respond_to do |format| - format.html do - @twitter_user = account.twitter_user - end + format.html format.json do @include_user_stats = true end @@ -170,6 +166,7 @@ class UsersController < ApplicationController .order_by_id .limit(500) .map{|e| Tweet.cached(e.tweet_id)} + .compact .inject(Hash.new(0)){|hash, tweet| hash[tweet.user_id] += 1; hash} .sort_by{|user_id, count| -count} @@ -186,11 +183,13 @@ class UsersController < ApplicationController end if params[:user_id] + #@user = User.cached(params[:user_id].to_i) @user = User.cached(params[:user_id].to_i) end if !@user && params[:screen_name] - @user = User.where(:screen_name => params[:screen_name]).first + #@user = User.where(:screen_name => params[:screen_name]).first + @user = User.cached(params[:screen_name]) end raise Aclog::Exceptions::UserNotFound unless @user diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a41f15d..7b41dcc 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,10 +1,14 @@ require "time" module ApplicationHelper - def format_tweet_created_at(dt) + def format_time(dt) dt.to_time.localtime("+09:00").strftime("%Y-%m-%d %H:%M:%S") end + def format_date_ago(dt) + "#{(DateTime.now.utc - dt.to_datetime).to_i}d ago" + end + def format_tweet_text(text) text .gsub(/<url:(.+?):(.+?)>/){link_to(CGI.unescape($2), CGI.unescape($1), :target => "_blank")} @@ -21,4 +25,22 @@ module ApplicationHelper def twitter_status_url(tweet) "https://twitter.com/#{tweet.user.screen_name}/status/#{tweet.id}" end + + def twitter_user_url(screen_name) + "https://twitter.com/#{screen_name}" + end + + def link_to_user_page(screen_name, &blk) + if block_given? + body = capture(&blk) + end + + body ||= "@#{screen_name}" + link_to(body, :controller => "users", :action => "best", :screen_name => screen_name) + end + + # utf8 + def utf8_enforcer_tag + "" + end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb new file mode 100644 index 0000000..b3ce20a --- /dev/null +++ b/app/helpers/search_helper.rb @@ -0,0 +1,2 @@ +module SearchHelper +end diff --git a/app/models/account.rb b/app/models/account.rb index 14b9da9..bd655db 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -5,7 +5,7 @@ class Account < ActiveRecord::Base def twitter_user Rails.cache.fetch("twitter_user/#{user_id}", :expires_in => 1.hour) do - client.user(user_id) + client.user(user_id) rescue nil end end @@ -16,4 +16,17 @@ class Account < ActiveRecord::Base :oauth_token => oauth_token, :oauth_token_secret => oauth_token_secret) end + + def stats_api + return {} unless twitter_user + { + favorites_count: twitter_user.favourites_count, + listed_count: twitter_user.listed_count, + followers_count: twitter_user.followers_count, + tweets_count: twitter_user.statuses_count, + friends_count: twitter_user.friends_count, + listed_count: twitter_user.listed_count, + bio: twitter_user.description + } + end end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index d30ff4e..3df7c3c 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -36,11 +36,10 @@ class Tweet < ActiveRecord::Base end scope :discovered_by, -> user do - where("id IN (" + - "SELECT tweet_id FROM favorites WHERE user_id = ?" + - " UNION ALL " + - "SELECT tweet_id FROM retweets WHERE user_id = ?" + - ")", user.id, user.id) + where("id IN (SELECT tweet_id FROM favorites WHERE user_id = ?)" + + " OR " + + "id IN (SELECT tweet_id FROM retweets WHERE user_id = ?)", + user.id, user.id) end def self.cached(id) diff --git a/app/models/user.rb b/app/models/user.rb index 9598236..58597cc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,38 +1,70 @@ class User < ActiveRecord::Base - def protected? - protected - end - has_many :tweets, :dependent => :delete_all has_many :favorites, :dependent => :delete_all has_many :retweets, :dependent => :delete_all - def self.cached(uid) - Rails.cache.fetch("user/#{uid}", :expires_in => 1.hour) do - where(:id => uid).first + def self.cached(identity) + if identity.is_a?(Fixnum) || identity.is_a?(Bignum) + Rails.cache.fetch("user/#{identity}", :expires_in => 3.hour) do + where(:id => identity).first + end + elsif identity.is_a?(String) + if /^[A-Za-z0-9_]{1,15}$/ =~ identity + uid = Rails.cache.fetch("screen_name/#{identity}", :expires_in => 3.hour) do + if user = where(:screen_name => identity).first + user.id + end + end + + cached(uid) + end + elsif identity.is_a?(NilClass) + nil + else + raise Exception, "Invalid identity: #{identity}" end end + def protected? + protected + end + def registered? - Account.exists?(:user_id => id) + account + end + + def account + Account.where(:user_id => id).first end def profile_image_url_original profile_image_url.sub(/_normal((\.(png|jpeg|gif))?)/, "\\1") end - def stats - return @stats_cache if @stats_cache - - hash = tweets.inject(Hash.new(0)) do |hash, m| - hash[:favorited_count] += m.favorites_count - hash[:retweeted_count] += m.retweets_count - hash + def twitter_user + if registered? + account.twitter_user + else + raise Exception, "why??" end - hash[:favorites_count] = favorites.count - hash[:retweets_count] = retweets.count - hash[:tweets_count] = tweets.count + end + + def stats + @stats_cache ||= ->{ + raise Aclog::Exceptions::UserNotRegistered unless account - @stats_cache = hash + tweets.inject( + {favorites_count: favorites.count, + retweets_count: retweets.count, + tweets_count: tweets.length, # cache: tweets.inject calls "SELECT `tweets`.*" + favorited_count: 0, + retweeted_count: 0, + stats_api: account.stats_api} + ) do |hash, m| + hash[:favorited_count] += m.favorites_count + hash[:retweeted_count] += m.retweets_count + hash + end + }.call end end diff --git a/app/views/layouts/_base.html.haml b/app/views/layouts/_base.html.haml index 8997bd5..e3cee24 100644 --- a/app/views/layouts/_base.html.haml +++ b/app/views/layouts/_base.html.haml @@ -12,10 +12,12 @@ = link_to "aclog", :controller => "main", :action => "index" %ul.nav.pull-right %li + = link_to "search", :controller => "search", :action => "search" + %li = link_to "about", :controller => "main", :action => "about" - - if session[:screen_name] + - if session[:user_id] %li= link_to "logout", :controller => "sessions", :action => "destroy" - %li= link_to "@#{session[:screen_name]}", :controller => "users", :action => "best", :screen_name => session[:screen_name] + %li= link_to_user_page session[:account].user.screen_name - else %li= link_to "login", "/i/login" .container diff --git a/app/views/main/api.html.haml b/app/views/main/api.html.haml index 8d5adef..8a42042 100644 --- a/app/views/main/api.html.haml +++ b/app/views/main/api.html.haml @@ -1,6 +1,8 @@ %h1 API %p ドキュメントは今度もっとわかりやすく書き直しますね +%p + aclog のweb版で見れるものすべて JSON で取れるようにしたい %h3 URL %p 全部 GET です @@ -45,11 +47,6 @@ %p 1- %p default: 1 %p リストのみ。ページ - %dt all - %dd - %p true or false - %p default: false - %p timeline のみ。反応がなかったツイートも含めるかどうか %dt tweets %dd %p favorite or retweet or all diff --git a/app/views/main/index.html.haml b/app/views/main/index.html.haml index a0ab627..c5b5446 100644 --- a/app/views/main/index.html.haml +++ b/app/views/main/index.html.haml @@ -33,4 +33,20 @@ %dd= link_to "/i/timeline", "/i/timeline" %p Favstar のパスにアクセスされた場合リダイレクトするようになっているはず… - +%h3 検索クエリ +半角スペース区切りで、すべて AND 検索になります。 +%dl + %dt ユーザーの + %dt user:sg4k + %dt 反応数 〜〜以上 + %dd reaction:30 + %dd favorite:30 + %dd retweet:30 + %dt 並び替え + %dd order:old + %dd order:reaction + %dd order:favorite + %dd order:retweet + %dt 期間 + %dd 20130328..20130330 + %dt ツイート本文の検索はできません。付けるかはわかんない diff --git a/app/views/shared/_search.html.haml b/app/views/shared/_search.html.haml new file mode 100644 index 0000000..2eeb390 --- /dev/null +++ b/app/views/shared/_search.html.haml @@ -0,0 +1,4 @@ +.search + = form_tag({:controller => "search", :action => "search"}, {:method => :get}) do + = text_field_tag :query, params[:query] + = submit_tag "search", :name => nil diff --git a/app/views/shared/_sidebar_search.html.haml b/app/views/shared/_sidebar_search.html.haml new file mode 100644 index 0000000..098b35c --- /dev/null +++ b/app/views/shared/_sidebar_search.html.haml @@ -0,0 +1,12 @@ +%ul.nav.nav-tabs.nav-stacked + %li + = link_to "about", :controller => "main", :action => "about" + %li + = link_to "api", :controller => "main", :action => "api" +%ul.nav.nav-tabs.nav-stacked + %li + = link_to "best", :controller => "i", :action => "best" + %li + = link_to "recent", :controller => "i", :action => "recent" + %li + = link_to "timeline", :controller => "i", :action => "timeline" diff --git a/app/views/shared/_sidebar_users.html.haml b/app/views/shared/_sidebar_users.html.haml index bfd4b4d..05e2377 100644 --- a/app/views/shared/_sidebar_users.html.haml +++ b/app/views/shared/_sidebar_users.html.haml @@ -1,17 +1,22 @@ .sidebar - .avatar= link_to (image_tag @user.profile_image_url_original, :alt => @user.screen_name, :width => 64, :height => 64, :class => "icon img-rounded"), :controller => "users", :action => "best", :screen_name => @user.screen_name - .screen_name= link_to "@#{@user.screen_name}", :controller => "users", :action => "best", :screen_name => @user.screen_name + .avatar + = link_to_user_page @user.screen_name do + = image_tag @user.profile_image_url_original, :alt => @user.screen_name, :width => 64, :height => 64, :class => "icon img-rounded" + .screen_name= link_to @user.screen_name, twitter_user_url(@user.screen_name) - if @user.registered? - %table.table.table-condensed.records - %tr - %td favorited - %td.data= @user.stats[:favorited_count] - %tr - %td retweeted - %td.data= @user.stats[:retweeted_count] - %tr - %td avg. fav - %td.data= ((@user.stats[:favorited_count] + 0.0) / @user.stats[:tweets_count]).round(2) + %ul.records + %li + %span favorited + %span.data= @user.stats[:favorited_count] + %li + %span retweeted + %span.data= @user.stats[:retweeted_count] + %li + %span avg. fav + %span.data= ((@user.stats[:favorited_count] + 0.0) / @user.stats[:tweets_count]).round(2) + %li + %span joined + %span.data= format_date_ago(@user.created_at) - else .alert.alert-info = "@#{@user.screen_name} has never signed in to aclog" @@ -23,8 +28,6 @@ %li = link_to "best", :controller => "users", :action => "best", :screen_name => @user.screen_name %li - = link_to "recent", :controller => "users", :action => "recent", :screen_name => @user.screen_name - %li = link_to "timeline", :controller => "users", :action => "timeline", :screen_name => @user.screen_name %li = link_to "discovered", :controller => "users", :action => "discovered", :screen_name => @user.screen_name diff --git a/app/views/shared/_tweet.html.haml b/app/views/shared/_tweet.html.haml index 97408e5..6c822eb 100644 --- a/app/views/shared/_tweet.html.haml +++ b/app/views/shared/_tweet.html.haml @@ -12,16 +12,16 @@ .tweet_content .user %span.name - = link_to item.user.name, :controller => "users", :action => "best", :screen_name => item.user.screen_name - %span.screen_name - = link_to "@#{item.user.screen_name}", :controller => "users", :action => "best", :screen_name => item.user.screen_name + = link_to_user_page item.user.screen_name do + = item.user.name + %span.screen_name= link_to_user_page item.user.screen_name .text = raw format_tweet_text(item.text) .meta.clearfix %span.twitter_bird = link_to image_tag("bird_gray_16.png", :alt => "Twitter"), twitter_status_url(item), :target => "_blank" %span.created_at - = link_to format_tweet_created_at(item.tweeted_at), :controller => "users", :action => "show", :id => item.id + = link_to format_time(item.tweeted_at), :controller => "users", :action => "show", :id => item.id %span.source = raw format_source_text(item.source) .stats @@ -36,6 +36,7 @@ - m = a.user %li - if m - = link_to image_tag(m.profile_image_url, :alt => m.screen_name, :title => m.name), :controller => "users", :action => "best", :screen_name => m.screen_name + = link_to_user_page m.screen_name do + = image_tag m.profile_image_url, :alt => m.screen_name, :title => m.name - else = image_tag asset_path("missing_profile_image.png"), :alt => "Missing User: #{a.user_id}", :title => "Missing User: #{a.user_id}" diff --git a/app/views/shared/tweets.html.haml b/app/views/shared/tweets.html.haml index f6a3f12..6ceb720 100644 --- a/app/views/shared/tweets.html.haml +++ b/app/views/shared/tweets.html.haml @@ -1,3 +1,5 @@ +- if @show_search + = render :partial => "shared/search" .items = render :partial => "shared/tweet", :collection => @items, :as => :item - if @items diff --git a/app/views/shared/users.html.haml b/app/views/shared/users.html.haml index 6393630..776a193 100644 --- a/app/views/shared/users.html.haml +++ b/app/views/shared/users.html.haml @@ -4,10 +4,12 @@ - user = User.cached(user_id) %li - if user - %a{:href => url_for(params.merge(:screen_name_b => user.screen_name))} + = link_to_user_page user.screen_name do .avatar= image_tag user.profile_image_url, :alt => user.screen_name, :title => user.name - else .avatar= image_tag asset_path("missing_profile_image.png"), :alt => "Missing User: #{user_id}", :title => "Missing User: #{user_id}" - .count= count - .type= @event_type + .data + = link_to url_for(params.merge(:screen_name_b => user.screen_name)) do + .count= count + .type= @event_type diff --git a/app/views/users/info.html.haml b/app/views/users/info.html.haml index 1beb940..2ca31d9 100644 --- a/app/views/users/info.html.haml +++ b/app/views/users/info.html.haml @@ -1,17 +1,29 @@ .avatar - = image_tag @user.profile_image_url_original, :alt => @user.screen_name + = image_tag @user.profile_image_url_original, alt: @user.screen_name, width: 128, height: 128 +%p profile %dl.dl-horizontal %dt Username %dd= @user.screen_name %dt Name - %dd= raw @twitter_user.name - %dt Tweets - %dd= @twitter_user.statuses_count + %dd= raw @user.name + %dt tweets + %dd= @user.stats[:stats_api][:tweets_count] %dt Following - %dd= @twitter_user.friends_count + %dd= @user.stats[:stats_api][:friends_count] %dt Followers - %dd= @twitter_user.followers_count + %dd= @user.stats[:stats_api][:followers_count] %dt Favorites - %dd= @twitter_user.favourites_count + %dd= @user.stats[:stats_api][:favorites_count] + %dt Listed + %dd= @user.stats[:stats_api][:listed_count] %dt Bio - %dd= raw @twitter_user.description + %dd= raw @user.stats[:stats_api][:bio] +- if @user.registered? + %p records + %dl.dl-horizontal + %dt Favorited + %dd= @user.stats[:favorited_count] + %dt Retweeted + %dd= @user.stats[:retweeted_count] + %dt Since + %dd= @user.account.created_at |