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.
@@ -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