cline 0.0.1 → 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.
data/.gitignore CHANGED
@@ -2,3 +2,6 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ coverage
6
+ spec/tmp/*
7
+ tmp/*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=Fuubar
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Cline - CLI Line Notifier
2
+
3
+ ## Installation
4
+
5
+ ~~~~
6
+ gem install cline
7
+ cline init
8
+ echo "Cline.out_stream = Cline::OutStreams::WithGrowl.new($stdout)" > ~/.cline/config # growlnotify required
9
+ echo "Cline.fetchers << Cline::Fetchers::Feed" >> ~/.cline/config
10
+ curl http://foo.examle.com/url/to/opml.xml > ~/.cline/feeds.xml
11
+
12
+ cline fetch
13
+ cline tick --offset 0 --interval 5
14
+ ~~~~
15
+
16
+ ## Use case
17
+
18
+ in ~/.screenrc
19
+
20
+ ~~~~
21
+ backtick 0 0 0 cline tick 0 60
22
+ ~~~~
23
+
24
+ ## initialize Database
25
+
26
+ `init`command initialize new sqlite3 database.
27
+
28
+ ~~~~
29
+ cline init
30
+ ~~~~
31
+
32
+ ## Reload
33
+
34
+ `fetch`command fetch new notifications from `Cline.fetchers`.
35
+
36
+ ~~~~
37
+ cline fetch
38
+ ~~~~
39
+
40
+ ### Custom Fetcher
41
+
42
+ *fetcher* required `fetch` method.
43
+
44
+ example:
45
+
46
+ ~~~~ruby
47
+ class MyFetcher
48
+ def self.fetch
49
+ new.sources.each do |source|
50
+ Cline::Notification.find_by_message(source.body) || Cline::Notification.create!(message: source.body, time: source.created_at)
51
+ end
52
+ end
53
+
54
+ def sources
55
+ # get new sources...
56
+ end
57
+ end
58
+ ~~~~
59
+
60
+ ### Registration
61
+
62
+ in ~/.cline/config
63
+
64
+ ~~~~ruby
65
+ require 'path/to/my_fetcher'
66
+ Cline.fetchers << MyFetcher
67
+ ~~~~
68
+
69
+ ## Notifier
70
+
71
+ `show` and `tick` command uses Cline's notifier.
72
+ Default notifier is STDOUT.
73
+
74
+ ### Custom Notifyer
75
+
76
+ Cline's notifier required `puts` instance method.
77
+
78
+ example:
79
+
80
+ ~~~~ruby
81
+ class MyNotifier
82
+ def puts(str)
83
+ # implement notifier behaviour...
84
+ end
85
+ end
86
+ ~~~~
87
+
88
+ ### Registration
89
+
90
+ in ~/.cline/config
91
+
92
+ ~~~~ruby
93
+ require 'path/to/my_notifier'
94
+ Cline.out_stream = MyNotifier
95
+ ~~~~
96
+
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
7
+
data/bin/cline ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require_relative '../lib/cline'
5
+
6
+ begin
7
+ Cline::Command.start
8
+ rescue Exception
9
+ Pathname.new("#{Cline.cline_dir}/log").open('a') do |f|
10
+ f.puts '---'
11
+ f.puts Time.now
12
+ f.puts $!.class
13
+ f.puts $!.message
14
+ f.puts $!.backtrace.join("\n")
15
+ end
16
+
17
+ raise $!
18
+ end
data/cline.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
8
8
  s.authors = ["hibariya"]
9
9
  s.email = ["celluloid.key@gmail.com"]
10
10
  s.homepage = "https://github.com/hibariya/cline"
11
- s.summary = %q{CLI local news}
12
- s.description = %q{TBA}
11
+ s.summary = %q{CLI Line Notifier}
12
+ s.description = %q{Cline - CLI Line Notifier}
13
13
 
14
14
  #s.rubyforge_project = "cline"
15
15
 
@@ -21,4 +21,19 @@ Gem::Specification.new do |s|
21
21
  # specify any dependencies here; for example:
22
22
  # s.add_development_dependency "rspec"
23
23
  # s.add_runtime_dependency "rest-client"
24
+
25
+ s.add_runtime_dependency 'thor', ['>= 0.14.6']
26
+ s.add_runtime_dependency 'activerecord', ['>= 3.1.1']
27
+ s.add_runtime_dependency 'sqlite3', ['>= 1.3.4']
28
+ s.add_runtime_dependency 'feedzirra', ['~> 0.0.31'] # FIXME builder dependency workaround...
29
+
30
+ s.add_development_dependency 'rake', ['>= 0.9.2']
31
+ s.add_development_dependency 'ir_b', ['>= 1.4.0']
32
+ s.add_development_dependency 'tapp', ['>= 1.1.0']
33
+ s.add_development_dependency 'rspec', ['>= 2.6.0']
34
+ s.add_development_dependency 'rr', ['>= 1.0.4']
35
+ s.add_development_dependency 'fabrication', ['>= 1.2.0']
36
+ s.add_development_dependency 'fuubar', ['>= 0.0.6']
37
+ s.add_development_dependency 'simplecov', ['>= 0.5.3']
38
+ s.add_development_dependency 'activesupport', ['>= 3.1.1']
24
39
  end
@@ -0,0 +1,45 @@
1
+ # coding: utf-8
2
+
3
+ module Cline
4
+ class Command < Thor
5
+ def self.start(*)
6
+ Cline.boot
7
+ super
8
+ end
9
+
10
+ desc 'show', 'show a latest message'
11
+ method_options offset: :integer
12
+ def show(offset = options[:offset] || 0)
13
+ Notification.display offset
14
+ end
15
+
16
+ desc 'tick', 'rotate message'
17
+ method_options offset: :integer, interval: :integer
18
+ def tick(offset = options[:offset] || 0, interval = options[:interval] || 60)
19
+ loop do
20
+ show offset
21
+ sleep interval.to_i
22
+ end
23
+ end
24
+
25
+ desc 'status', 'show status'
26
+ def status
27
+ say "displayed : #{Notification.displayed.count}", :green
28
+ say "total : #{Notification.count}", :cyan
29
+ end
30
+
31
+ desc 'fetch', 'fetch sources'
32
+ def fetch
33
+ Cline.fetchers.each &:fetch
34
+ end
35
+
36
+ desc 'init', 'init database'
37
+ def init
38
+ ActiveRecord::Base.connection.create_table(:notifications) do |t|
39
+ t.text :message, null: false, default: ''
40
+ t.integer :display_count, null: false, default: 0
41
+ t.time :time, null: false
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+
3
+ module Cline::Fetchers
4
+ class Feed
5
+ def self.fetch
6
+ opml = Pathname.new("#{Cline.cline_dir}/feeds.xml")
7
+ entries = new(opml.read).fetch
8
+
9
+ entries.each do |entry|
10
+ message = "#{entry.title} #{entry.url}"
11
+ Cline::Notification.tap {|n| n.find_by_message(message) || n.create(message: message, time: entry.published) }
12
+ end
13
+ end
14
+
15
+ def initialize(opml_str)
16
+ require 'rexml/document'
17
+ require 'feedzirra'
18
+
19
+ @opml = REXML::Document.new(opml_str)
20
+ @feeds = parse_opml(@opml.elements['opml/body'])
21
+ end
22
+
23
+ def fetch
24
+ entries = []
25
+
26
+ @feeds.map { |feed_url|
27
+ Thread.fork do
28
+ begin
29
+ feed = Feedzirra::Feed.fetch_and_parse(feed_url)
30
+ entries += feed.entries if feed.is_a? Feedzirra::FeedUtilities
31
+ rescue
32
+ puts $!.class, $!.message
33
+ end
34
+ end
35
+ }.map(&:join)
36
+
37
+ entries
38
+ end
39
+
40
+ def parse_opml(opml_node)
41
+ feeds = []
42
+
43
+ opml_node.elements.each('outline') do |el|
44
+ unless el.elements.size.zero?
45
+ feeds += parse_opml(el)
46
+ else
47
+ url = el.attributes['xmlUrl']
48
+ feeds << url if url
49
+ end
50
+ end
51
+
52
+ feeds
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+
3
+ module Cline
4
+ class Notification < ActiveRecord::Base
5
+ validate :time, presence: true
6
+ validate :message, presence: true, uniqueness: true
7
+ validate :display_count, presence: true, numerically: true
8
+
9
+ scope :earliest, ->(limit = 1, offset = 0) {
10
+ order('display_count, time').limit(limit).offset(offset)
11
+ }
12
+
13
+ scope :displayed, where('display_count > 0')
14
+
15
+ class << self
16
+ def display(offset)
17
+ earliest(1, offset).first.display
18
+ end
19
+ end
20
+
21
+ def display
22
+ Cline.out_stream.puts display_message
23
+
24
+ increment! :display_count
25
+ end
26
+
27
+ def display_message
28
+ "[#{time}][#{display_count}] #{message}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+
3
+ module Cline::OutStreams
4
+ class WithGrowl
5
+ attr_reader:stream
6
+
7
+ def initialize(stream = $stdout)
8
+ @stream = stream
9
+ end
10
+
11
+ def puts(str)
12
+ puts_stream str
13
+ puts_growlnotify str
14
+ end
15
+
16
+ private
17
+
18
+ def puts_stream(str)
19
+ stream.puts str
20
+ end
21
+
22
+ def puts_growlnotify(str)
23
+ `growlnotify -m '#{str}'`
24
+ end
25
+ end
26
+ end
data/lib/cline/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Cline
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/cline.rb CHANGED
@@ -1,5 +1,61 @@
1
- require "cline/version"
1
+ # coding: utf-8
2
+
3
+ here = File.dirname(__FILE__)
4
+ $LOAD_PATH.unshift here unless $LOAD_PATH.include?(here)
2
5
 
3
6
  module Cline
4
- # Your code goes here...
7
+ class << self
8
+ def cline_dir
9
+ "#{ENV['HOME']}/.cline"
10
+ end
11
+
12
+ def boot
13
+ mkdir_if_needed
14
+ setup_logger
15
+ establish_connection
16
+ load_config_if_exists
17
+ end
18
+
19
+ def mkdir_if_needed
20
+ path = Pathname.new(cline_dir)
21
+ path.mkdir unless path.directory?
22
+ end
23
+
24
+ def setup_logger
25
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
26
+ ActiveRecord::Base.logger.level = Logger::WARN
27
+ end
28
+
29
+ def establish_connection
30
+ ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: "#{cline_dir}/cline.sqlite3", timeout: 10000
31
+ end
32
+
33
+ def load_config_if_exists
34
+ config = Pathname.new("#{cline_dir}/config")
35
+ load config if config.exist?
36
+ end
37
+
38
+ def out_stream
39
+ @out_stream || STDOUT
40
+ end
41
+
42
+ def out_stream=(stream)
43
+ @out_stream = stream
44
+ end
45
+
46
+ fetchers = []
47
+ define_method(:fetchers) { fetchers }
48
+ end
5
49
  end
50
+
51
+ require 'logger'
52
+ require 'pathname'
53
+ require 'thor'
54
+ require 'sqlite3'
55
+ require 'active_record'
56
+
57
+ require "cline/version"
58
+ require "cline/notification"
59
+ require "cline/command"
60
+ require "cline/fetchers"
61
+ require "cline/out_streams"
@@ -0,0 +1,6 @@
1
+ # coding: utf-8
2
+
3
+ Fabricator(:notification, class_name: Cline::Notification) do
4
+ message { sequence(:message) {|i| "message ##{i}" } }
5
+ time { Time.now }
6
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ describe Cline do
6
+ describe '#load_config_if_exists' do
7
+ let(:config) { Pathname.new(Cline.cline_dir).join('config') }
8
+
9
+ before do
10
+ config.open('w') {|f| f.puts "Cline.fetchers << Cline::Fetchers::Feed" }
11
+
12
+ Cline.boot
13
+ end
14
+
15
+ specify "config file should loaded" do
16
+ Cline.fetchers.should include Cline::Fetchers::Feed
17
+ end
18
+
19
+ after do
20
+ config.unlink
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ describe Cline::Notification do
6
+ describe '.ealiest' do
7
+ let!(:notification1) { Fabricate(:notification, time: 2.days.ago.beginning_of_day, display_count: 3) }
8
+ let!(:notification2) { Fabricate(:notification, time: 3.days.ago.beginning_of_day, display_count: 3) }
9
+ let!(:notification3) { Fabricate(:notification, time: 3.days.ago.beginning_of_day, display_count: 2) }
10
+
11
+ context 'default(limit 1 offset 0)' do
12
+ subject { Cline::Notification.earliest.all }
13
+
14
+ it { should == [notification3] }
15
+ end
16
+
17
+ context 'limit 2 offset 1' do
18
+ subject { Cline::Notification.earliest(2, 1).all }
19
+
20
+ it { should == [notification2, notification1] }
21
+ end
22
+ end
23
+
24
+ describe '.displayed' do
25
+ let!(:notification1) { Fabricate(:notification, display_count: 0) }
26
+ let!(:notification2) { Fabricate(:notification, display_count: 1) }
27
+
28
+ subject { Cline::Notification.displayed.all }
29
+
30
+ it { should == [notification2] }
31
+ end
32
+
33
+ describe '#display' do
34
+ let!(:notification) { Fabricate(:notification, display_count: 0) }
35
+
36
+ specify 'display_count should incremented' do
37
+ -> { notification.display }.should change(notification, :display_count).by(1)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+
3
+ require 'tapp'
4
+ require 'simplecov'
5
+ require 'fabrication'
6
+
7
+ SimpleCov.start
8
+
9
+ CLINE_ROOT = Pathname.new(File.dirname(__FILE__) + '/../').realpath
10
+ require_relative '../lib/cline'
11
+
12
+ Dir[File.dirname(__FILE__) + '/support/*'].each {|f| require f }
13
+ require_relative 'fabricators'
14
+
15
+ RSpec.configure do |config|
16
+ config.filter_run focus: true
17
+ config.run_all_when_everything_filtered = true
18
+ config.mock_with :rr
19
+
20
+ config.before(:suite) do
21
+ home = CLINE_ROOT.join('spec', 'tmp')
22
+ ENV['HOME'] = home.to_s
23
+
24
+ cline_dir = Pathname.new(Cline.cline_dir)
25
+ cline_dir.rmtree if cline_dir.directory?
26
+
27
+ Cline.boot
28
+ Cline::Command.new.invoke :init
29
+ end
30
+
31
+ config.before(:each) do
32
+ Cline.boot
33
+ Cline.out_stream = StringIO.new
34
+ Cline::Notification.delete_all
35
+ end
36
+ end
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,21 +9,177 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-22 00:00:00.000000000 Z
13
- dependencies: []
14
- description: TBA
12
+ date: 2011-11-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: &70240666988320 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.14.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70240666988320
25
+ - !ruby/object:Gem::Dependency
26
+ name: activerecord
27
+ requirement: &70240666984720 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 3.1.1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70240666984720
36
+ - !ruby/object:Gem::Dependency
37
+ name: sqlite3
38
+ requirement: &70240666983100 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 1.3.4
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70240666983100
47
+ - !ruby/object:Gem::Dependency
48
+ name: feedzirra
49
+ requirement: &70240666980960 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.31
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70240666980960
58
+ - !ruby/object:Gem::Dependency
59
+ name: rake
60
+ requirement: &70240666979860 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: 0.9.2
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70240666979860
69
+ - !ruby/object:Gem::Dependency
70
+ name: ir_b
71
+ requirement: &70240666978660 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: 1.4.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70240666978660
80
+ - !ruby/object:Gem::Dependency
81
+ name: tapp
82
+ requirement: &70240666977600 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: 1.1.0
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70240666977600
91
+ - !ruby/object:Gem::Dependency
92
+ name: rspec
93
+ requirement: &70240666976460 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: 2.6.0
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *70240666976460
102
+ - !ruby/object:Gem::Dependency
103
+ name: rr
104
+ requirement: &70240666974980 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 1.0.4
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *70240666974980
113
+ - !ruby/object:Gem::Dependency
114
+ name: fabrication
115
+ requirement: &70240666973900 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: 1.2.0
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: *70240666973900
124
+ - !ruby/object:Gem::Dependency
125
+ name: fuubar
126
+ requirement: &70240666973120 !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: 0.0.6
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: *70240666973120
135
+ - !ruby/object:Gem::Dependency
136
+ name: simplecov
137
+ requirement: &70240666971520 !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: 0.5.3
143
+ type: :development
144
+ prerelease: false
145
+ version_requirements: *70240666971520
146
+ - !ruby/object:Gem::Dependency
147
+ name: activesupport
148
+ requirement: &70240666970180 !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ! '>='
152
+ - !ruby/object:Gem::Version
153
+ version: 3.1.1
154
+ type: :development
155
+ prerelease: false
156
+ version_requirements: *70240666970180
157
+ description: Cline - CLI Line Notifier
15
158
  email:
16
159
  - celluloid.key@gmail.com
17
- executables: []
160
+ executables:
161
+ - cline
18
162
  extensions: []
19
163
  extra_rdoc_files: []
20
164
  files:
21
165
  - .gitignore
166
+ - .rspec
22
167
  - Gemfile
168
+ - README.md
23
169
  - Rakefile
170
+ - bin/cline
24
171
  - cline.gemspec
25
172
  - lib/cline.rb
173
+ - lib/cline/command.rb
174
+ - lib/cline/fetchers.rb
175
+ - lib/cline/notification.rb
176
+ - lib/cline/out_streams.rb
26
177
  - lib/cline/version.rb
178
+ - spec/fabricators.rb
179
+ - spec/lib/cline_spec.rb
180
+ - spec/lib/notification_spec.rb
181
+ - spec/spec_helper.rb
182
+ - spec/support/.gitkeep
27
183
  homepage: https://github.com/hibariya/cline
28
184
  licenses: []
29
185
  post_install_message:
@@ -44,8 +200,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
44
200
  version: '0'
45
201
  requirements: []
46
202
  rubyforge_project:
47
- rubygems_version: 1.8.10
203
+ rubygems_version: 1.8.11
48
204
  signing_key:
49
205
  specification_version: 3
50
- summary: CLI local news
51
- test_files: []
206
+ summary: CLI Line Notifier
207
+ test_files:
208
+ - spec/fabricators.rb
209
+ - spec/lib/cline_spec.rb
210
+ - spec/lib/notification_spec.rb
211
+ - spec/spec_helper.rb
212
+ - spec/support/.gitkeep