rubyshelltools 0.0.1 → 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.
- data/lib/core_extensions/mutex.rb +17 -0
- data/lib/load.rb +6 -0
- data/lib/modules/calendar/calendar.rb +22 -9
- data/lib/modules/calendar/calendar_event.rb +17 -4
- data/lib/modules/calendar/eventable.rb +2 -1
- data/lib/modules/persistent/disk_store.rb +23 -14
- data/lib/modules/persistent/memory_store.rb +19 -7
- data/lib/modules/persistent/persistent.rb +38 -2
- data/lib/modules/persistent/store.rb +58 -40
- data/rst.rb +4 -2
- metadata +3 -2
@@ -0,0 +1,17 @@
|
|
1
|
+
# Not sure if this is a bug.
|
2
|
+
# Anyhow, PStore works only if the two methods are defined for Muttex.
|
3
|
+
# @see Persistent::DiskStore
|
4
|
+
class Mutex
|
5
|
+
if RUBY_VERSION >= "1.9"
|
6
|
+
#:nodoc
|
7
|
+
def marshal_dump
|
8
|
+
[]
|
9
|
+
end
|
10
|
+
|
11
|
+
#:nodoc
|
12
|
+
def marshal_load array
|
13
|
+
# do nothing
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
data/lib/load.rb
CHANGED
@@ -7,8 +7,13 @@ unless defined? LIB_LOADED
|
|
7
7
|
DEFAULT_DATE_FORMAT = '%a, %b %d %Y'
|
8
8
|
|
9
9
|
# Filename for the calendar-store
|
10
|
+
# @see Calendar::Calendar
|
10
11
|
CALENDAR_FILE = 'calendars.data'
|
11
12
|
|
13
|
+
# If no calendar-name is given
|
14
|
+
# @see Calendar::Calendar
|
15
|
+
DEFAULT_CALENDAR_NAME = 'unnamed'
|
16
|
+
|
12
17
|
require File.expand_path('../../rst',__FILE__)
|
13
18
|
|
14
19
|
# Load all necessary files
|
@@ -16,6 +21,7 @@ unless defined? LIB_LOADED
|
|
16
21
|
|
17
22
|
$LOAD_PATH.unshift(File.expand_path('../core_extensions',__FILE__))
|
18
23
|
require 'numeric'
|
24
|
+
require 'mutex'
|
19
25
|
|
20
26
|
$LOAD_PATH.unshift(File.expand_path('../errors',__FILE__))
|
21
27
|
require 'store_errors'
|
@@ -6,7 +6,9 @@ module RST
|
|
6
6
|
# Calendar-module provides the Calendar-class which is supposed to hold
|
7
7
|
# 'Eventable'-objects. Where Eventable is a module you can include to any class.
|
8
8
|
# A Calendar has a start- and ending-date, and a name. The name is used
|
9
|
-
# to store the calendar in a store using this name.
|
9
|
+
# to store the calendar in a store using this name as the id.
|
10
|
+
#
|
11
|
+
# @see Persistent::DiskStore
|
10
12
|
#
|
11
13
|
# @example
|
12
14
|
#
|
@@ -38,7 +40,7 @@ module RST
|
|
38
40
|
#
|
39
41
|
module Calendar
|
40
42
|
|
41
|
-
#
|
43
|
+
# Some helper-methods useful when dealing with dates
|
42
44
|
module CalendarHelper
|
43
45
|
|
44
46
|
# You can use 'today' or any format which Date.parse can handle.
|
@@ -58,6 +60,9 @@ module RST
|
|
58
60
|
# Calendar has a name and will be stored in Persistent::DiskStore(CALENDAR_FILE)
|
59
61
|
# with it's name as the id. Thus you can save different calendars in
|
60
62
|
# the same file. If no name is given 'unnamed' will be the default.
|
63
|
+
# @see CALENDAR_FILE
|
64
|
+
# @see Persistent::DiskStore
|
65
|
+
# @see Calendar::CalendarEvent
|
61
66
|
class Calendar
|
62
67
|
|
63
68
|
include Persistent::Persistentable
|
@@ -68,7 +73,8 @@ module RST
|
|
68
73
|
# @param [String] _name Name and persistent-id of this calendar.
|
69
74
|
# @param [Date|Time|String] _start The date when the calendar starts
|
70
75
|
# @param [Date|Time|String] _end The date when the calendar ends
|
71
|
-
|
76
|
+
# @see DEFAULT_CALENDAR_NAME
|
77
|
+
def initialize(_name=DEFAULT_CALENDAR_NAME, _start=nil, _end=nil, _events=[])
|
72
78
|
@name = _name
|
73
79
|
@start_date = parse_date_param(_start)
|
74
80
|
@end_date = parse_date_param(_end)
|
@@ -89,6 +95,7 @@ module RST
|
|
89
95
|
end
|
90
96
|
|
91
97
|
# Override Persistentable's id-method
|
98
|
+
# @see Persistent::Persistentable
|
92
99
|
# @return [String] - the calendar-name is it's id
|
93
100
|
def id
|
94
101
|
@name || super
|
@@ -123,9 +130,9 @@ module RST
|
|
123
130
|
|
124
131
|
|
125
132
|
private
|
126
|
-
#
|
133
|
+
# All events on the given date
|
127
134
|
# @param [Date] date
|
128
|
-
# @return [Array] of CalendarEvents
|
135
|
+
# @return [Array] of [CalendarEvents]
|
129
136
|
def events_on(date)
|
130
137
|
events.select { |event| event.event_date == date }
|
131
138
|
end
|
@@ -139,14 +146,20 @@ module RST
|
|
139
146
|
|
140
147
|
# Output date and Events on this date in one line
|
141
148
|
# @param [Date] _date
|
142
|
-
# @param [Boolean] show_empty - do
|
149
|
+
# @param [Boolean] show_empty - do output lines with no events
|
143
150
|
# @return [String]
|
144
151
|
def format_events_for(_date,show_empty=false)
|
145
|
-
if show_empty ||
|
146
|
-
|
147
|
-
[_date.strftime(DEFAULT_DATE_FORMAT), _events].compact.join(": ")
|
152
|
+
if show_empty || (_line=event_headlines_for(_date)) != ''
|
153
|
+
"%s: %s" % [_date.strftime(DEFAULT_DATE_FORMAT), _line]
|
148
154
|
end
|
149
155
|
end
|
156
|
+
|
157
|
+
# Concatenate headlines of all events on this date
|
158
|
+
# @param [Date] _date
|
159
|
+
# @return [String]
|
160
|
+
def event_headlines_for(_date)
|
161
|
+
events_on(_date).map(&:event_headline).join(' + ').strip
|
162
|
+
end
|
150
163
|
|
151
164
|
end
|
152
165
|
|
@@ -2,9 +2,22 @@ module RST
|
|
2
2
|
|
3
3
|
module Calendar
|
4
4
|
|
5
|
-
# A CalendarEvent happens on a given date and
|
6
|
-
#
|
7
|
-
# @see Calendar
|
5
|
+
# A CalendarEvent happens on a given date and has a label.
|
6
|
+
# It's can be used in a Calendar
|
7
|
+
# @see Calendar::Calendar
|
8
|
+
# @see Calendar::Eventable
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# calendar = Calendar::Calendar.new('my calendar')
|
12
|
+
# event = Calendar::CalendarEvent.new( 'today', "I'm happy" )
|
13
|
+
# calendar << event
|
14
|
+
# calendar.format_events_for(Date.today)
|
15
|
+
# => "Sun, Mar 17 2013: I'm happy"
|
16
|
+
# calendar << Calendar::CalendarEvent.new('2013-03-18', "Back to work :(")
|
17
|
+
# calendar.list_days('today','4/2013')
|
18
|
+
# => Sun, Mar 17 2013: I'm happy
|
19
|
+
# => Mon, Mar 18 2013: Back to work :(
|
20
|
+
#
|
8
21
|
class CalendarEvent
|
9
22
|
|
10
23
|
attr_reader :event_date, :label
|
@@ -15,8 +28,8 @@ module RST
|
|
15
28
|
# @param [String|Date] _date - Date of the event (only all-day-events are possible yet)
|
16
29
|
# @param [String] _label - Events name
|
17
30
|
def initialize(_date,_label)
|
18
|
-
@event_date = ensure_date(_date)
|
19
31
|
@label = _label
|
32
|
+
schedule! ensure_date(_date)
|
20
33
|
end
|
21
34
|
|
22
35
|
# override abstract method for an Eventable
|
@@ -4,21 +4,25 @@ module RST
|
|
4
4
|
# #DiskStore is responsible to save an Array of objects
|
5
5
|
# persistently to the disk - PStore is used to effort this.
|
6
6
|
# @see RST::Persistent::Store
|
7
|
+
# @see Mutex
|
7
8
|
# @api persistent
|
8
9
|
class DiskStore < Store
|
9
10
|
|
10
|
-
|
11
|
+
# [String] filename - used for PStore (no path! filename only)
|
12
|
+
# @see #store_path
|
13
|
+
attr_reader :filename
|
11
14
|
|
12
15
|
# The first argument is the filename
|
13
16
|
# Following args are passed to base-class
|
14
17
|
# @example
|
15
18
|
#
|
16
19
|
# DiskStore.new('myfile')
|
17
|
-
# DiskStore.new(
|
18
|
-
#
|
20
|
+
# DiskStore.new('myfile', other_option: '...')
|
21
|
+
# DiskStore.new( nil, other_option: '...') # use default filename
|
22
|
+
# @see DEFAULT_STORE_FILENAME
|
19
23
|
# @param [String] _filename
|
20
24
|
# @param [Array] args - arguments passed to the super-class
|
21
|
-
def initialize(_filename=
|
25
|
+
def initialize(_filename=DEFAULT_STORE_FILENAME,args={})
|
22
26
|
@filename = _filename
|
23
27
|
super(args)
|
24
28
|
end
|
@@ -45,9 +49,18 @@ module RST
|
|
45
49
|
File.unlink(store_path)
|
46
50
|
end
|
47
51
|
|
52
|
+
# Find and update or add object to the store.
|
53
|
+
# @param [Object] object
|
54
|
+
def update(object)
|
55
|
+
@store.transaction do |s|
|
56
|
+
s[object.id] = object
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
48
60
|
private
|
49
61
|
|
50
62
|
# Initialize a PStore-instance
|
63
|
+
# @see store_path
|
51
64
|
def setup_backend
|
52
65
|
@store = PStore.new(store_path)
|
53
66
|
end
|
@@ -57,8 +70,13 @@ module RST
|
|
57
70
|
# environment. Creates the path if not exists.
|
58
71
|
#
|
59
72
|
# @example
|
60
|
-
#
|
73
|
+
# RST_ENV=development
|
74
|
+
# RST_DATA=$HOME/.rst-data
|
75
|
+
#
|
76
|
+
# ~/.rst-data/development/store.data
|
61
77
|
#
|
78
|
+
# if no environment-variables defined, the defaulsts will be STOREPATH and 'development'
|
79
|
+
# @see STOREPATH
|
62
80
|
# @return [String]
|
63
81
|
def store_path
|
64
82
|
prefix = ENV['RST_DATA'] || RST::STOREPATH
|
@@ -68,21 +86,12 @@ module RST
|
|
68
86
|
File.join(_dir, filename)
|
69
87
|
end
|
70
88
|
|
71
|
-
# Find and update or add object to the store.
|
72
|
-
# @param [Object] object
|
73
|
-
def update_or_add(object)
|
74
|
-
@store.transaction do |s|
|
75
|
-
s[object.id] = object
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
89
|
# @param [Object] object - the object to be removed.
|
80
90
|
def remove_object(object)
|
81
91
|
@store.transaction do |s|
|
82
92
|
s.delete(object.id)
|
83
93
|
end
|
84
94
|
end
|
85
|
-
|
86
95
|
end
|
87
96
|
|
88
97
|
end
|
@@ -13,6 +13,18 @@ module RST
|
|
13
13
|
@objects || []
|
14
14
|
end
|
15
15
|
|
16
|
+
# Find and update or add an object to the store
|
17
|
+
# @param [Object] object
|
18
|
+
def update(object)
|
19
|
+
@objects -= [object]
|
20
|
+
@objects << object
|
21
|
+
end
|
22
|
+
|
23
|
+
# remove all objects
|
24
|
+
def delete!
|
25
|
+
@objects = []
|
26
|
+
end
|
27
|
+
|
16
28
|
private
|
17
29
|
|
18
30
|
# Initialize an empty array as an in-memory-store
|
@@ -20,17 +32,17 @@ module RST
|
|
20
32
|
@objects = []
|
21
33
|
end
|
22
34
|
|
23
|
-
# Find and update or add an object to the store
|
24
|
-
# @param [Object] object
|
25
|
-
def update_or_add(object)
|
26
|
-
@objects -= [object]
|
27
|
-
@objects << object
|
28
|
-
end
|
29
|
-
|
30
35
|
# @param [Object] object - the object to be removed
|
31
36
|
def remove_object(object)
|
32
37
|
@objects -= [object]
|
33
38
|
end
|
39
|
+
|
40
|
+
# Make sure the current state of objects is stored
|
41
|
+
# @abstract - Overwrite in descendants thus every object gets persistently stored.
|
42
|
+
def sync_store
|
43
|
+
#noop for MemoryStore
|
44
|
+
end
|
45
|
+
|
34
46
|
end
|
35
47
|
end
|
36
48
|
end
|
@@ -6,30 +6,66 @@ module RST
|
|
6
6
|
# The Persistent-module injects Store-functions
|
7
7
|
# to any Object including it.
|
8
8
|
# @api persistent
|
9
|
+
# @see [Store]
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# store = MemoryStore.new
|
13
|
+
# object= store.create { PersistentableObject.new }
|
14
|
+
# ...modify object ...
|
15
|
+
# object.save
|
16
|
+
#
|
17
|
+
# object = store.find(object.id)
|
18
|
+
# object.delete
|
19
|
+
#
|
9
20
|
module Persistent
|
10
21
|
|
11
|
-
KEY_LENGTH=
|
22
|
+
KEY_LENGTH=42 # Length of Store-IDs
|
12
23
|
|
13
24
|
# The interface for persistent-able objects
|
14
25
|
module Persistentable
|
15
26
|
|
27
|
+
attr_reader :store # @see Persistent::Store
|
28
|
+
|
16
29
|
# Save the object to Store
|
17
30
|
def save
|
31
|
+
store.update(self)
|
18
32
|
end
|
19
33
|
|
20
34
|
# Remove the object from Store
|
21
35
|
def delete
|
36
|
+
self.store -= self
|
22
37
|
end
|
23
38
|
|
24
39
|
# If the object doesn't provide an id-method
|
25
40
|
# define one
|
26
41
|
unless respond_to?(:id)
|
27
|
-
#
|
42
|
+
# Return an unique ID (create one if not exists yet)
|
43
|
+
# @return [String]
|
44
|
+
# @see KEY_LENGTH
|
28
45
|
def id
|
29
46
|
@id ||= SecureRandom::hex(KEY_LENGTH)
|
30
47
|
end
|
31
48
|
end
|
32
49
|
|
50
|
+
# Setter for store-attribute
|
51
|
+
# @param [Store] store
|
52
|
+
def store=(store)
|
53
|
+
move_store(store)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Move the object to another store
|
59
|
+
# @abstract - Moving is not implemented yet, though. The method sets the store-attribute if not set already.
|
60
|
+
def move_store store
|
61
|
+
if @store && @store != store
|
62
|
+
raise NotImplementedError.new('An object can\'t be moved to another store')
|
63
|
+
elsif @store == store
|
64
|
+
self
|
65
|
+
else
|
66
|
+
@store = store
|
67
|
+
end
|
68
|
+
end
|
33
69
|
end
|
34
70
|
|
35
71
|
end
|
@@ -9,18 +9,13 @@ module RST
|
|
9
9
|
#
|
10
10
|
# Store provides the interface for all store-able classes
|
11
11
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# Derive a concrete store-class from 'Store' and overwrite the
|
15
|
-
# following methods:
|
16
|
-
#
|
17
|
-
# * setup_backend
|
18
|
-
# * sync_store
|
19
|
-
#
|
20
|
-
# @abstract
|
12
|
+
# @abstract public API-methods should not be overwritten by descendant classes.
|
13
|
+
# But descendants must overwrite all methods marked as abstract here.
|
21
14
|
#
|
22
15
|
class Store
|
23
16
|
|
17
|
+
# @group PUBLIC API
|
18
|
+
|
24
19
|
# Sets options-hash and calls the abstract
|
25
20
|
# 'setup_backend'-callback
|
26
21
|
def initialize(args={})
|
@@ -29,9 +24,9 @@ module RST
|
|
29
24
|
end
|
30
25
|
|
31
26
|
# Add an object and sync store
|
32
|
-
# @param [Persistentable] object - object including the Persistent-module
|
27
|
+
# @param [Persistentable] object - any object including the Persistent-module
|
33
28
|
def <<(object)
|
34
|
-
|
29
|
+
update(object)
|
35
30
|
end
|
36
31
|
|
37
32
|
# Remove an object from the store
|
@@ -42,46 +37,62 @@ module RST
|
|
42
37
|
self
|
43
38
|
end
|
44
39
|
|
45
|
-
# @return
|
46
|
-
# @abstract
|
47
|
-
def all
|
48
|
-
raise AbstractMethodCallError.new("Please, overwrite #{__callee__} in #{self.class.to_s}")
|
49
|
-
end
|
50
|
-
|
51
|
-
# @return [Object|nil] the first object in the store
|
40
|
+
# @return [Object|nil] the first object in the store or nil if the store is empty.
|
52
41
|
def first
|
53
42
|
all.first
|
54
43
|
end
|
55
44
|
|
56
|
-
# @param [Array] ids to search
|
57
|
-
# @return [nil]
|
45
|
+
# @param [Array|String] ids or id to search
|
46
|
+
# @return [nil] if nothing found
|
58
47
|
# @return [Object] if exactly one object found
|
59
|
-
# @return [Array]
|
48
|
+
# @return [Array] of objects with matching ids if more than one matched
|
60
49
|
def find(*ids)
|
61
50
|
flatten all.select { |obj| ids.include?(obj.id) }
|
62
51
|
end
|
63
52
|
|
64
|
-
|
65
|
-
#
|
66
|
-
|
67
|
-
|
53
|
+
|
54
|
+
# Create objects and set the object's store-attribute
|
55
|
+
# @example
|
56
|
+
#
|
57
|
+
# new_object = store.create
|
58
|
+
# MyClass.new(....)
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# @return [Persistentable] - the newly created object
|
62
|
+
def create
|
63
|
+
obj = yield
|
64
|
+
obj.store = self
|
65
|
+
self << obj
|
66
|
+
obj
|
68
67
|
end
|
69
68
|
|
70
|
-
|
69
|
+
# @group ABSTRACT METHODS TO BE OVERWRITTEN IN DESCENDANTS
|
71
70
|
|
72
|
-
#
|
73
|
-
# objects
|
74
|
-
|
75
|
-
|
76
|
-
raise AbstractMethodCallError.new("Please override method :setup_backend in class #{self.class.to_s}")
|
71
|
+
# @return Enumerable
|
72
|
+
# @abstract Overwrite in descendants thus it returns an Enumerable of all objects in the store
|
73
|
+
def all
|
74
|
+
raise AbstractMethodCallError.new("Please, overwrite #{__callee__} in #{self.class.to_s}")
|
77
75
|
end
|
78
76
|
|
79
|
-
#
|
80
|
-
# @abstract
|
81
|
-
def
|
82
|
-
|
77
|
+
# Delete the store
|
78
|
+
# @abstract - override this method in descendants thus the store removes all objects.
|
79
|
+
def delete!
|
80
|
+
raise AbstractMethodCallError.new("Please, overrwrite #{__callee__} in #{self.class.to_s}")
|
83
81
|
end
|
84
82
|
|
83
|
+
# Find and update or add an object to the store
|
84
|
+
# @param [Object] object
|
85
|
+
# @abstract - override in other StoreClasses
|
86
|
+
def update(object)
|
87
|
+
raise AbstractMethodCallError.new("Please, overrwrite #{__callee__} in #{self.class.to_s}")
|
88
|
+
end
|
89
|
+
|
90
|
+
# @endgroup
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# @group PRIVATE API
|
95
|
+
|
85
96
|
# Flatten the result of a select
|
86
97
|
# @param [Array] result
|
87
98
|
# @return [Array] if result contains more than one element
|
@@ -97,12 +108,19 @@ module RST
|
|
97
108
|
end
|
98
109
|
end
|
99
110
|
|
111
|
+
# @group ABSTRACT METHODS TO BE OVERWRITTEN IN DESCENDANTS
|
100
112
|
|
101
|
-
#
|
102
|
-
#
|
103
|
-
# @abstract -
|
104
|
-
def
|
105
|
-
raise AbstractMethodCallError.new("Please
|
113
|
+
# callback called from initializer. Aimed to initialize the
|
114
|
+
# objects-array, PStore, database-connection,...
|
115
|
+
# @abstract - Overwrite in descendants thus they initialize the store.
|
116
|
+
def setup_backend
|
117
|
+
raise AbstractMethodCallError.new("Please override method :setup_backend in class #{self.class.to_s}")
|
118
|
+
end
|
119
|
+
|
120
|
+
# Make sure the current state of objects is stored
|
121
|
+
# @abstract - Overwrite in descendants thus every object gets persistently stored.
|
122
|
+
def sync_store
|
123
|
+
raise AbstractMethodCallError.new("Please override method :sync_store in class #{self.class.to_s}")
|
106
124
|
end
|
107
125
|
|
108
126
|
# @param [Object] object
|
data/rst.rb
CHANGED
@@ -3,7 +3,7 @@ require 'logger'
|
|
3
3
|
# # Ruby Shell Tools Top-level file
|
4
4
|
module RST
|
5
5
|
# Gem-version
|
6
|
-
VERSION = '0.0.
|
6
|
+
VERSION = '0.0.2'
|
7
7
|
|
8
8
|
# Path to the docs used by the software
|
9
9
|
DOCS = File.expand_path('../assets/docs', __FILE__)
|
@@ -11,8 +11,10 @@ module RST
|
|
11
11
|
# Default DataStore-path. You can overwrite this by
|
12
12
|
# defining `ENV[RST_DATA]`
|
13
13
|
STOREPATH = File.expand_path('../data/', __FILE__)
|
14
|
-
|
15
14
|
|
15
|
+
# @see Persistent::DiskStore
|
16
|
+
DEFAULT_STORE_FILENAME = 'rst_data.pstore'
|
17
|
+
|
16
18
|
# intialize the logger
|
17
19
|
# @example Usage
|
18
20
|
# RST.logger.info('This will output to STDERR')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyshelltools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redcarpet
|
@@ -69,6 +69,7 @@ extra_rdoc_files:
|
|
69
69
|
- assets/docs/examples.md
|
70
70
|
files:
|
71
71
|
- ./rst.rb
|
72
|
+
- lib/core_extensions/mutex.rb
|
72
73
|
- lib/core_extensions/numeric.rb
|
73
74
|
- lib/errors/store_errors.rb
|
74
75
|
- lib/load.rb
|