aboutsummaryrefslogtreecommitdiffstats
path: root/app/models/tweet.rb
diff options
context:
space:
mode:
authorRhenium <rhenium@rhe.jp>2014-03-18 23:07:02 +0900
committerRhenium <rhenium@rhe.jp>2014-03-18 23:07:02 +0900
commitadef4c0c2087e1e475ab1aa4b4df68554bdb1a88 (patch)
tree38b10ca182f2676e3ed55ba1ea778bcf941b5fd7 /app/models/tweet.rb
parentc73aecc3605aee69a12b4f02a4d01e00b77cac83 (diff)
downloadaclog-adef4c0c2087e1e475ab1aa4b4df68554bdb1a88.tar.gz
refactor models
Diffstat (limited to 'app/models/tweet.rb')
-rw-r--r--app/models/tweet.rb235
1 files changed, 114 insertions, 121 deletions
diff --git a/app/models/tweet.rb b/app/models/tweet.rb
index b7121a9..9229e10 100644
--- a/app/models/tweet.rb
+++ b/app/models/tweet.rb
@@ -10,6 +10,8 @@ class Tweet < ActiveRecord::Base
has_many :favoriters, -> { order("favorites.id") }, through: :favorites, source: :user
has_many :retweeters, -> { order("retweets.id") }, through: :retweets, source: :user
+ scope :eager_load_for_html, -> { eager_load(:user) }
+
scope :recent, ->(period = 3.days) { where("tweets.id > ?", snowflake_min(Time.zone.now - period)) }
scope :reacted, ->(count = nil) { where("reactions_count >= ?", (count || 1).to_i) }
scope :not_protected, -> { joins(:user).references(:user).where(users: { protected: false }) }
@@ -35,6 +37,117 @@ class Tweet < ActiveRecord::Base
joins("INNER JOIN ((#{un})) reactions ON reactions.tweet_id = tweets.id")
}
+ class << self
+ def create_from_json(json)
+ tweet = transaction do
+ self.find_by(id: json[:id]) ||
+ self.create!(id: json[:id],
+ text: extract_entities(json),
+ source: json[:source],
+ tweeted_at: json[:created_at],
+ in_reply_to_id: json[:in_reply_to_status_id],
+ user: User.create_from_json(json[:user]))
+ end
+ rescue ActiveRecord::RecordNotUnique => e
+ logger.debug("Duplicate tweet: #{tweet}: #{e.class}") # collector may be threaded
+ self.find(json[:id])
+ end
+
+ def create_from_twitter_object(obj)
+ t = self.create_from_json(obj.attrs)
+ t.update_reactions_count(json: obj.attrs)
+ t
+ end
+
+ def destroy_from_json(json)
+ deleted_count = self.delete(json[:delete][:status][:id])
+
+ if deleted_count > 0
+ Favorite.where(tweet_id: json[:delete][:status][:id]).delete_all
+ Retweet.where(tweet_id: json[:delete][:status][:id]).delete_all
+ true
+ else
+ false
+ end
+ end
+
+ def import(id, client = nil)
+ client ||= Account.random.client
+
+ st = client.status(id)
+ tweet = self.create_from_twitter_object(st)
+ tweet.update(text: extract_entities(st.attrs),
+ source: st.attrs[:source],
+ in_reply_to_id: (tweet.in_reply_to_id || st.attrs[:in_reply_to_status_id]))
+
+ nt = tweet
+ nt = self.create_from_twitter_object(client.status(nt.in_reply_to_id)) while !nt.in_reply_to && nt.in_reply_to_id
+
+ tweet.reload
+ end
+
+ def filter_by_query(query)
+ strings = []
+ query.gsub!(/"((?:\\"|[^"])*?)"/) {|m| strings << $1; "##{strings.size - 1}" }
+
+ escape_text = -> str do
+ str.gsub(/#(\d+)/) { strings[$1.to_i] }
+ .gsub("%", "\\%")
+ .gsub("*", "%")
+ .gsub("_", "\\_")
+ .gsub("?", "_")
+ end
+
+ parse_condition = ->(scoped, token) do
+ positive = !token.slice!(/^[-!]/)
+
+ where_args = case token
+ when /^(?:user|from):([A-Za-z0-9_]{1,20})$/
+ u = User.find_by(screen_name: $1)
+ uid = u && u.id || -1
+ { user_id: uid }
+ when /^fav(?:orite)?s?:(\d+)$/
+ ["favorites_count >= ?", $1.to_i]
+ when /^(?:retweet|rt)s?:(\d+)$/
+ ["retweets_count >= ?", $1.to_i]
+ when /^(?:sum|(?:re)?act(?:ion)?s?):(\d+)$/
+ ["reactions_count >= ?", $1.to_i]
+ when /^(?:source|via):(.+)$/
+ ["source LIKE ?", escape_text.call($1)]
+ when /^text:(.+)$/
+ ["text LIKE ?", "%" + escape_text.call($1) + "%"]
+ else
+ nil
+ end
+
+ positive ? scoped.where(where_args) : scoped.where.not(where_args)
+ end
+
+ query.scan(/\S+/).inject(self.all) {|s, token| parse_condition.call(s, token) }
+ end
+
+ private
+ # replace t.co with expanded_url
+ def extract_entities(json)
+ entity_values = json[:entities].values.flatten.sort_by {|v| v[:indices].first }
+ entity_values.select! {|e| e[:url] }
+
+ result = ""
+ last_index = entity_values.inject(0) do |last_index, entity|
+ result << json[:text][last_index...entity[:indices].first]
+ result << entity[:expanded_url]
+ entity[:indices].last
+ end
+ result << json[:text][last_index..-1]
+
+ result
+ end
+
+ def snowflake_min(time)
+ (time.to_datetime.to_i * 1000 - 1288834974657) << 22
+ end
+ end
+
def twitter_url
"https://twitter.com/#{self.user.screen_name}/status/#{self.id}"
end
@@ -45,7 +158,7 @@ class Tweet < ActiveRecord::Base
level = 0
while node.in_reply_to && level < max_level
- nodes.unshift(node = node.in_reply_to)
+ nodes << (node = node.in_reply_to)
level += 1
end
nodes
@@ -71,124 +184,4 @@ class Tweet < ActiveRecord::Base
"retweets_count = GREATEST(retweets_count #{rts_op} #{retweets_count.abs}, #{(json[:retweet_count] || 0).to_i}), " +
"reactions_count = favorites_count + retweets_count")
end
-
- def self.create_from_json(json)
- tweet = transaction do
- self.find_by(id: json[:id]) ||
- self.create!(id: json[:id],
- text: extract_entities(json),
- source: json[:source],
- tweeted_at: json[:created_at],
- in_reply_to_id: json[:in_reply_to_status_id],
- user: User.create_from_json(json[:user]))
- end
- rescue ActiveRecord::RecordNotUnique => e
- logger.debug("Duplicate tweet: #{tweet}: #{e.class}")
- rescue => e
- logger.error("Failed to create a tweet: #{tweet}: #{e.class}: #{e.message}/#{e.backtrace.join("\n")}")
- ensure
- return tweet
- end
-
- def self.create_from_twitter_object(obj)
- t = self.create_from_json(obj.attrs)
- t.update_reactions_count(json: obj.attrs)
- t
- end
-
- def self.destroy_from_json(json)
- deleted_count = self.delete(json[:delete][:status][:id])
-
- if deleted_count > 0
- Favorite.where(tweet_id: json[:delete][:status][:id]).delete_all
- Retweet.where(tweet_id: json[:delete][:status][:id]).delete_all
- true
- else
- false
- end
- end
-
- def self.import(id, client = nil)
- client ||= Account.random.client
-
- st = client.status(id)
- tweet = self.create_from_twitter_object(st)
- tweet.update(text: extract_entities(st.attrs),
- source: st.attrs[:source],
- in_reply_to_id: st.attrs[:in_reply_to_status_id])
-
- begin
- nt = tweet
- nt = self.create_from_twitter_object(client.status(nt.in_reply_to_id)) while !nt.in_reply_to && nt.in_reply_to_id
- rescue Twitter::Error
- logger.warn($!)
- end
-
- tweet.reload
- end
-
- def self.eager_load_for_html
- self.eager_load(:user)
- end
-
- def self.filter_by_query(query)
- strings = []
- query.gsub!(/"((?:\\"|[^"])*?)"/) {|m| strings << $1; "##{strings.size - 1}" }
-
- escape_text = -> str do
- str.gsub(/#(\d+)/) { strings[$1.to_i] }
- .gsub("%", "\\%")
- .gsub("*", "%")
- .gsub("_", "\\_")
- .gsub("?", "_")
- end
-
- parse_condition = ->(scoped, token) do
- positive = !token.slice!(/^[-!]/)
-
- where_args = case token
- when /^(?:user|from):([A-Za-z0-9_]{1,20})$/
- u = User.find_by(screen_name: $1)
- uid = u && u.id || -1
- { user_id: uid }
- when /^fav(?:orite)?s?:(\d+)$/
- ["favorites_count >= ?", $1.to_i]
- when /^(?:retweet|rt)s?:(\d+)$/
- ["retweets_count >= ?", $1.to_i]
- when /^(?:sum|(?:re)?act(?:ion)?s?):(\d+)$/
- ["reactions_count >= ?", $1.to_i]
- when /^(?:source|via):(.+)$/
- ["source LIKE ?", escape_text.call($1)]
- when /^text:(.+)$/
- ["text LIKE ?", "%" + escape_text.call($1) + "%"]
- else
- nil
- end
-
- positive ? scoped.where(where_args) : scoped.where.not(where_args)
- end
-
- query.scan(/\S+/).inject(self.all) {|s, token| parse_condition.call(s, token) }
- end
-
- private
- # replace t.co with expanded_url
- def self.extract_entities(json)
- entity_values = json[:entities].values.flatten.sort_by {|v| v[:indices].first }
- entity_values.select! {|e| e[:url] }
-
- result = ""
- last_index = entity_values.inject(0) do |last_index, entity|
- result << json[:text][last_index...entity[:indices].first]
- result << entity[:expanded_url]
- entity[:indices].last
- end
- result << json[:text][last_index..-1]
-
- result
- end
-
- def self.snowflake_min(time)
- (time.to_datetime.to_i * 1000 - 1288834974657) << 22
- end
end