bluebird 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NzdhMmI0OGJlM2U2ZjEwY2IxMzg0ZmJlZmNjOTNhYzg5MTQxOWY3Yg==
5
+ data.tar.gz: !binary |-
6
+ YTA5YmQ1MjQxNWE5MzBhNjQwNzc5NzRiY2QyNzQ2MTEzNDZjZDY0YQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NTIxM2QzMGExZGYxNjY0ZTRhMmZkZDc5N2M1MDllODA2ZTliMjQzZWZiNzY4
10
+ ZDczYzJkNjdkN2JkZTczNTI1OGQ3OTYyYTc1ZmY5OThiZjhkYjI1ODczNTAy
11
+ OWU1YzIwYmJjM2M1ZTNjMmQ1OGMzNjkxZGFkZGMwZjI0YTEwMTU=
12
+ data.tar.gz: !binary |-
13
+ NzFiYTYxY2M3ZmQ4YTlmNjZhNDE4MmUwZDQzMmYxMjUxMjM4MDRjMTI1Y2Ey
14
+ MjIzYzU4MzE2ODU5ODJjM2UwYzUzYjRiMGQwYTgxZTk3ZTUxYjc1YzA2YTA2
15
+ YTdlNTRiMGJhYmMyNzVmMmRiMWFhMDhiZmIwNjE0NzU0NDdhN2Q=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 İ. Emre Kutlu
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,62 @@
1
+ # Bluebird
2
+
3
+ Bluebird modifies your tweets, mostly to fit 140 characters.
4
+ It uses "strategies". Every strategy makes its own modifications to the tweets.
5
+ It does not ensure that tweet will fit 140 characters. (More strategies will be added to ensure it.)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ````ruby
12
+ gem 'bluebird'
13
+ ````
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install bluebird
22
+
23
+ ## Configuration
24
+
25
+ You do not need any configuration to start using bluebird but it is intended for a quick start. <code>characters_reserved_per_media</code>, <code>short_url_length_https</code> and <code>short_url_length</code> can change any time, **it is your duty** to fetch these values from twitter API and change bluebird configuration. For more information visit [twitter docs](https://dev.twitter.com/docs/api/1.1/get/help/configuration).
26
+
27
+
28
+ These are the default values:
29
+
30
+ ````ruby
31
+ Bluebird.configure do |config|
32
+ config.strategies = [:strip, :squeeze, :truncate_text]
33
+ config.max_length = 140
34
+ config.characters_reserved_per_media = 23
35
+ config.short_url_length_https = 23
36
+ config.short_url_length = 22
37
+
38
+ config.truncate_text.omission = '...'
39
+ end
40
+ ````
41
+
42
+ If you want to change defaults for Rails, create a file in <code>initializers</code> folder and copy the code above.
43
+
44
+ By default bluebird runs three strategies in these order; <code>strip</code>, <code>squeeze</code> and <code>truncate</code>. For detailed information about each strategy, visit [Strategies](https://github.com/emrekutlu/bluebird/wiki/Strategies).
45
+
46
+ ## Usage
47
+
48
+ ````ruby
49
+ Bluebird.modify('The bluebirds are a group of medium-sized, mostly insectivorous or omnivorous birds in the genus Sialia of the thrush family (Turdidae). #bluebird #animals')
50
+ # => 'The bluebirds are a group of medium-sized, mostly insectivorous or omnivorous birds in the genus Sialia of the thrush ... #bluebird #animals'
51
+ ````
52
+
53
+ If uploading media:
54
+
55
+ ````ruby
56
+ Bluebird.modify('The bluebirds are a group of medium-sized, mostly insectivorous or omnivorous birds in the genus Sialia of the thrush family (Turdidae). #bluebird #animals', media: true)
57
+ # => 'The bluebirds are a group of medium-sized, mostly insectivorous or omnivorous birds in the genu... #bluebird #animals'
58
+ ````
59
+
60
+ ## License
61
+
62
+ Bluebird is released under the [MIT License](https://github.com/emrekutlu/bluebird/blob/master/LICENSE.txt).
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task default: :spec
7
+ task test: :spec
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bluebird/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'bluebird'
8
+ spec.version = Bluebird::VERSION
9
+ spec.authors = ['İ. Emre Kutlu']
10
+ spec.email = ['emrekutlu@gmail.com']
11
+ spec.description = %q{Modifies your tweets mostly to fit 140 characters}
12
+ spec.summary = %q{Modifies your tweets}
13
+ spec.homepage = 'http://github.com/emrekutlu/bluebird'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'twitter-text', '~> 1.6'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'rake', '~> 10.1'
25
+ spec.add_development_dependency 'rspec', '~> 2.14'
26
+ end
@@ -0,0 +1,56 @@
1
+ require 'twitter-text'
2
+
3
+ require 'bluebird/version'
4
+ require 'bluebird/config'
5
+ require 'bluebird/tweet'
6
+ require 'bluebird/partial'
7
+ require 'bluebird/strategies/base'
8
+ require 'bluebird/strategies/strip'
9
+ require 'bluebird/strategies/squeeze'
10
+ require 'bluebird/strategies/truncate_text'
11
+
12
+ module Bluebird
13
+ class << self
14
+
15
+ def configure
16
+ yield(Config)
17
+ end
18
+
19
+ def modify(status, opts = {})
20
+ tweet = Bluebird::Tweet.new(status, opts)
21
+ run_strategies(tweet)
22
+ tweet.status
23
+ end
24
+
25
+ private
26
+
27
+ def run_strategies(tweet)
28
+ Config.strategies.each do |strategy|
29
+ strategy_by_symbol(strategy).run(tweet, Config)
30
+ end
31
+ end
32
+
33
+ def symbol_to_class(symbol)
34
+ symbol.to_s.split('_').map do |string|
35
+ i = 0
36
+ string.chars.map do |char|
37
+ i += 1
38
+ if i.eql?(1)
39
+ char.upcase
40
+ else
41
+ char
42
+ end
43
+ end
44
+ end.join
45
+ end
46
+
47
+ def strategy_by_symbol(symbol)
48
+ strategies_module.const_get(symbol_to_class(symbol)).const_get('Strategy')
49
+ end
50
+
51
+ def strategies_module
52
+ @strategies_module ||= Object.const_get('Bluebird').const_get('Strategies')
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,21 @@
1
+ module Bluebird
2
+ class Config
3
+
4
+ @strategies = [:strip, :squeeze, :truncate_text]
5
+ @max_length = 140
6
+ @characters_reserved_per_media = 23
7
+ @short_url_length_https = 23
8
+ @short_url_length = 22
9
+
10
+ class << self
11
+ attr_accessor :strategies, :max_length, :characters_reserved_per_media, :short_url_length_https, :short_url_length
12
+
13
+ def register(name, klass)
14
+ singleton_class.send(:attr_accessor, name)
15
+ send("#{name}=", klass)
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,70 @@
1
+ module Bluebird
2
+ class Partial
3
+
4
+ attr_accessor :content, :prev_partial, :next_partial
5
+
6
+ def initialize(content, partial_type)
7
+ @content = content
8
+ @partial_type = partial_type
9
+ end
10
+
11
+ def length
12
+ if url?
13
+ https? ? Config.short_url_length_https : Config.short_url_length
14
+ else
15
+ content.char_length
16
+ end
17
+ end
18
+
19
+ def first?
20
+ !prev_partial
21
+ end
22
+
23
+ def last?
24
+ !next_partial
25
+ end
26
+
27
+ def text?
28
+ @partial_type.eql?(:text)
29
+ end
30
+
31
+ def mention?
32
+ @partial_type.eql?(:mention)
33
+ end
34
+
35
+ def list?
36
+ @partial_type.eql?(:list)
37
+ end
38
+
39
+ def url?
40
+ @partial_type.eql?(:url)
41
+ end
42
+
43
+ def hashtag?
44
+ @partial_type.eql?(:hashtag)
45
+ end
46
+
47
+ def cashtag?
48
+ @partial_type.eql?(:cashtag)
49
+ end
50
+
51
+ def entity?
52
+ !text?
53
+ end
54
+
55
+ def separator?
56
+ # separates two entities
57
+ # ex: @iekutlu and @mekanio => " and " is a separater
58
+ prev_partial && prev_partial.entity? && next_partial && next_partial.entity?
59
+ end
60
+
61
+ def reply_preventer?
62
+ first? && next_partial && next_partial.mention?
63
+ end
64
+
65
+ def https?
66
+ url? && content.start_with?('https://')
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,13 @@
1
+ module Bluebird
2
+ module Strategies
3
+ class Base
4
+ class << self
5
+
6
+ def run(tweet, config)
7
+ raise NotImplementedError
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1 @@
1
+ require 'bluebird/strategies/squeeze/strategy'
@@ -0,0 +1,29 @@
1
+ module Bluebird
2
+ module Strategies
3
+ module Squeeze
4
+ class Strategy < Bluebird::Strategies::Base
5
+ class << self
6
+
7
+ def run(tweet, config)
8
+ if run?(tweet, config.max_length)
9
+ squeeze(tweet)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def run?(tweet, max)
16
+ tweet.length > max
17
+ end
18
+
19
+ def squeeze(tweet)
20
+ tweet.text_partials.each do |partial|
21
+ partial.content.squeeze!(' ')
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1 @@
1
+ require 'bluebird/strategies/strip/strategy'
@@ -0,0 +1,31 @@
1
+ module Bluebird
2
+ module Strategies
3
+ module Strip
4
+ class Strategy < Bluebird::Strategies::Base
5
+ class << self
6
+
7
+ def run(tweet, config)
8
+ if run?(tweet, config.max_length)
9
+ strip(tweet)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def run?(tweet, max)
16
+ tweet.length > max
17
+ end
18
+
19
+ def strip(tweet)
20
+ first = tweet.partials.first
21
+ last = tweet.partials.last
22
+
23
+ first.content.lstrip! if first.text?
24
+ last.content.rstrip! if last.text?
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ require 'bluebird/strategies/truncate_text/strategy'
2
+ require 'bluebird/strategies/truncate_text/config'
3
+
4
+ Bluebird::Config.register(:truncate_text, Bluebird::Strategies::TruncateText::Config)
@@ -0,0 +1,15 @@
1
+ module Bluebird
2
+ module Strategies
3
+ module TruncateText
4
+ class Config
5
+
6
+ @omission = '...'
7
+
8
+ class << self
9
+ attr_accessor :omission
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,147 @@
1
+ module Bluebird
2
+ module Strategies
3
+ module TruncateText
4
+ class Strategy < Bluebird::Strategies::Base
5
+ class << self
6
+
7
+ def run(tweet, config)
8
+ if run?(tweet, config.max_length)
9
+ truncate(tweet, config.max_length)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def run?(tweet, max)
16
+ tweet.length > max
17
+ end
18
+
19
+ def truncate(tweet, max)
20
+ count = count_to_truncate(tweet, max)
21
+
22
+ if needs_truncation?(count)
23
+ if (suitable = suitable_partial(tweet, count))
24
+ truncate_partial(suitable, count)
25
+ add_omission(suitable)
26
+ else
27
+ truncate_partials(tweet, count)
28
+ end
29
+ end
30
+ end
31
+
32
+ def needs_truncation?(count)
33
+ count > 0
34
+ end
35
+
36
+ def truncate_partials(tweet, count)
37
+ truncatable_partials(tweet).each do |partial|
38
+ count -= truncate_partial(partial, count)
39
+ unless needs_truncation?(count)
40
+ add_omission(partial)
41
+ break
42
+ end
43
+ end
44
+ end
45
+
46
+ def truncate_partial(partial, count)
47
+ truncated_count = 0
48
+
49
+ if handle_truncation?(partial, count)
50
+
51
+ partial.content = if keep_last_space?(partial)
52
+ partial.content[0, partial.length - count - 1] + ' '
53
+ else
54
+ partial.content[0, partial.length - count]
55
+ end
56
+
57
+ truncated_count = count
58
+ else
59
+ if deletable?(partial)
60
+ truncated_count = partial.length
61
+ partial.content = ''
62
+ else
63
+ truncated_count = partial.length - 1
64
+ partial.content = partial.reply_preventer? ? '.' : ' '
65
+ end
66
+ end
67
+
68
+ truncated_count
69
+ end
70
+
71
+ def truncatable_partials(tweet)
72
+ tweet.text_partials.reverse
73
+ end
74
+
75
+ def suitable_partial(tweet, count)
76
+ truncatable_partials(tweet).each do |partial|
77
+ return partial if handle_truncation?(partial, count)
78
+ end
79
+ nil
80
+ end
81
+
82
+ def count_to_truncate(tweet, max)
83
+ tweet.length > max ? tweet.length - max : 0
84
+ end
85
+
86
+ def deletable?(partial)
87
+ !partial.separator? && !partial.reply_preventer?
88
+ end
89
+
90
+ def handle_truncation?(partial, count)
91
+ truncatable_length(partial) > count
92
+ end
93
+
94
+ def add_omission(partial)
95
+ if add_omission?
96
+ length = omission.char_length
97
+ if handle_truncation?(partial, length)
98
+ truncate_partial(partial, length)
99
+ if ends_with_space?(partial)
100
+ delete_last_character(partial)
101
+ partial.content += omission + ' '
102
+ else
103
+ partial.content += omission
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ def add_omission?
110
+ omission && omission.char_length > 0
111
+ end
112
+
113
+ def truncatable_length(partial)
114
+ if partial.last?
115
+ partial.length
116
+ elsif partial.first?
117
+ if partial.reply_preventer?
118
+ partial.length - 1
119
+ else
120
+ partial.length
121
+ end
122
+ else
123
+ partial.length - 1
124
+ end
125
+ end
126
+
127
+ def ends_with_space?(partial)
128
+ partial.content.to_char_a.last.eql?(' ')
129
+ end
130
+
131
+ def keep_last_space?(partial)
132
+ ends_with_space?(partial) && partial.next_partial
133
+ end
134
+
135
+ def delete_last_character(partial)
136
+ partial.content = partial.content[0, partial.content.length - 1]
137
+ end
138
+
139
+ def omission
140
+ Bluebird::Strategies::TruncateText::Config.omission
141
+ end
142
+
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end