kaboom 0.3.1

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,116 @@
1
+ # coding: utf-8
2
+
3
+ #
4
+ # Config manages all the config information for boom and its backends. It's a
5
+ # simple JSON Hash that gets persisted to `~/.boom` on-disk. You may access it
6
+ # as a Hash:
7
+ #
8
+ # config.attributes = { :backend => "JSON" }
9
+ # config.attributes[:backend]
10
+ # # => "json"
11
+ #
12
+ # config.attributes[:backend] = "Redis"
13
+ # config.attributes[:backend]
14
+ # # => "redis"
15
+ #
16
+ module Boom
17
+ class Config
18
+
19
+ # The main config file for boom
20
+ FILE = "#{ENV['HOME']}/.boom.conf"
21
+ REMOTE_FILE = FILE.gsub(/\.conf$/, ".remote.conf")
22
+
23
+ #if set to true then we will use a different config file for storage
24
+ #engine
25
+ attr_accessor :remote
26
+
27
+ # Public: The attributes Hash for configuration options. The attributes
28
+ # needed are dictated by each backend, but the `backend` option must be
29
+ # present.
30
+ attr_reader :attributes
31
+
32
+
33
+ # Public: an alias for accessing Boom.config.attributes[key]
34
+ # like Boom.config[key] instead
35
+ #
36
+ # Returns the value in the attributes hash
37
+ def [] key
38
+ attributes[key]
39
+ end
40
+
41
+ # Public: creates a new instance of Config.
42
+ #
43
+ # This will load the attributes from boom's config file, or bootstrap it
44
+ # if this is a new install. Bootstrapping defaults to the JSON backend.
45
+ #
46
+ # Returns nothing.
47
+ def initialize remote=false
48
+ @remote = remote
49
+ bootstrap unless File.exist?(file)
50
+ load_attributes
51
+ end
52
+
53
+ # Public: accessor for the configuration file.
54
+ #
55
+ # Returns the String file path.
56
+ def file
57
+ remote ? REMOTE_FILE : FILE
58
+ end
59
+
60
+ # Public: saves an empty, barebones hash to @attributes for the purpose of
61
+ # new user setup.
62
+ #
63
+ # Returns whether the attributes were saved.
64
+ def bootstrap
65
+ @attributes = {
66
+ :backend => 'json'
67
+ }
68
+ save
69
+ end
70
+
71
+ # Public: assigns a hash to the configuration attributes object. The
72
+ # contents of the attributes hash depends on what the backend needs. A
73
+ # `backend` key MUST be present, however.
74
+ #
75
+ # attrs - the Hash representation of attributes to persist to disk.
76
+ #
77
+ # Examples
78
+ #
79
+ # config.attributes = {"backend" => "json"}
80
+ #
81
+ # Returns whether the attributes were saved.
82
+ def attributes=(attrs)
83
+ @attributes = attrs
84
+ save
85
+ end
86
+
87
+ # Public: loads and parses the JSON tree from disk into memory and stores
88
+ # it in the attributes Hash.
89
+ #
90
+ # Returns nothing.
91
+ def load_attributes
92
+ @attributes = MultiJson.decode(File.new(file, 'r').read)
93
+ end
94
+
95
+ # Public: writes the in-memory JSON Hash to disk.
96
+ #
97
+ # Returns nothing.
98
+ def save
99
+ json = MultiJson.encode(attributes)
100
+ File.open(file, 'w') {|f| f.write(json) }
101
+ end
102
+
103
+ def invalid_message
104
+ %(#{red "Is your config correct? You said:"}
105
+
106
+ #{File.read Boom.config.file}
107
+
108
+ #{cyan "Our survey says:"}
109
+
110
+ #{self.class.sample_config}
111
+
112
+ #{yellow "Go edit "} #{Boom.config.file + yellow(" and make it all better") }
113
+ ).gsub(/^ {8}/, '') # strip the first eight spaces of every line
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,7 @@
1
+ unless Symbol.method_defined?(:to_proc)
2
+ class Symbol
3
+ def to_proc
4
+ Proc.new { |obj, *args| obj.send(self, *args) }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,72 @@
1
+ # coding: utf-8
2
+
3
+ # The representation of the base unit in boom. An Item contains just a name and
4
+ # a value. It doesn't know its parent relationship explicitly; the parent List
5
+ # object instead knows which Items it contains.
6
+ #
7
+ module Boom
8
+ class Item
9
+
10
+ # Public: the String name of the Item
11
+ attr_accessor :name
12
+
13
+ # Public: the String value of the Item
14
+ attr_accessor :value
15
+
16
+ # Public: creates a new Item object.
17
+ #
18
+ # name - the String name of the Item
19
+ # value - the String value of the Item
20
+ #
21
+ # Examples
22
+ #
23
+ # Item.new("github", "https://github.com")
24
+ #
25
+ # Returns the newly initialized Item.
26
+ def initialize(name,value)
27
+ @name = name
28
+ @value = value
29
+ end
30
+
31
+ # Public: the shortened String name of the Item. Truncates with ellipses if
32
+ # larger.
33
+ #
34
+ # Examples
35
+ #
36
+ # item = Item.new("github's home page","https://github.com")
37
+ # item.short_name
38
+ # # => 'github's home p…'
39
+ #
40
+ # item = Item.new("github","https://github.com")
41
+ # item.short_name
42
+ # # => 'github'
43
+ #
44
+ # Returns the shortened name.
45
+ def short_name
46
+ name.length > 15 ? "#{name[0..14]}…" : name[0..14]
47
+ end
48
+
49
+ # Public: the amount of consistent spaces to pad based on Item#short_name.
50
+ #
51
+ # Returns a String of spaces.
52
+ def spacer
53
+ name.length > 15 ? '' : ' '*(15-name.length+1)
54
+ end
55
+
56
+ # Public: only return url part of value - if no url has been
57
+ # detected returns value.
58
+ #
59
+ # Returns a String which preferably is a URL.
60
+ def url
61
+ @url ||= value.split(/\s+/).detect { |v| v =~ %r{\A[a-z0-9]+:\S+}i } || value
62
+ end
63
+
64
+ # Public: creates a Hash for this Item.
65
+ #
66
+ # Returns a Hash of its data.
67
+ def to_hash
68
+ { @name => @value }
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,100 @@
1
+ # coding: utf-8
2
+
3
+ # The List contains many Items. They exist as buckets in which to categorize
4
+ # individual Items. The relationship is maintained in a simple array on the
5
+ # List-level.
6
+ #
7
+ module Boom
8
+ class List
9
+
10
+ # Public: creates a new List instance in-memory.
11
+ #
12
+ # name - The name of the List. Fails if already used.
13
+ #
14
+ # Returns the unpersisted List instance.
15
+ def initialize(name)
16
+ @items = []
17
+ @name = name
18
+ end
19
+
20
+ # Public: accesses the in-memory JSON representation.
21
+ #
22
+ # Returns a Storage instance.
23
+ def self.storage
24
+ Boom.storage
25
+ end
26
+
27
+ # Public: lets you access the array of items contained within this List.
28
+ #
29
+ # Returns an Array of Items.
30
+ attr_accessor :items
31
+
32
+ # Public: the name of the List.
33
+ #
34
+ # Returns the String name.
35
+ attr_accessor :name
36
+
37
+ # Public: associates an Item with this List. If the item name is already
38
+ # defined, then the value will be replaced
39
+ #
40
+ # item - the Item object to associate with this List.
41
+ #
42
+ # Returns the current set of items.
43
+ def add_item(item)
44
+ delete_item(item.name) if find_item(item.name)
45
+ @items << item
46
+ end
47
+
48
+ # Public: finds any given List by name.
49
+ #
50
+ # name - String name of the list to search for
51
+ #
52
+ # Returns the first instance of List that it finds.
53
+ def self.find(name)
54
+ storage.lists.find { |list| list.name == name }
55
+ end
56
+
57
+ # Public: deletes a List by name.
58
+ #
59
+ # name - String name of the list to delete
60
+ #
61
+ # Returns whether one or more lists were removed.
62
+ def self.delete(name)
63
+ previous = storage.lists.size
64
+ storage.lists = storage.lists.reject { |list| list.name == name }
65
+ previous != storage.lists.size
66
+ end
67
+
68
+ # Public: deletes an Item by name.
69
+ #
70
+ # name - String name of the item to delete
71
+ #
72
+ # Returns whether an item was removed.
73
+ def delete_item(name)
74
+ previous = items.size
75
+ items.reject! { |item| item.name == name}
76
+ previous != items.size
77
+ end
78
+
79
+ # Public: finds an Item by name. If the name is typically truncated, also
80
+ # allow a search based on that truncated name.
81
+ #
82
+ # name - String name of the Item to find
83
+ #
84
+ # Returns the found item
85
+ def find_item(name)
86
+ items.find do |item|
87
+ item.name == name ||
88
+ item.short_name.gsub('…','') == name.gsub('…','')
89
+ end
90
+ end
91
+
92
+ # Public: a Hash representation of this List.
93
+ #
94
+ # Returns a Hash of its own data and its child Items.
95
+ def to_hash
96
+ { name => items.collect(&:to_hash) }
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,13 @@
1
+ module Boom
2
+ module Output
3
+ # Public: prints any given string.
4
+ #
5
+ # s = String output
6
+ #
7
+ # Prints to STDOUT and returns. This method exists to standardize output
8
+ # and for easy mocking or overriding.
9
+ def output(s)
10
+ puts(s)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,103 @@
1
+ # coding: utf-8
2
+
3
+ # Platform is a centralized point to shell out platform specific functionality
4
+ # like clipboard access or commands to open URLs.
5
+ #
6
+ #
7
+ # Clipboard is a centralized point to shell out to each individual platform's
8
+ # clipboard, pasteboard, or whatever they decide to call it.
9
+ #
10
+ module Boom
11
+ class Platform
12
+ class << self
13
+ # Public: tests if currently running on darwin.
14
+ #
15
+ # Returns true if running on darwin (MacOS X), else false
16
+ def darwin?
17
+ !!(RUBY_PLATFORM =~ /darwin/)
18
+ end
19
+
20
+ # Public: tests if currently running on windows.
21
+ #
22
+ # Apparently Windows RUBY_PLATFORM can be 'win32' or 'mingw32'
23
+ #
24
+ # Returns true if running on windows (win32/mingw32), else false
25
+ def windows?
26
+ !!(RUBY_PLATFORM =~ /mswin|mingw/)
27
+ end
28
+
29
+ # Public: returns the command used to open a file or URL
30
+ # for the current platform.
31
+ #
32
+ # Currently only supports MacOS X and Linux with `xdg-open`.
33
+ #
34
+ # Returns a String with the bin
35
+ def open_command
36
+ if darwin?
37
+ 'open'
38
+ elsif windows?
39
+ 'start'
40
+ else
41
+ 'xdg-open'
42
+ end
43
+ end
44
+
45
+ # Public: opens a given Item's value in the browser. This
46
+ # method is designed to handle multiple platforms.
47
+ #
48
+ # Returns a String of the Item value.
49
+ def open(item)
50
+ unless windows?
51
+ system("#{open_command} '#{item.url.gsub("\'","'\\\\''")}'")
52
+ else
53
+ system("#{open_command} #{item.url.gsub("\'","'\\\\''")}")
54
+ end
55
+
56
+ item.value
57
+ end
58
+
59
+ # Public: returns the command used to copy a given Item's value to the
60
+ # clipboard for the current platform.
61
+ #
62
+ # Returns a String with the bin
63
+ def copy_command
64
+ if darwin?
65
+ 'pbcopy'
66
+ elsif windows?
67
+ 'clip'
68
+ else
69
+ 'xclip -selection clipboard'
70
+ end
71
+ end
72
+
73
+ # Public: copies a given Item's value to the clipboard. This method is
74
+ # designed to handle multiple platforms.
75
+ #
76
+ # Returns the String value of the Item.
77
+ def copy(item)
78
+ IO.popen(copy_command,"w") {|cc| cc.write(item.value)}
79
+ item.value
80
+ end
81
+
82
+ # Public: opens the JSON file in an editor for you to edit. Uses the
83
+ # $EDITOR environment variable, or %EDITOR% on Windows for editing.
84
+ # This method is designed to handle multiple platforms.
85
+ # If $EDITOR is nil, try to open using the open_command.
86
+ #
87
+ # Returns a String with a helpful message.
88
+ def edit(json_file)
89
+ unless $EDITOR.nil?
90
+ unless windows?
91
+ system("`echo $EDITOR` #{json_file} &")
92
+ else
93
+ system("start %EDITOR% #{json_file}")
94
+ end
95
+ else
96
+ system("#{open_command} #{json_file}")
97
+ end
98
+
99
+ "Make your edits, and do be sure to save."
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,47 @@
1
+ module Boom
2
+ module Remote
3
+ include Output
4
+ include Color
5
+
6
+ def allowed_storage_types
7
+ [Boom::Storage::Gist, Boom::Storage::Mongodb, Boom::Storage::Redis]
8
+ end
9
+
10
+ # Returns true if the user is using a sensible seeming storage backend
11
+ # Params storage, the current storage instance
12
+ # else exits with a warning if not
13
+ def allowed? storage
14
+ return true if Boom.local?
15
+ return true if allowed_storage_types.include? storage.class
16
+
17
+ output error_message storage
18
+ false
19
+ end
20
+
21
+ def error_message storage
22
+ %(
23
+ #{red("<<----BOOOOOM!!!----->>>> ")} (as in explosion, rather than fast)
24
+ You probably don't want to use the #{storage.class} storage whilst
25
+ using boom in remote mode. Your config looks akin to:
26
+
27
+ #{red File.read Boom.config.file}
28
+
29
+ but things might be better with something a little more like:
30
+
31
+ #{cyan Boom::Storage::Redis.sample_config}
32
+
33
+ or
34
+
35
+ #{cyan Boom::Storage::Mongodb.sample_config}
36
+
37
+ Now head yourself on over to #{yellow Boom.config.file}
38
+ and edit some goodness into it
39
+
40
+ Meanwhile, please enjoy your ordinary boom service:
41
+ ___________________________________________________
42
+
43
+ ).gsub(/^ {8}/, '')
44
+ end
45
+ extend self
46
+ end
47
+ end