keenser 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/bin/json +12 -0
- data/bin/keenser +83 -0
- data/keenser.gemspec +28 -0
- data/lib/keenser.rb +22 -0
- data/lib/keenser/db.rb +46 -0
- data/lib/keenser/error.rb +5 -0
- data/lib/keenser/model.rb +29 -0
- data/lib/keenser/model/computer.rb +24 -0
- data/lib/keenser/model/song.rb +33 -0
- data/lib/keenser/model/song_tag.rb +8 -0
- data/lib/keenser/model/tag.rb +33 -0
- data/lib/keenser/music.rb +89 -0
- data/lib/keenser/version.rb +3 -0
- metadata +148 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bb15d9e7f2a403745d55e4d20e3b9d7b26f76f2b
|
4
|
+
data.tar.gz: e2be51b6d7835e9f291d1085b3ef763211b109ed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ad210281aa3d2cfb6e4c3b3c002448d9964039a3b694b088b8f933c4b52482d266f08008da4ee86c9e16d7a4afa45946fdeab2bc1ef8e7a4f5be9f8f844f6e4b
|
7
|
+
data.tar.gz: e9c90a3868bddb46c568846a0879d07c9aece700954ef6a2d480ea09b9310a9bbdc95cf322ff77479f20709179e70b1736a92c7b45e71e752d3c7700f4b19676
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 James Dabbs
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Keenser
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'keenser'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install keenser
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( http://github.com/<my-github-username>/keenser/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/json
ADDED
data/bin/keenser
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
%w{
|
4
|
+
colorize json pry thor
|
5
|
+
}.each { |m| require m }
|
6
|
+
|
7
|
+
require_relative "../lib/keenser"
|
8
|
+
|
9
|
+
# TODO: make asks readline friendly
|
10
|
+
class Default < Thor
|
11
|
+
desc "setup", "Set up the database"
|
12
|
+
def setup
|
13
|
+
unless ENV["SYNC"]
|
14
|
+
ENV["SYNC"] = ask "Path to sync directory >"
|
15
|
+
puts %!You should probably add `export SYNC="#{ENV['SYNC']}"` to your .*rc file!.yellow
|
16
|
+
puts
|
17
|
+
end
|
18
|
+
|
19
|
+
Keenser::DB.setup!
|
20
|
+
|
21
|
+
# FIXME: need to do something to reload the connection / model after the
|
22
|
+
# db is setup for the first time
|
23
|
+
unless Keenser::Computer.local
|
24
|
+
puts arr "Configuring local machine"
|
25
|
+
|
26
|
+
role = askd "Role", :client
|
27
|
+
music = askd "Music directory", "#{ENV['SYNC']}/Music"
|
28
|
+
playlists = askd "Playlist directory", "#{ENV['HOME']}/Playlists"
|
29
|
+
|
30
|
+
Keenser::Computer.create({
|
31
|
+
name: `hostname`,
|
32
|
+
role: role,
|
33
|
+
music: music,
|
34
|
+
playlists: playlists
|
35
|
+
})
|
36
|
+
|
37
|
+
puts "Saved local config"
|
38
|
+
puts
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "You're all set!"
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "console", "Attach to a database console"
|
45
|
+
def console
|
46
|
+
Keenser.pry
|
47
|
+
end
|
48
|
+
|
49
|
+
# TODO: look into subcommands
|
50
|
+
desc "play", "Play music"
|
51
|
+
def play *args
|
52
|
+
cmd = args.shift || "pause"
|
53
|
+
Keenser::Music.send cmd, *args
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "status", "Show current dependency status"
|
57
|
+
def status
|
58
|
+
[Keenser::DB, Keenser::Music].each do |m|
|
59
|
+
name = m.name.sub /^Keenser::/, ''
|
60
|
+
status, msg = begin
|
61
|
+
['✓'.green, m.status]
|
62
|
+
rescue => e
|
63
|
+
['✗'.red, e.to_s]
|
64
|
+
end
|
65
|
+
puts "#{status} #{name.underline}\t #{msg}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
no_commands do
|
70
|
+
|
71
|
+
def arr str
|
72
|
+
"#{'==>'.light_blue} #{str}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def askd field, default
|
76
|
+
val = ask("#{field} (#{default}) >").strip
|
77
|
+
val.empty? ? default : val
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Default.start
|
data/keenser.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'keenser/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "keenser"
|
8
|
+
spec.version = Keenser::VERSION
|
9
|
+
spec.authors = ["James Dabbs"]
|
10
|
+
spec.email = ["jamesdabbs@gmail.com"]
|
11
|
+
spec.summary = %q{A wee sidekick}
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = ""
|
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_dependency "colorize", "~> 0.6"
|
22
|
+
spec.add_dependency "pry", "~> 0.9"
|
23
|
+
spec.add_dependency "sequel", "~> 4.6"
|
24
|
+
spec.add_dependency "thor", "~> 0.18"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.1"
|
28
|
+
end
|
data/lib/keenser.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "sequel"
|
2
|
+
|
3
|
+
%w{
|
4
|
+
version error db music
|
5
|
+
}.each { |m| require_relative "../lib/keenser/#{m}" }
|
6
|
+
|
7
|
+
module Keenser
|
8
|
+
def self.src path
|
9
|
+
File.expand_path "../keenser/#{path}", __FILE__
|
10
|
+
end
|
11
|
+
|
12
|
+
# The Sequel library assumes you have DB connection live before you can
|
13
|
+
# do things like subclass models. Models are lazy loaded so that the rest
|
14
|
+
# of the app can still be functional.
|
15
|
+
autoload :Model, src("model")
|
16
|
+
{
|
17
|
+
:Computer => "computer",
|
18
|
+
:Song => "song",
|
19
|
+
:Tag => "tag",
|
20
|
+
:SongTag => "song_tag"
|
21
|
+
}.each { |name,m| autoload name, src("model/#{m}") }
|
22
|
+
end
|
data/lib/keenser/db.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Keenser
|
2
|
+
module DB
|
3
|
+
def self.connection
|
4
|
+
@connection ||= begin
|
5
|
+
connect!
|
6
|
+
rescue SyncError
|
7
|
+
nil
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.connect!
|
12
|
+
sync = ENV['SYNC'] || raise(Error.new "Please specify SYNC directory")
|
13
|
+
|
14
|
+
unless sync
|
15
|
+
raise SyncError.new "You must specify a SYNC directory"
|
16
|
+
end
|
17
|
+
|
18
|
+
unless Dir.exists? sync
|
19
|
+
raise SyncError.new "Could not find sync directory `#{sync}`"
|
20
|
+
end
|
21
|
+
|
22
|
+
Sequel.connect "sqlite://#{URI.escape sync}/keenser.sqlite"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.setup!
|
26
|
+
Model.without_tables.each do |m|
|
27
|
+
connection.create_table m.table_name do
|
28
|
+
primary_key :id
|
29
|
+
instance_exec &m.schema
|
30
|
+
end
|
31
|
+
|
32
|
+
m.db = connect!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.status
|
37
|
+
connect!
|
38
|
+
|
39
|
+
ms = Model.without_tables.map { |m| m.table_name }
|
40
|
+
raise Error.new "Expected tables not found: #{ms.join ', '}" unless ms.empty?
|
41
|
+
raise Error.new "Local computer is not configured" unless Computer.local
|
42
|
+
|
43
|
+
"Ready to go"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Keenser
|
2
|
+
# Sequel requires a db connection before subclassing models
|
3
|
+
DB.connect!
|
4
|
+
|
5
|
+
# Direct subclassing would assume a concrete data set. See
|
6
|
+
# http://comments.gmane.org/gmane.comp.lang.ruby.sequel/6205
|
7
|
+
Model = Class.new Sequel::Model
|
8
|
+
class Model
|
9
|
+
def self.each
|
10
|
+
ObjectSpace.each_object(Class).select { |k| k < Keenser::Model }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.without_tables
|
14
|
+
each.reject { |m| DB.connection.table_exists? m.table_name }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.schema &block
|
18
|
+
block ? @schema = block : @schema
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_or_create attrs
|
22
|
+
where(attrs).limit(1).first || create(attrs)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# The collection methods above explore object space, so we need to be sure
|
27
|
+
# that all models are loaded.
|
28
|
+
Dir[File.expand_path '../model/**/*.rb', __FILE__].each { |m| require m }
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Keenser
|
2
|
+
class Computer < Model
|
3
|
+
Roles = [:server, :client]
|
4
|
+
|
5
|
+
schema do
|
6
|
+
String :name
|
7
|
+
String :role
|
8
|
+
String :music
|
9
|
+
String :playlists
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate
|
13
|
+
super
|
14
|
+
errors.add :name, "can't be empty" if name.empty?
|
15
|
+
errors.add :role, "must be one of: #{Roles.join ', '}" unless Roles.include? role.to_sym
|
16
|
+
errors.add :music, "directory does not exist" unless Dir.exists? music
|
17
|
+
errors.add :playlists, "directory does not exist" unless Dir.exists? playlists
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.local
|
21
|
+
@local ||= where(name: `hostname`).first
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Keenser
|
2
|
+
class Song < Model
|
3
|
+
attr_accessor :status
|
4
|
+
|
5
|
+
schema do
|
6
|
+
String :artist
|
7
|
+
String :title
|
8
|
+
String :path
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
pre = status == "playing" ? "" : "[#{status}] "
|
13
|
+
disp = "#{pre}#{artist} - #{title}"
|
14
|
+
if disp.length > 40
|
15
|
+
disp.slice(0,39) + "…"
|
16
|
+
else
|
17
|
+
disp.ljust 40
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def local_path
|
22
|
+
"#{Computer.local.music}/#{path}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def tag name
|
26
|
+
# Make sure we're not creating a duplicate song record
|
27
|
+
s = Song.where(path: path).first || (save; self)
|
28
|
+
t = Tag.find_or_create name: name
|
29
|
+
SongTag.find_or_create song_id: s.id, tag_id: t.id
|
30
|
+
t.export!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Keenser
|
2
|
+
class Tag < Model
|
3
|
+
schema do
|
4
|
+
String :name
|
5
|
+
end
|
6
|
+
|
7
|
+
def validate
|
8
|
+
super
|
9
|
+
errors.add :name, "can't be empty" if name.empty?
|
10
|
+
end
|
11
|
+
|
12
|
+
def songs
|
13
|
+
Song.join(:song_tags, song_id: :id).where(tag_id: id)
|
14
|
+
end
|
15
|
+
|
16
|
+
def playlist
|
17
|
+
"#{Computer.local.playlists}/#{name}.m3u"
|
18
|
+
end
|
19
|
+
|
20
|
+
def export!
|
21
|
+
File.open playlist, "w" do |f|
|
22
|
+
songs.each { |s| f.puts s.local_path }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def import path
|
27
|
+
File.read(path).split(/[\r\n]/).each do |line|
|
28
|
+
next unless line.include? Computer.local.music
|
29
|
+
# TODO: create song (with artist and title), add song tag
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Keenser
|
2
|
+
module Music
|
3
|
+
extend self
|
4
|
+
|
5
|
+
SESSION_NAME = "music"
|
6
|
+
|
7
|
+
def status
|
8
|
+
expect "tmux"
|
9
|
+
expect "cmus-remote"
|
10
|
+
|
11
|
+
if `cmus-remote -Q 2>/dev/null` =~ /status (.*)/
|
12
|
+
$1.capitalize
|
13
|
+
else
|
14
|
+
"Cmus is not running"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
%w{ prev pause next }.each do |cmd|
|
19
|
+
define_method(cmd) { remote cmd; puts playing.to_s }
|
20
|
+
end
|
21
|
+
|
22
|
+
def tag name
|
23
|
+
playing.tag name
|
24
|
+
puts "#{name} ← #{playing}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def list name
|
28
|
+
t = Tag.where(name: name).first
|
29
|
+
raise Error.new "Cannot find tag `#{t}`" unless t
|
30
|
+
`echo "load -p #{t.playlist}\nset play_library=false\nplayer-next" | cmus-remote`
|
31
|
+
puts "`#{name}` now playing"
|
32
|
+
end
|
33
|
+
|
34
|
+
def import name, path
|
35
|
+
n = 0
|
36
|
+
t = Tag.find_or_create name: name
|
37
|
+
File.read(path).split(/[\r\n]/).each do |line|
|
38
|
+
next unless line.include? Computer.local.music
|
39
|
+
# TODO: parse artist and title?
|
40
|
+
s = Song.find_or_create path: line.sub(/^#{Computer.local.music}\/?/, '')
|
41
|
+
SongTag.find_or_create song_id: s.id, tag_id: t.id
|
42
|
+
n += 1
|
43
|
+
end
|
44
|
+
t.export!
|
45
|
+
puts "Imported #{n} songs"
|
46
|
+
end
|
47
|
+
|
48
|
+
def all
|
49
|
+
`echo "set play_library=true\nplayer-next" | cmus-remote`
|
50
|
+
puts playing.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def playing
|
56
|
+
s = Song.new
|
57
|
+
`cmus-remote -Q`.lines.each do |l|
|
58
|
+
if l =~ /status (\w+)/
|
59
|
+
s.status = $1
|
60
|
+
elsif l =~ /file (.*)/
|
61
|
+
s.path = $1.sub /^#{Computer.local.music}\/?/, ''
|
62
|
+
elsif l =~ /tag artist (.*)/
|
63
|
+
s.artist = $1
|
64
|
+
elsif l =~ /tag title (.*)/
|
65
|
+
s.title = $1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
s
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def test cmd
|
73
|
+
system "#{cmd} >/dev/null 2>&1"
|
74
|
+
end
|
75
|
+
|
76
|
+
def expect bin
|
77
|
+
raise Error.new "`#{bin}` is not present" unless test "which #{bin}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def remote cmd
|
81
|
+
unless test "tmux list-windows -t '#{SESSION_NAME}'"
|
82
|
+
# tmux might complain about starting nested sessions without the TMUX=
|
83
|
+
`TMUX= tmux new-session -s '#{SESSION_NAME}' -d 'cmus'`
|
84
|
+
end
|
85
|
+
|
86
|
+
`cmus-remote --#{cmd}`
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: keenser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Dabbs
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.9'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sequel
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.6'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: thor
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.18'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.18'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.5'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.5'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10.1'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.1'
|
97
|
+
description: A wee sidekick
|
98
|
+
email:
|
99
|
+
- jamesdabbs@gmail.com
|
100
|
+
executables:
|
101
|
+
- json
|
102
|
+
- keenser
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE.txt
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/json
|
112
|
+
- bin/keenser
|
113
|
+
- keenser.gemspec
|
114
|
+
- lib/keenser.rb
|
115
|
+
- lib/keenser/db.rb
|
116
|
+
- lib/keenser/error.rb
|
117
|
+
- lib/keenser/model.rb
|
118
|
+
- lib/keenser/model/computer.rb
|
119
|
+
- lib/keenser/model/song.rb
|
120
|
+
- lib/keenser/model/song_tag.rb
|
121
|
+
- lib/keenser/model/tag.rb
|
122
|
+
- lib/keenser/music.rb
|
123
|
+
- lib/keenser/version.rb
|
124
|
+
homepage: ''
|
125
|
+
licenses:
|
126
|
+
- MIT
|
127
|
+
metadata: {}
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project:
|
144
|
+
rubygems_version: 2.2.0
|
145
|
+
signing_key:
|
146
|
+
specification_version: 4
|
147
|
+
summary: A wee sidekick
|
148
|
+
test_files: []
|