bluebird 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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