boom 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,81 +0,0 @@
1
- # coding: utf-8
2
-
3
- # Storage is the middleman between changes the client makes in-memory and how
4
- # it's actually persisted to disk (and vice-versa). There are also a few
5
- # convenience methods to run searches and operations on the in-memory hash.
6
- #
7
- module Boom
8
- module Storage
9
- class Base
10
-
11
- # Public: initializes a Storage instance by loading in your persisted data from adapter.
12
- #
13
- # Returns the Storage instance.
14
- def initialize
15
- @lists = []
16
- bootstrap
17
- populate
18
- end
19
-
20
- # run bootstrap tasks for the storage
21
- def bootstrap ; end
22
-
23
- # populate the in-memory store with all the lists and items
24
- def populate ; end
25
-
26
- # save the data
27
- def save ; end
28
-
29
- # Public: the in-memory collection of all Lists attached to this Storage
30
- # instance.
31
- #
32
- # lists - an Array of individual List items
33
- #
34
- # Returns nothing.
35
- attr_writer :lists
36
-
37
- # Public: the list of Lists in your JSON data, sorted by number of items
38
- # descending.
39
- #
40
- # Returns an Array of List objects.
41
- def lists
42
- @lists.sort_by { |list| -list.items.size }
43
- end
44
-
45
- # Public: tests whether a named List exists.
46
- #
47
- # name - the String name of a List
48
- #
49
- # Returns true if found, false if not.
50
- def list_exists?(name)
51
- @lists.detect { |list| list.name == name }
52
- end
53
-
54
- # Public: all Items in storage.
55
- #
56
- # Returns an Array of all Items.
57
- def items
58
- @lists.collect(&:items).flatten
59
- end
60
-
61
- # Public: tests whether a named Item exists.
62
- #
63
- # name - the String name of an Item
64
- #
65
- # Returns true if found, false if not.
66
- def item_exists?(name)
67
- items.detect { |item| item.name == name }
68
- end
69
-
70
- # Public: creates a Hash of the representation of the in-memory data
71
- # structure. This percolates down to Items by calling to_hash on the List,
72
- # which in tern calls to_hash on individual Items.
73
- #
74
- # Returns a Hash of the entire data set.
75
- def to_hash
76
- { :lists => lists.collect(&:to_hash) }
77
- end
78
-
79
- end
80
- end
81
- end
@@ -1,114 +0,0 @@
1
- # coding: utf-8
2
- #
3
- # Gist backend for Boom.
4
- #
5
- # Your .boom.conf file should look like this:
6
- #
7
- # {
8
- # "backend": "gist",
9
- # "gist": {
10
- # "username": "your_github_username",
11
- # "password": "your_github_password"
12
- # }
13
- # }
14
- #
15
- # There are two optional keys which can be under "gist":
16
- #
17
- # gist_id - The ID of an existing Gist to use. If not
18
- # present, a Gist will be created the first time
19
- # Boom is run and will be persisted to the config.
20
- # public - Makes the Gist public. An absent value or
21
- # any value other than boolean true will make
22
- # the Gist private.
23
- #
24
-
25
- module Boom
26
- module Storage
27
- class Gist < Base
28
-
29
- def bootstrap
30
- begin
31
- require "httparty"
32
-
33
- self.class.send(:include, HTTParty)
34
- self.class.base_uri "https://api.github.com"
35
- rescue LoadError
36
- puts "The Gist backend requires HTTParty: gem install httparty"
37
- exit
38
- end
39
-
40
- unless Boom.config.attributes["gist"]
41
- puts 'A "gist" data structure must be defined in ~/.boom.conf'
42
- exit
43
- end
44
-
45
- set_up_auth
46
- find_or_create_gist
47
- end
48
-
49
- def populate
50
- @storage['lists'].each do |lists|
51
- lists.each do |list_name, items|
52
- @lists << list = List.new(list_name)
53
-
54
- items.each do |item|
55
- item.each do |name,value|
56
- list.add_item(Item.new(name,value))
57
- end
58
- end
59
- end
60
- end
61
- end
62
-
63
- def save
64
- self.class.post("/gists/#{@gist_id}", request_params)
65
- end
66
-
67
- private
68
-
69
- def set_up_auth
70
- username, password = Boom.config.attributes["gist"]["username"], Boom.config.attributes["gist"]["password"]
71
-
72
- if username and password
73
- self.class.basic_auth(username, password)
74
- else
75
- puts "GitHub username and password must be defined in ~/.boom.conf"
76
- exit
77
- end
78
- end
79
-
80
- def find_or_create_gist
81
- @gist_id = Boom.config.attributes["gist"]["gist_id"]
82
- @public = Boom.config.attributes["gist"]["public"] == true
83
-
84
- if @gist_id.nil? or @gist_id.empty?
85
- response = self.class.post("/gists", request_params)
86
- else
87
- response = self.class.get("/gists/#{@gist_id}", request_params)
88
- end
89
-
90
- @storage = MultiJson.decode(response["files"]["boom.json"]["content"]) if response["files"] and response["files"]["boom.json"]
91
-
92
- unless @storage
93
- puts "Boom data could not be obtained"
94
- exit
95
- end
96
-
97
- unless @gist_id
98
- Boom.config.attributes["gist"]["gist_id"] = @gist_id = response["id"]
99
- Boom.config.save
100
- end
101
- end
102
-
103
- def request_params
104
- {
105
- :body => MultiJson.encode({
106
- :description => "boom!",
107
- :public => @public,
108
- :files => { "boom.json" => { :content => MultiJson.encode(to_hash) } }
109
- })
110
- }
111
- end
112
- end
113
- end
114
- end
@@ -1,69 +0,0 @@
1
- # coding: utf-8
2
- #
3
- # Json is the default storage option for boom. It writes a Json file to
4
- # ~/.boom. Pretty neat, huh?
5
- #
6
- module Boom
7
- module Storage
8
- class Json < Base
9
-
10
- JSON_FILE = "#{ENV['HOME']}/.boom"
11
-
12
- # Public: the path to the Json file used by boom.
13
- #
14
- # Returns the String path of boom's Json representation.
15
- def json_file
16
- JSON_FILE
17
- end
18
-
19
- # Takes care of bootstrapping the Json file, both in terms of creating the
20
- # file and in terms of creating a skeleton Json schema.
21
- #
22
- # Return true if successfully saved.
23
- def bootstrap
24
- return if File.exist?(json_file)
25
- FileUtils.touch json_file
26
- File.open(json_file, 'w') {|f| f.write(to_json) }
27
- save
28
- end
29
-
30
- # Take a Json representation of data and explode it out into the consituent
31
- # Lists and Items for the given Storage instance.
32
- #
33
- # Returns nothing.
34
- def populate
35
- storage = MultiJson.decode(File.new(json_file, 'r').read)
36
-
37
- storage['lists'].each do |lists|
38
- lists.each do |list_name, items|
39
- @lists << list = List.new(list_name)
40
-
41
- items.each do |item|
42
- item.each do |name,value|
43
- list.add_item(Item.new(name,value))
44
- end
45
- end
46
- end
47
- end
48
- end
49
-
50
- # Public: persists your in-memory objects to disk in Json format.
51
- #
52
- # lists_Json - list in Json format
53
- #
54
- # Returns true if successful, false if unsuccessful.
55
- def save
56
- File.open(json_file, 'w') {|f| f.write(to_json) }
57
- end
58
-
59
- # Public: the Json representation of the current List and Item assortment
60
- # attached to the Storage instance.
61
- #
62
- # Returns a String Json representation of its Lists and their Items.
63
- def to_json
64
- MultiJson.encode(to_hash)
65
- end
66
-
67
- end
68
- end
69
- end
@@ -1,135 +0,0 @@
1
- # coding: utf-8
2
-
3
- # Keychain provides methods for using Mac OS X's Keychain as a storage option.
4
- # It saves lists as Keychain files in ~/Library/Keychains with the filename
5
- # format being: "Boom.list.mylist.keychain"
6
- #
7
- module Boom
8
- module Storage
9
- class Keychain < Base
10
-
11
- KEYCHAIN_FORMAT = %r{Boom\.list\.(.+)\.keychain}
12
-
13
- # Opens Keychain app when json_file is called during `boom edit`
14
- #
15
- # Returns nothing
16
- def open_keychain_app
17
- `open /Applications/Utilities/'Keychain Access.app' &`
18
- end
19
-
20
- alias_method :json_file, :open_keychain_app
21
-
22
- # Boostraps Keychain by checking if you're using a Mac which is a prereq
23
- #
24
- # Returns
25
- def bootstrap
26
- raise RuntimeError unless is_mac?
27
- rescue
28
- puts('No Keychain utility to access, maybe try another storage option?')
29
- exit
30
- end
31
-
32
- # Asks if you're using Mac OS X
33
- #
34
- # Returns true on a Mac
35
- def is_mac?
36
- return Boom::Platform.darwin?
37
- end
38
-
39
- # Populate the in-memory store with all the lists and items from Keychain
40
- #
41
- # Returns Array of keychain names, i.e. ["Boom.list.mylist.keychain"]
42
- def populate
43
- stored_keychain_lists.each do |keychain|
44
- @lists << list = List.new(keychain.scan(KEYCHAIN_FORMAT).flatten.first)
45
- extract_keychain_items(keychain).each do |name|
46
- list.add_item(Item.new(name, extract_keychain_value(name, keychain)))
47
- end
48
- end
49
- end
50
-
51
- # Saves the data from memory to the correct Keychain
52
- #
53
- # Returns nothing
54
- def save
55
- @lists.each do |list|
56
- keychain_name = list_to_filename(list.name)
57
- create_keychain_list(keychain_name) unless stored_keychain_lists.include?(keychain_name)
58
- unless list.items.empty?
59
- list.items.each do |item|
60
- store_item(item, keychain_name)
61
- end
62
- end
63
- delete_unwanted_items(list)
64
- end
65
- delete_unwanted_lists
66
- rescue RuntimeError
67
- puts(e "Couldn't save to your keychain, check Console.app or above for relevant messages")
68
- end
69
-
70
-
71
- private
72
-
73
- # Returns an Array of keychains stored in ~/Library/Keychains:
74
- # => ["Boom.list.mylist.keychain"]
75
- def stored_keychain_lists
76
- @stored_keychain_lists ||= `security -q list-keychains |grep Boom.list` \
77
- .split(/[\/\n\"]/).select {|kc| kc =~ KEYCHAIN_FORMAT}
78
- end
79
-
80
- # Create the keychain list "Boom.list.mylist.keychain" in ~/Library/Keychains
81
- def create_keychain_list(keychain_name)
82
- `security -q create-keychain #{keychain_name}`
83
- end
84
-
85
- # Saves the individual item's value to the right list/keychain
86
- def store_item(item, keychain_name)
87
- `security 2>/dev/null -q add-generic-password -a '#{item.name}' -s '#{item.name}' -w '#{item.value}' #{keychain_name}`
88
- end
89
-
90
- # Retrieves the value of a particular item in a list
91
- def extract_keychain_value(item_name, keychain)
92
- `security 2>&1 >/dev/null find-generic-password -ga '#{item_name}' #{keychain}`.chomp.split('"').last
93
- end
94
-
95
- # Gets all items in a particular list
96
- def extract_keychain_items(keychain_name)
97
- @stored_items ||= {}
98
- @stored_items[keychain_name] ||= `security dump-keychain -a #{keychain_name} |grep acct` \
99
- .split(/\s|\\n|\\"|acct|<blob>=|\"/).reject {|f| f.empty?}
100
- end
101
-
102
- # Converts list name to the corresponding keychain filename format based
103
- # on the KEYCHAIN_FORMAT
104
- def list_to_filename(list_name)
105
- KEYCHAIN_FORMAT.source.gsub(/\(\.\+\)/, list_name).gsub('\\','')
106
- end
107
-
108
- # Delete's a keychain file
109
- def delete_list(keychain_filename)
110
- `security delete-keychain #{keychain_filename}`
111
- end
112
-
113
- # Delete's all keychain files you don't want anymore
114
- def delete_unwanted_lists
115
- (stored_keychain_lists - @lists.map {|list| list_to_filename(list.name)}).each do |filename|
116
- delete_list(filename)
117
- end
118
- end
119
-
120
- # Removes unwanted items in a list
121
- # security util doesn't have a delete password option so we'll have to
122
- # drop it and recreate it with what is in memory
123
- def delete_unwanted_items(list)
124
- filename = list_to_filename(list.name)
125
- if (list.items.size < extract_keychain_items(filename).size)
126
- delete_list(filename)
127
- create_keychain_list(filename)
128
- list.items.each do |item|
129
- store_item(item, filename)
130
- end unless list.items.empty?
131
- end
132
- end
133
- end
134
- end
135
- end
@@ -1,84 +0,0 @@
1
- # coding: utf-8
2
-
3
- # Storage adapter that saves data from boom to MongoDB instead of JSON file.
4
- begin
5
- require 'mongo'
6
- rescue LoadError
7
- end
8
-
9
- module Boom
10
- module Storage
11
- class Mongodb < Base
12
-
13
- # Public: Initialize MongoDB connection and check dep.
14
- #
15
- # Returns Mongo connection
16
- def mongo
17
- @mongo ||= ::Mongo::Connection.new(
18
- Boom.config.attributes["mongodb"]["host"],
19
- Boom.config.attributes["mongodb"]["port"]
20
- ).db(Boom.config.attributes["mongodb"]["database"])
21
-
22
- @mongo.authenticate(
23
- Boom.config.attributes['mongodb']['username'],
24
- Boom.config.attributes['mongodb']['password']
25
- )
26
-
27
- # Return connection
28
- @mongo
29
- rescue NameError => e
30
- puts "You don't have the Mongo gem installed yet:\n gem install mongo"
31
- exit
32
- end
33
-
34
- # Public: The MongoDB collection
35
- #
36
- # Returns the MongoDB collection
37
- def collection
38
- @collection ||= mongo.collection(Boom.config.attributes["mongodb"]["collection"])
39
- end
40
-
41
- # Public: Bootstrap
42
- #
43
- # Returns
44
- def bootstrap
45
- collection.insert("boom" => '{"lists": [{}]}') if collection.find_one.nil?
46
- end
47
-
48
- # Public: Populates the memory list from MongoDB
49
- #
50
- # Returns nothing
51
- def populate
52
- storage = MultiJson.decode(collection.find_one['boom']) || []
53
-
54
- storage['lists'].each do |lists|
55
- lists.each do |list_name, items|
56
- @lists << list = List.new(list_name)
57
-
58
- items.each do |item|
59
- item.each do |name,value|
60
- list.add_item(Item.new(name,value))
61
- end
62
- end
63
- end
64
- end
65
- end
66
-
67
- # Public: Save to MongoDB
68
- #
69
- # Returns Mongo ID
70
- def save
71
- doc = collection.find_one()
72
- collection.update({"_id" => doc["_id"]}, {'boom' => to_json})
73
- end
74
-
75
- # Public: Convert to JSON
76
- #
77
- # Returns
78
- def to_json
79
- MultiJson.encode(to_hash)
80
- end
81
-
82
- end
83
- end
84
- end
@@ -1,72 +0,0 @@
1
- # coding: utf-8
2
- #
3
- # Sup, Redis.
4
- #
5
- begin
6
- require 'digest'
7
- require 'redis'
8
- require 'uri'
9
- rescue LoadError
10
- end
11
-
12
- module Boom
13
- module Storage
14
- class Redis < Base
15
-
16
- def redis
17
- uri = URI.parse(Boom.config.attributes['uri'] || '')
18
-
19
- @redis ||= ::Redis.new :host => (uri.host || Boom.config.attributes["redis"]["host"]),
20
- :port => (uri.port || Boom.config.attributes["redis"]["port"]),
21
- :user => (uri.user || Boom.config.attributes["redis"]["user"]),
22
- :password => (uri.password || Boom.config.attributes["redis"]["password"])
23
- rescue NameError => e
24
- puts "You don't have Redis installed yet:\n gem install redis"
25
- exit
26
- end
27
-
28
- def bootstrap
29
- end
30
-
31
- def populate
32
- lists = redis.smembers("boom:lists") || []
33
-
34
- lists.each do |sha|
35
- list_name = redis.get("boom:lists:#{sha}:name")
36
- @lists << list = List.new(list_name)
37
-
38
- shas = redis.lrange("boom:lists:#{sha}:items",0,-1) || []
39
-
40
- shas.each do |item_sha|
41
- name = redis.get "boom:items:#{item_sha}:name"
42
- value = redis.get "boom:items:#{item_sha}:value"
43
- list.add_item(Item.new(name, value))
44
- end
45
- end
46
- end
47
-
48
- def clear
49
- redis.del "boom:lists"
50
- redis.del "boom:items"
51
- end
52
-
53
- def save
54
- clear
55
-
56
- lists.each do |list|
57
- list_sha = Digest::SHA1.hexdigest(list.name)
58
- redis.set "boom:lists:#{list_sha}:name", list.name
59
- redis.sadd "boom:lists", list_sha
60
-
61
- list.items.each do |item|
62
- item_sha = Digest::SHA1.hexdigest(item.name)
63
- redis.rpush "boom:lists:#{list_sha}:items", item_sha
64
- redis.set "boom:items:#{item_sha}:name", item.name
65
- redis.set "boom:items:#{item_sha}:value", item.value
66
- end
67
- end
68
- end
69
-
70
- end
71
- end
72
- end
@@ -1,3 +0,0 @@
1
- {
2
- backend: 'Json'
3
- }
@@ -1,3 +0,0 @@
1
- {
2
- "backend": "Json"
3
- }
@@ -1 +0,0 @@
1
- {"lists":[{"urls":[{"github":"https://github.com"},{"blog":"http://zachholman.com"}]}]}
data/test/helper.rb DELETED
@@ -1,24 +0,0 @@
1
- # coding: utf-8
2
-
3
- require 'test/unit'
4
-
5
- begin
6
- require 'rubygems'
7
- require 'redgreen'
8
- rescue LoadError
9
- end
10
-
11
- require 'mocha'
12
-
13
- $LOAD_PATH.unshift(File.dirname(__FILE__))
14
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
-
16
- require 'boom'
17
-
18
- def boom_json(name)
19
- root = File.expand_path(File.dirname(__FILE__))
20
- Boom::Storage::Json.any_instance.stubs(:save).returns(true)
21
- Boom::Storage::Json.any_instance.stubs(:json_file).
22
- returns("#{root}/examples/#{name}.json")
23
- Boom.stubs(:storage).returns(Boom::Storage::Json.new)
24
- end
data/test/test_color.rb DELETED
@@ -1,30 +0,0 @@
1
- require 'helper'
2
-
3
- class TestColor < Test::Unit::TestCase
4
-
5
- def test_colorize
6
- assert_equal "\e[35mBoom!\e[0m",
7
- Boom::Color.colorize("Boom!", :magenta)
8
- end
9
-
10
- def test_magenta
11
- assert_equal "\e[35mMagenta!\e[0m",
12
- Boom::Color.magenta("Magenta!")
13
- end
14
-
15
- def test_red
16
- assert_equal "\e[31mRed!\e[0m",
17
- Boom::Color.red("Red!")
18
- end
19
-
20
- def test_yellow
21
- assert_equal "\e[33mYellow!\e[0m",
22
- Boom::Color.yellow("Yellow!")
23
- end
24
-
25
- def test_cyan
26
- assert_equal "\e[36mCyan!\e[0m",
27
- Boom::Color.cyan("Cyan!")
28
- end
29
-
30
- end