rubyshelltools 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- # Methods useful when dealing with Dates
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
- def initialize(_name='unnamed', _start=nil, _end=nil, _events=[])
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
- # List Event-headlines for a given date
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 not output lines with no events
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
- (_events = events_on(_date).map(&:event_headline).join(' + ').strip) != ''
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
- # has a label. It's supposed to be appended to a Calendar-object
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
@@ -40,9 +40,10 @@ module RST
40
40
  end
41
41
 
42
42
  # used in calendar-output as a short entry
43
+ # @abstract - overwrite in descendants
43
44
  # @return [String]
44
45
  def event_headline
45
- "(untitled event)"
46
+ self.inspect
46
47
  end
47
48
 
48
49
  end
@@ -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
- attr_reader :filename
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(filename: 'myfile', other_option: '...')
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='store.data',args={})
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
- # .../data/development/store.data
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=8 # Length of Store-IDs
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
- # Set and return an unique ID
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
- # ## Usage:
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
- update_or_add(object)
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 Enumerable
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] if nothing found
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] of objects with matching ids if more than one matched
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
- # Delete the store
65
- # @abstract - override this for real persistent store-classes
66
- def delete!
67
- @objects = []
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
- private
69
+ # @group ABSTRACT METHODS TO BE OVERWRITTEN IN DESCENDANTS
71
70
 
72
- # callback called from initializer and aimed to initialize the
73
- # objects-array, PStore, database-connection,...
74
- # @abstract
75
- def setup_backend
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
- # Make sure the current state of objects is stored
80
- # @abstract
81
- def sync_store
82
- # RST.logger.warn("Store class #{self.class.to_s} should overwrite method :sync_store" )
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
- # Find and update or add an object to the store
102
- # @param [Object] object
103
- # @abstract - override in other StoreClasses
104
- def update_or_add(object)
105
- raise AbstractMethodCallError.new("Please, overrwrite #{__callee__} in #{self.class.to_s}")
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.1'
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.1
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-16 00:00:00.000000000 Z
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