plain_record 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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