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 | |
parent | 61087e2460e60b08b554b600cdebc6efb2456dd6 (diff) | |
download | aclog-b1cd0b45b761132e47cbc01e99acd728f047b9ae.tar.gz |
Refactor
Add search (partial)
44 files changed, 412 insertions, 139 deletions
@@ -6,6 +6,7 @@ gem 'mysql2' gem 'dalli' gem 'unicorn' +gem 'thin' gem 'daemon-spawn', :require => 'daemon_spawn' gem 'rails_config' diff --git a/Gemfile.lock b/Gemfile.lock index b8ad380..0ff8dbb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,6 +46,7 @@ GEM execjs coffee-script-source (1.6.2) daemon-spawn (0.4.2) + daemons (1.1.9) dalli (2.6.2) em-twitter (0.2.1) eventmachine (~> 1.0) @@ -134,6 +135,10 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (~> 2.8) + thin (1.5.1) + daemons (>= 1.0.9) + eventmachine (>= 0.12.6) + rack (>= 1.0.0) thor (0.17.0) thread_safe (0.1.0) atomic @@ -173,6 +178,7 @@ DEPENDENCIES rails (= 4.0.0.beta1) rails_config sass-rails (~> 4.0.0.beta1) + thin twitter uglifier (>= 1.0.3) unicorn 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 diff --git a/client/worker.rb b/client/worker.rb index eeb7bf9..951b8f7 100644 --- a/client/worker.rb +++ b/client/worker.rb @@ -24,13 +24,13 @@ class Worker last_index = entities.inject(0) do |last_index, entity| result << chars[last_index...entity[:indices].first] result << if entity[:url] - "<url:#{CGI.escape(entity[:expanded_url])}:#{CGI.escape(entity[:display_url])}>" + "<url:#{escape_colon(entity[:expanded_url])}:#{escape_colon(entity[:display_url])}>" elsif entity[:text] - "<hashtag:#{CGI.escape(entity[:text])}>" + "<hashtag:#{escape_colon(entity[:text])}>" elsif entity[:screen_name] - "<mention:#{CGI.escape(entity[:screen_name])}>" + "<mention:#{escape_colon(entity[:screen_name])}>" elsif entity[:cashtag] - "<cashtag:#{CGI.escape(entity[:cashtag])}>" + "<cashtag:#{escape_colon(entity[:cashtag])}>" end entity[:indices].last end @@ -39,6 +39,8 @@ class Worker result.flatten.join end + def escape_colon(str); str.gsub(":", "%3A").gsub("<", "%3C").gsub(">", "%3E"); end + def format_source(status) if status[:source].index("<a") url = status[:source].scan(/href="(.+?)"/).flatten.first @@ -192,6 +194,7 @@ class Worker send_retweet.call(hash) end elsif hash[:user][:id] == user_id + # update: exclude not favorited tweet send_tweet.call(hash) end elsif hash[:friends] diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 0a965bc..2bbd73d 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -Aclog::Application.config.session_store :encrypted_cookie_store, key: '_Aclog_session' +Aclog::Application.config.session_store ActionDispatch::Session::CacheStore, :expire_after => 3.days diff --git a/config/routes.rb b/config/routes.rb index 73abb83..09e0b43 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,6 +28,8 @@ Aclog::Application.routes.draw do get "/i/show" => "users#show" get "/i/:id" => "users#show", :constraints => constraints + get "/search" => "search#search" + # i -- end get "/i(/:none)" => redirect("/") @@ -48,7 +50,6 @@ Aclog::Application.routes.draw do get "/:screen_name/recent(/:page)" => "users#recent", :constraints => constraints get "/:screen_name/recent/:order(/:page)" => "users#recent", :constraints => constraints get "/:screen_name/timeline(/:page)" => "users#timeline", :constraints => constraints - get "/:screen_name/timeline/all(/:page)" => "users#timeline", :constraints => constraints, :defaults => {:all => "true"} get "/:screen_name/discovered(/:page)" => "users#discovered", :constraints => constraints get "/:screen_name/discovered/:tweets(/:page)" => "users#discovered", :constraints => constraints get "/:screen_name/info" => "users#info", :constraints => constraints diff --git a/public/assets/application-252e3d8b1032ad10b8a6615bb347aad0.js b/public/assets/application-252e3d8b1032ad10b8a6615bb347aad0.js new file mode 100644 index 0000000..3355de2 --- /dev/null +++ b/public/assets/application-252e3d8b1032ad10b8a6615bb347aad0.js @@ -0,0 +1,57 @@ +(function() { + if (window.__twitterIntentHandler) return; + var intentRegex = /twitter\.com(\:\d{2,4})?\/intent\/(\w+)/, + windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes', + width = 550, + height = 420, + winHeight = screen.height, + winWidth = screen.width; + + function handleIntent(e) { + e = e || window.event; + var target = e.target || e.srcElement, + m, left, top; + + while (target && target.nodeName.toLowerCase() !== 'a') { + target = target.parentNode; + } + + if (target && target.nodeName.toLowerCase() === 'a' && target.href) { + m = target.href.match(intentRegex); + if (m) { + left = Math.round((winWidth / 2) - (width / 2)); + top = 0; + + if (winHeight > height) { + top = Math.round((winHeight / 2) - (height / 2)); + } + + window.open(target.href, 'intent', windowOptions + ',width=' + width + + ',height=' + height + ',left=' + left + ',top=' + top); + e.returnValue = false; + e.preventDefault && e.preventDefault(); + } + } + } + + if (document.addEventListener) { + document.addEventListener('click', handleIntent, false); + } else if (document.attachEvent) { + document.attachEvent('onclick', handleIntent); + } + window.__twitterIntentHandler = true; +}()); +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// the compiled file. +// +// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD +// GO AFTER THE REQUIRES BELOW. +// + +; diff --git a/public/assets/application-252e3d8b1032ad10b8a6615bb347aad0.js.gz b/public/assets/application-252e3d8b1032ad10b8a6615bb347aad0.js.gz Binary files differnew file mode 100644 index 0000000..fb5c3ab --- /dev/null +++ b/public/assets/application-252e3d8b1032ad10b8a6615bb347aad0.js.gz diff --git a/public/assets/application-eb873c58ea6dc20a9ecb48c9bd070d3c.css b/public/assets/application-38ff4e9e27d0ec3d917e7d43fcec9197.css index 3259ade..97f786b 100644 --- a/public/assets/application-eb873c58ea6dc20a9ecb48c9bd070d3c.css +++ b/public/assets/application-38ff4e9e27d0ec3d917e7d43fcec9197.css @@ -4854,12 +4854,20 @@ a.label:hover, a.label:focus, a.badge:hover, a.badge:focus { border: 1px solid #c1c5cb; border-width: 1px 0; padding: 15px; } - .items .item .tweet .avatar { - width: 60px; - float: left; } - .items .item .tweet .avatar img { + .items .item .tweet .left { + float: left; + width: 60px; } + .items .item .tweet .left .avatar img { width: 48px; height: 48px; } + .items .item .tweet .left ul.actions li { + padding: 0; } + .items .item .tweet .left ul.actions li a { + float: left; } + .items .item .tweet .left ul.actions li a img { + width: 16px; + height: 16px; + vertical-align: bottom; } .items .item .tweet .tweet_content_fix { float: left; width: 0; @@ -4965,8 +4973,25 @@ a.label:hover, a.label:focus, a.badge:hover, a.badge:focus { margin: 10px 0; } .sidebar .avatar { text-align: center; } - .sidebar .records .data { - text-align: right; } + .sidebar .records { + list-style: none; + margin: 0 0 20px; } + .sidebar .records li { + *zoom: 1; + border-top: 1px solid #dddddd; } + .sidebar .records li:before, .sidebar .records li:after { + display: table; + content: ""; + line-height: 0; } + .sidebar .records li:after { + clear: both; } + .sidebar .records li span { + display: block; + float: left; + line-height: 20px; + padding: 4px 5px; } + .sidebar .records li .data { + float: right; } a { text-decoration: none; diff --git a/public/assets/application-eb873c58ea6dc20a9ecb48c9bd070d3c.css.gz b/public/assets/application-38ff4e9e27d0ec3d917e7d43fcec9197.css.gz Binary files differindex 7cf8835..7140e98 100644 --- a/public/assets/application-eb873c58ea6dc20a9ecb48c9bd070d3c.css.gz +++ b/public/assets/application-38ff4e9e27d0ec3d917e7d43fcec9197.css.gz diff --git a/public/assets/application-c0feb24db0871514682a56320c7b2b2b.js b/public/assets/application-c0feb24db0871514682a56320c7b2b2b.js deleted file mode 100644 index d287f32..0000000 --- a/public/assets/application-c0feb24db0871514682a56320c7b2b2b.js +++ /dev/null @@ -1,34 +0,0 @@ -(function() { - - -}).call(this); -(function() { - - -}).call(this); -(function() { - - -}).call(this); -(function() { - - -}).call(this); -(function() { - - -}).call(this); -// This is a manifest file that'll be compiled into application.js, which will include all the files -// listed below. -// -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, -// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. -// -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// the compiled file. -// -// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD -// GO AFTER THE REQUIRES BELOW. -// - -; diff --git a/public/assets/application-c0feb24db0871514682a56320c7b2b2b.js.gz b/public/assets/application-c0feb24db0871514682a56320c7b2b2b.js.gz Binary files differdeleted file mode 100644 index b820f5f..0000000 --- a/public/assets/application-c0feb24db0871514682a56320c7b2b2b.js.gz +++ /dev/null diff --git a/public/assets/favorite-f705dbd122eeb6b0a17f75306600f33c.png b/public/assets/favorite-f705dbd122eeb6b0a17f75306600f33c.png Binary files differnew file mode 100644 index 0000000..d4c53be --- /dev/null +++ b/public/assets/favorite-f705dbd122eeb6b0a17f75306600f33c.png diff --git a/public/assets/manifest-48ac099aa1a004ba8c6f9a787cc5fe11.json b/public/assets/manifest-48ac099aa1a004ba8c6f9a787cc5fe11.json new file mode 100644 index 0000000..e030656 --- /dev/null +++ b/public/assets/manifest-48ac099aa1a004ba8c6f9a787cc5fe11.json @@ -0,0 +1 @@ +{"files":{"bird_gray_16-93f51980875017e26744056b62aaac22.png":{"logical_path":"bird_gray_16.png","mtime":"2013-03-09T14:41:39+09:00","size":1106,"digest":"93f51980875017e26744056b62aaac22"},"favorite-f705dbd122eeb6b0a17f75306600f33c.png":{"logical_path":"favorite.png","mtime":"2013-03-28T13:53:46+09:00","size":1329,"digest":"f705dbd122eeb6b0a17f75306600f33c"},"missing_profile_image-d5d708680693a18615dfb8f5b1a5a96c.png":{"logical_path":"missing_profile_image.png","mtime":"2013-03-16T16:45:26+09:00","size":9350,"digest":"d5d708680693a18615dfb8f5b1a5a96c"},"protected_profile_image-d5d708680693a18615dfb8f5b1a5a96c.png":{"logical_path":"protected_profile_image.png","mtime":"2013-03-28T01:43:45+09:00","size":9350,"digest":"d5d708680693a18615dfb8f5b1a5a96c"},"rails-bc7d436ef8afbf0f88829742a43ba3a4.png":{"logical_path":"rails.png","mtime":"2013-02-25T21:20:52+09:00","size":6646,"digest":"bc7d436ef8afbf0f88829742a43ba3a4"},"reply-aded44c637c7902c47d64921125bec96.png":{"logical_path":"reply.png","mtime":"2013-03-28T15:24:04+09:00","size":1153,"digest":"aded44c637c7902c47d64921125bec96"},"retweet-d214a384fd3a031f48046b77e5c15d8d.png":{"logical_path":"retweet.png","mtime":"2013-03-28T13:56:02+09:00","size":1095,"digest":"d214a384fd3a031f48046b77e5c15d8d"},"application-252e3d8b1032ad10b8a6615bb347aad0.js":{"logical_path":"application.js","mtime":"2013-03-28T15:50:43+09:00","size":1939,"digest":"252e3d8b1032ad10b8a6615bb347aad0"},"application-38ff4e9e27d0ec3d917e7d43fcec9197.css":{"logical_path":"application.css","mtime":"2013-03-29T03:09:23+09:00","size":132403,"digest":"38ff4e9e27d0ec3d917e7d43fcec9197"},"glyphicons-halflings-white-0e5e72373e37a02757721decf4d90757.png":{"logical_path":"glyphicons-halflings-white.png","mtime":"2013-03-26T17:49:47+09:00","size":8777,"digest":"0e5e72373e37a02757721decf4d90757"},"glyphicons-halflings-f358b5f4bb585b4c519a838dba74111a.png":{"logical_path":"glyphicons-halflings.png","mtime":"2013-03-26T17:49:47+09:00","size":12799,"digest":"f358b5f4bb585b4c519a838dba74111a"}},"assets":{"bird_gray_16.png":"bird_gray_16-93f51980875017e26744056b62aaac22.png","favorite.png":"favorite-f705dbd122eeb6b0a17f75306600f33c.png","missing_profile_image.png":"missing_profile_image-d5d708680693a18615dfb8f5b1a5a96c.png","protected_profile_image.png":"protected_profile_image-d5d708680693a18615dfb8f5b1a5a96c.png","rails.png":"rails-bc7d436ef8afbf0f88829742a43ba3a4.png","reply.png":"reply-aded44c637c7902c47d64921125bec96.png","retweet.png":"retweet-d214a384fd3a031f48046b77e5c15d8d.png","application.js":"application-252e3d8b1032ad10b8a6615bb347aad0.js","application.css":"application-38ff4e9e27d0ec3d917e7d43fcec9197.css","glyphicons-halflings-white.png":"glyphicons-halflings-white-0e5e72373e37a02757721decf4d90757.png","glyphicons-halflings.png":"glyphicons-halflings-f358b5f4bb585b4c519a838dba74111a.png"}}
\ No newline at end of file diff --git a/public/assets/manifest-b6e16b8d09a3fc9041a0a623c7b857af.json b/public/assets/manifest-b6e16b8d09a3fc9041a0a623c7b857af.json deleted file mode 100644 index 5b52f0e..0000000 --- a/public/assets/manifest-b6e16b8d09a3fc9041a0a623c7b857af.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"bird_gray_16-93f51980875017e26744056b62aaac22.png":{"logical_path":"bird_gray_16.png","mtime":"2013-03-09T14:41:39+09:00","size":1106,"digest":"93f51980875017e26744056b62aaac22"},"missing_profile_image-d5d708680693a18615dfb8f5b1a5a96c.png":{"logical_path":"missing_profile_image.png","mtime":"2013-03-16T16:45:26+09:00","size":9350,"digest":"d5d708680693a18615dfb8f5b1a5a96c"},"rails-bc7d436ef8afbf0f88829742a43ba3a4.png":{"logical_path":"rails.png","mtime":"2013-02-25T21:20:52+09:00","size":6646,"digest":"bc7d436ef8afbf0f88829742a43ba3a4"},"application-c0feb24db0871514682a56320c7b2b2b.js":{"logical_path":"application.js","mtime":"2013-03-09T01:51:23+09:00","size":738,"digest":"c0feb24db0871514682a56320c7b2b2b"},"application-eb873c58ea6dc20a9ecb48c9bd070d3c.css":{"logical_path":"application.css","mtime":"2013-03-27T17:43:10+09:00","size":131606,"digest":"eb873c58ea6dc20a9ecb48c9bd070d3c"},"glyphicons-halflings-white-0e5e72373e37a02757721decf4d90757.png":{"logical_path":"glyphicons-halflings-white.png","mtime":"2013-03-26T17:49:47+09:00","size":8777,"digest":"0e5e72373e37a02757721decf4d90757"},"glyphicons-halflings-f358b5f4bb585b4c519a838dba74111a.png":{"logical_path":"glyphicons-halflings.png","mtime":"2013-03-26T17:49:47+09:00","size":12799,"digest":"f358b5f4bb585b4c519a838dba74111a"}},"assets":{"bird_gray_16.png":"bird_gray_16-93f51980875017e26744056b62aaac22.png","missing_profile_image.png":"missing_profile_image-d5d708680693a18615dfb8f5b1a5a96c.png","rails.png":"rails-bc7d436ef8afbf0f88829742a43ba3a4.png","application.js":"application-c0feb24db0871514682a56320c7b2b2b.js","application.css":"application-eb873c58ea6dc20a9ecb48c9bd070d3c.css","glyphicons-halflings-white.png":"glyphicons-halflings-white-0e5e72373e37a02757721decf4d90757.png","glyphicons-halflings.png":"glyphicons-halflings-f358b5f4bb585b4c519a838dba74111a.png"}}
\ No newline at end of file diff --git a/public/assets/protected_profile_image-d5d708680693a18615dfb8f5b1a5a96c.png b/public/assets/protected_profile_image-d5d708680693a18615dfb8f5b1a5a96c.png Binary files differnew file mode 100644 index 0000000..3d1e0f9 --- /dev/null +++ b/public/assets/protected_profile_image-d5d708680693a18615dfb8f5b1a5a96c.png diff --git a/public/assets/reply-aded44c637c7902c47d64921125bec96.png b/public/assets/reply-aded44c637c7902c47d64921125bec96.png Binary files differnew file mode 100644 index 0000000..5493c72 --- /dev/null +++ b/public/assets/reply-aded44c637c7902c47d64921125bec96.png diff --git a/public/assets/retweet-d214a384fd3a031f48046b77e5c15d8d.png b/public/assets/retweet-d214a384fd3a031f48046b77e5c15d8d.png Binary files differnew file mode 100644 index 0000000..94e3595 --- /dev/null +++ b/public/assets/retweet-d214a384fd3a031f48046b77e5c15d8d.png diff --git a/test/controllers/search_controller_test.rb b/test/controllers/search_controller_test.rb new file mode 100644 index 0000000..bfbf22d --- /dev/null +++ b/test/controllers/search_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class SearchControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/helpers/search_helper_test.rb b/test/helpers/search_helper_test.rb new file mode 100644 index 0000000..3034163 --- /dev/null +++ b/test/helpers/search_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class SearchHelperTest < ActionView::TestCase +end |