plain_record 0.1 → 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,146 @@
1
+ =begin
2
+ Module to add before/after hooks.
3
+
4
+ Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru>
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ =end
19
+
20
+ module PlainRecord
21
+ # Callbacks are hooks that allow you to define methods to run before and
22
+ # after some method, to change it logic.
23
+ module Callbacks
24
+ # Hash of class callbacks with property.
25
+ attr_accessor :callbacks
26
+
27
+ # Set block as callback before +events+. Callback with less +priority+ will
28
+ # start earlier.
29
+ #
30
+ # class File
31
+ # include PlainRecord::Callbacks
32
+ #
33
+ # attr_accessor :name
34
+ # attr_accessor :content
35
+ #
36
+ # def save
37
+ # use_callbacks(:save, self) do
38
+ # File.open(@name, 'w') { |io| io.puts @content }
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ # class NewFile < File
44
+ # before :save do |file|
45
+ # while File.exists? file.name
46
+ # file.name = 'another ' + file.name
47
+ # end
48
+ # end
49
+ #
50
+ # before, :save do
51
+ # raise ArgumentError if 255 < @name.length
52
+ # end
53
+ # end
54
+ def before(events, priority = 1, &block)
55
+ Array(events).each do |event|
56
+ add_callback(:before, event, priority, block)
57
+ end
58
+ end
59
+
60
+ # Set block as callback after +events+. Callback with less +priority+ will
61
+ # start earlier.
62
+ #
63
+ # After callbacks may change method return, which will be pass as first
64
+ # argument for first callback. It return will be pass for next callback and
65
+ # so on.
66
+ #
67
+ # class Person
68
+ # include PlainRecord::Callbacks
69
+ #
70
+ # def name
71
+ # use_callbacks(:name) do
72
+ # 'John'
73
+ # end
74
+ # end
75
+ # end
76
+ #
77
+ # class GreatPerson < Person
78
+ # after :name, 2 do |name|
79
+ # 'Great ' + name
80
+ # end
81
+ #
82
+ # after :name do |name|
83
+ # 'The ' + name
84
+ # end
85
+ # end
86
+ #
87
+ # GreatPerson.new.name #=> "The Great John"
88
+ def after(events, priority = 1, &block)
89
+ Array(events).each do |event|
90
+ add_callback(:after, event, priority, block)
91
+ end
92
+ end
93
+
94
+ # Call +before+ callbacks for +event+ with +params+. In your
95
+ # code use more pretty +use_callbacks+ method.
96
+ def call_before_callbacks(event, params)
97
+ init_callbacks(event)
98
+ @callbacks[:before][event].each do |before, priority|
99
+ before.call(*params)
100
+ end
101
+ end
102
+
103
+ # Call +before+ callbacks for +event+ with +params+. Callbacks can change
104
+ # +result+. In your code use more pretty +use_callbacks+ method.
105
+ def call_after_callbacks(event, result, params)
106
+ init_callbacks(event)
107
+ @callbacks[:after][event].each do |after, priority|
108
+ result = after.call(result, *params)
109
+ end
110
+ result
111
+ end
112
+
113
+ # Call before callback for +event+, run block and give it result to
114
+ # after callbacks.
115
+ #
116
+ # def my_save_method(entry)
117
+ # use_callbacks(:save, enrty) do
118
+ # entry.file.write
119
+ # end
120
+ # end
121
+ def use_callbacks(event, *params, &block)
122
+ call_before_callbacks(event, params)
123
+ result = yield
124
+ call_after_callbacks(event, result, params)
125
+ end
126
+
127
+ private
128
+
129
+ # Backend for +before+ and +after+ method to add callback.
130
+ def add_callback(type, event, priority, block)
131
+ init_callbacks(event)
132
+
133
+ @callbacks[type][event] << [block, priority]
134
+ @callbacks[type][event].sort! { |a, b| a[1] <=> b[1] }
135
+ end
136
+
137
+ # Check and create Hash into +callbacks+ for +event+ if necessary.
138
+ def init_callbacks(event)
139
+ unless @callbacks
140
+ @callbacks = { :before => { }, :after => { } }
141
+ end
142
+ @callbacks[:before][event] = [] unless @callbacks[:before][event]
143
+ @callbacks[:after][event] = [] unless @callbacks[:after][event]
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,141 @@
1
+ =begin
2
+ Extention to get property from entry file path.
3
+
4
+ Copyright (C) 2009 Andrey “A.I.” Sitnik <andrey@sitnik.ru>
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ =end
19
+
20
+ module PlainRecord
21
+ # Extention to get properties from enrty file path. For example, your blog
22
+ # post may stored in <tt>_name_/post.md</tt>, and post model will have +name+
23
+ # property. Also if you set name property to Model#first or Model#all method,
24
+ # they will load entry directly only by it file.
25
+ #
26
+ # To define filepath property:
27
+ # 1. Use <tt>*</tt> or <tt>**</tt> pattern in model path in +enrty_in+ or
28
+ # +list_in+.
29
+ # 2. In +virtual+ method use <tt>in_filepath(i)</tt> definer after name with
30
+ # <tt>*</tt> or <tt>**</tt> number (start from 1).
31
+ #
32
+ # Define filepath property only after +entry_in+ or +list_in+ call.
33
+ #
34
+ # class Post
35
+ # include PlainRecord::Resource
36
+ #
37
+ # entry_in '*/*/post.md'
38
+ #
39
+ # virtual :category, in_filepath(1)
40
+ # virtual :name, in_filepath(1)
41
+ # …
42
+ # end
43
+ #
44
+ # superpost = Post.new
45
+ # superpost.name = 'superpost'
46
+ # superpost.category = 'best/'
47
+ # superpost.save # Save to best/superpost/post.md
48
+ #
49
+ # bests = Post.all(category: 'best') # Look up only in best/ dir
50
+ module Filepath
51
+ attr_accessor :filepath_properties
52
+ attr_accessor :filepath_regexp
53
+
54
+ private
55
+
56
+ # Return definer for filepath property for +number+ <tt>*</tt> or
57
+ # <tt>**</tt> pattern in path.
58
+ def in_filepath(number)
59
+ proc do |property, caller|
60
+ if :virtual != caller
61
+ raise ArgumentError, "You must create filepath property #{property}" +
62
+ ' virtual creator'
63
+ end
64
+ Filepath.define_property(self, property, number)
65
+ nil
66
+ end
67
+ end
68
+
69
+ class << self
70
+ # Define class variables and events in +klass+. It should be call once on
71
+ # same class after +entry_in+ or +list_in+ call.
72
+ def install(klass)
73
+ klass.filepath_properties = { }
74
+
75
+ path = Regexp.escape(klass.path).gsub(/\\\*\\\*(\/|$)/, '(.*)').
76
+ gsub('\\*', '([^/]+)')
77
+ klass.filepath_regexp = Regexp.new(path)
78
+
79
+ klass.class_eval do
80
+ attr_accessor :filepath_data
81
+ end
82
+
83
+ klass.after :load do |result, entry|
84
+ if entry.path
85
+ data = klass.filepath_regexp.match(entry.path)
86
+ entry.filepath_data = { }
87
+ klass.filepath_properties.each_pair do |number, name|
88
+ entry.filepath_data[name] = data[number]
89
+ end
90
+ else
91
+ entry.filepath_data = { }
92
+ klass.filepath_properties.each_value do |name|
93
+ entry.filepath_data[name] = entry.data[name]
94
+ entry.data.delete(name)
95
+ end
96
+ end
97
+ result
98
+ end
99
+
100
+ klass.after :path do |path, matchers|
101
+ i = 0
102
+ path.gsub /(\*\*(\/|$)|\*)/ do |pattern|
103
+ i += 1
104
+ property = klass.filepath_properties[i]
105
+ unless matchers[property].is_a? Regexp or matchers[property].nil?
106
+ matchers[property]
107
+ else
108
+ pattern
109
+ end
110
+ end
111
+ end
112
+
113
+ klass.before :save do |entry|
114
+ unless entry.file
115
+ path = klass.path(entry.filepath_data)
116
+ entry.file = path unless path =~ /[\*\[\?\{]/
117
+ end
118
+ end
119
+ end
120
+
121
+ # Define in +klass+ filepath property with +name+ for +number+ <tt>*</tt>
122
+ # or <tt>**</tt> pattern in path.
123
+ def define_property(klass, name, number)
124
+ unless klass.filepath_properties
125
+ install(klass)
126
+ end
127
+
128
+ klass.filepath_properties[number] = name
129
+
130
+ klass.class_eval <<-EOS, __FILE__, __LINE__
131
+ def #{name}
132
+ @filepath_data[:#{name}]
133
+ end
134
+ def #{name}=(value)
135
+ @filepath_data[:#{name}] = value
136
+ end
137
+ EOS
138
+ end
139
+ end
140
+ end
141
+ end
@@ -30,23 +30,31 @@ module PlainRecord
30
30
  end
31
31
  @loaded[file]
32
32
  end
33
-
34
- def each_entry
35
- files.each do |file|
33
+
34
+ def each_entry(matcher = { })
35
+ files(matcher).each do |file|
36
36
  yield load_file(file)
37
37
  end
38
38
  end
39
-
39
+
40
40
  def delete_entry(file, entry = nil)
41
41
  delete_file(file)
42
42
  end
43
-
43
+
44
+ def move_entry(entry, from, to)
45
+ if from
46
+ @loaded.delete(from)
47
+ delete_file(from)
48
+ end
49
+ @loaded[to] = entry
50
+ end
51
+
44
52
  private
45
-
46
- def all_entries
47
- files.map { |file| load_file(file) }
53
+
54
+ def all_entries(matcher = { })
55
+ files(matcher).map { |file| load_file(file) }
48
56
  end
49
-
57
+
50
58
  def entries_string(entry)
51
59
  entry.to_yaml + entry.texts.map{ |i| "---\n" + i }.join("\n")
52
60
  end
@@ -29,15 +29,15 @@ module PlainRecord
29
29
  end
30
30
  @loaded[file]
31
31
  end
32
-
33
- def each_entry
34
- files.each do |file|
32
+
33
+ def each_entry(matcher = { })
34
+ files(matcher).each do |file|
35
35
  load_file(file).each do |entry|
36
36
  yield entry
37
37
  end
38
38
  end
39
39
  end
40
-
40
+
41
41
  def delete_entry(file, entry = nil)
42
42
  if entry.nil? or 1 == @loaded[file].length
43
43
  delete_file(file)
@@ -46,13 +46,26 @@ module PlainRecord
46
46
  save_file(file)
47
47
  end
48
48
  end
49
-
49
+
50
+ def move_entry(entry, from, to)
51
+ if from
52
+ @loaded[from].delete(entry)
53
+ if @loaded[from].empty?
54
+ delete_file(from)
55
+ else
56
+ save_file(from)
57
+ end
58
+ end
59
+ @loaded[to] = [] unless @loaded.has_key? to
60
+ @loaded[to] << entry
61
+ end
62
+
50
63
  private
51
-
52
- def all_entries
53
- files.map { |file| load_file(file) }.flatten
64
+
65
+ def all_entries(matcher = { })
66
+ files(matcher).map { |file| load_file(file) }.flatten
54
67
  end
55
-
68
+
56
69
  def entries_string(entries)
57
70
  entries.to_yaml
58
71
  end