document_file 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010 Ralph von der Heyden
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,49 @@
1
+ Document File
2
+ -------------
3
+
4
+ An attempt to create a simple model layer that abstracts flat text files.
5
+
6
+ Text files look like the ones used by jekyll (see
7
+ http://github.com/mojombo/jekyll). They consist of a preambel written in YAML
8
+ (also called YAML front matter), and some content in the format you prefer,
9
+ e.g. Textile. Example:
10
+
11
+ !!!document starts in the following line.
12
+ ---
13
+ id: 1
14
+ title: The shizzle!
15
+ tags: [tag]
16
+ number_of_foos: 42
17
+ ---
18
+
19
+ I like the flowers.
20
+ !!!document ends in the previous line.
21
+
22
+ Can be abstracted like this:
23
+
24
+ class MyDocument < DocumentFile::Base
25
+ end
26
+
27
+ MyDocument.documents_dir = './documents'
28
+
29
+ # You now have dynamic finders:
30
+ doc = MyDocument.find_by_title("The shizzle!") # => returns the document
31
+ doc = MyDocument.find_by_number_of_foos(42) # => returns the document
32
+ doc = MyDocument.find_by_file_name('foo.textile') # => returns the document
33
+
34
+ # You can list documents by Array attributes
35
+ docs = MyDocument.by_tags # => Returns {"tag" => [doc_1, doc2, ...], ...}
36
+
37
+ # You can access the attributes of single documents:
38
+ doc.title # => "The shizzle!"
39
+ doc.tags # => ["tag"]
40
+ doc.content # => "I like the flowers."
41
+ doc.filename # => returns the filename without extension
42
+ doc.file_name_with_extension # => does what it says
43
+ doc.file_extension # => does what it says
44
+
45
+ # You can initialize single documents, too:
46
+ doc = MyDocument.new('./documents/document-file.textile')
47
+
48
+ # If any of the files change, you must manually reload them:
49
+ MyDocument.reload!
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rake/testtask'
2
+ task :default => :test
3
+ Rake::TestTask.new(:test) do |t|
4
+ t.test_files = FileList['test/*_test.rb']
5
+ end
@@ -0,0 +1,105 @@
1
+ require 'yaml'
2
+
3
+ module DocumentFile
4
+ class Base
5
+ @@documents_dir = './documents'
6
+ @@documents = nil
7
+ attr_reader :content, :file_path
8
+
9
+ def initialize(new_file_path)
10
+ @file_path = new_file_path
11
+ define_attribute_finder('file_name')
12
+ read_yaml
13
+ end
14
+
15
+ def file_name
16
+ File.basename file_name_with_extension, file_extension
17
+ end
18
+
19
+ def file_name_with_extension
20
+ self.file_path.split('/').last
21
+ end
22
+
23
+ def file_extension
24
+ File.extname file_name_with_extension
25
+ end
26
+
27
+ def self.all
28
+ return @@documents if @@documents
29
+ self.reload!
30
+ end
31
+
32
+ def self.reload!
33
+ if File.directory?(@@documents_dir)
34
+ file_paths = Dir.glob("#{@@documents_dir}/*.*")
35
+ @@documents = file_paths.map { |file_path| self.new File.join(Dir.getwd, file_path) }
36
+ else
37
+ []
38
+ end
39
+ end
40
+
41
+ def self.documents_dir
42
+ @@documents_dir
43
+ end
44
+
45
+ def self.documents_dir=(new_dir)
46
+ @@documents_dir = new_dir
47
+ end
48
+
49
+ private
50
+ def read_yaml
51
+ @content = File.read(@file_path)
52
+
53
+ if @content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
54
+ @content = @content[($1.size + $2.size)..-1]
55
+ @data = YAML.load($1)
56
+ end
57
+ @data ||= {}
58
+ define_dynamic_methods
59
+ end
60
+
61
+ def define_dynamic_methods
62
+ @data.each do |method_name, value|
63
+ value = "'#{value}'" if value.is_a? String
64
+ instance_eval "def #{method_name}; #{value}; end"
65
+
66
+ if value.is_a? Array
67
+ by_attribute_method = <<-eos
68
+ def self.by_#{method_name}
69
+ documents = self.all
70
+ #{method_name} = {}
71
+ documents.each do |document|
72
+ document.#{method_name}.each do |single_item|
73
+ if #{method_name}.has_key? single_item
74
+ #{method_name}[single_item] << document
75
+ else
76
+ #{method_name}[single_item] = [document]
77
+ end
78
+ end
79
+ end
80
+ #{method_name}
81
+ end
82
+ eos
83
+ self.class.send(:module_eval, by_attribute_method)
84
+ end
85
+
86
+ define_attribute_finder(method_name)
87
+ end
88
+ @@dynamic_methods_defined = true
89
+ end
90
+
91
+ def define_attribute_finder(method_name)
92
+ find_by_attribute_method = <<-eos
93
+ def self.find_by_#{method_name}(attribute)
94
+ all.detect {|document| document.#{method_name} == attribute}
95
+ end
96
+ eos
97
+ self.class.send(:module_eval, find_by_attribute_method)
98
+ end
99
+
100
+ def self.method_missing(method_name, *args)
101
+ self.all unless @@documents
102
+ super
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,3 @@
1
+ module DocumentFile
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'document_file/base'
2
+ require 'document_file/version'
@@ -0,0 +1,202 @@
1
+ require 'minitest/spec'
2
+ MiniTest::Unit.autorun
3
+ require 'set'
4
+ require 'fileutils'
5
+
6
+ testdir = File.dirname(__FILE__)
7
+ libdir = File.dirname(File.dirname(__FILE__)) + '/lib'
8
+ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
9
+
10
+ require 'document_file'
11
+
12
+ class MyDocument < DocumentFile::Base
13
+ end
14
+
15
+ describe MyDocument do
16
+ before do
17
+ MyDocument.documents_dir = testdir + '/documents'
18
+ end
19
+
20
+ describe 'when finding all document_files' do
21
+ before do
22
+ @document_files = MyDocument.all
23
+ end
24
+
25
+ it 'should return an Array' do
26
+ assert_equal Array, @document_files.class
27
+ end
28
+
29
+ it "should find all document_files" do
30
+ assert_equal 2, @document_files.size
31
+ end
32
+ end
33
+
34
+ describe 'when initializing a MyDocument' do
35
+ before do
36
+ @document_file = MyDocument.new(testdir + '/documents/2010-08-08-test-document-file.textile')
37
+ end
38
+
39
+ it 'should initialize the content' do
40
+ assert_equal "I like the flowers.\n", @document_file.content
41
+ end
42
+
43
+ it 'should intitialize strings from the front matter' do
44
+ assert_equal String, @document_file.title.class
45
+ assert_equal 'The shizzle!', @document_file.title
46
+ end
47
+
48
+ it 'should intitialize strings from the front matter' do
49
+ assert_equal Array, @document_file.tags.class
50
+ assert_equal ['tag'], @document_file.tags
51
+ end
52
+
53
+ it 'should intitialize integers from the front matter' do
54
+ assert_equal Fixnum, @document_file.number_of_foos.class
55
+ assert_equal 42, @document_file.number_of_foos
56
+ end
57
+ end
58
+
59
+ describe 'when listing document_files by an array attribute' do
60
+ it 'should return a Hash' do
61
+ assert_equal Hash, MyDocument.by_tags.class
62
+ end
63
+
64
+ it 'should use the tags as Hash keys' do
65
+ assert_equal Set.new(['tag', 'tug']), MyDocument.by_tags.keys.to_set
66
+ end
67
+
68
+ it 'should use the document_files as Hash values' do
69
+ document_files = MyDocument.by_tags
70
+ assert_equal Set.new([1, 2]), document_files['tag'].map(&:id).to_set
71
+ assert_equal Set.new([2]), document_files['tug'].map(&:id).to_set
72
+ end
73
+ end
74
+
75
+ describe 'when finding a document_file' do
76
+ it 'should find the right document_file by an attribute' do
77
+ title = 'The shizzle!'
78
+ document_file = MyDocument.find_by_title(title)
79
+ assert_equal title, document_file.title
80
+ end
81
+
82
+ it 'should find the right document_file by file_name' do
83
+ file_name = '2010-08-08-test-document-file'
84
+ document_file = MyDocument.find_by_file_name file_name
85
+ assert_equal document_file.file_name, file_name
86
+ end
87
+ end
88
+
89
+ describe 'when getting the file name or file path' do
90
+ it 'should show the right file name' do
91
+ document_file = MyDocument.new './test/documents/2010-08-08-test-document-file.textile'
92
+ file_name = '2010-08-08-test-document-file'
93
+ assert_equal file_name, document_file.file_name
94
+ end
95
+
96
+ it 'should show the right file name with extension' do
97
+ document_file = MyDocument.new './test/documents/2010-08-08-test-document-file.textile'
98
+ file_name = '2010-08-08-test-document-file.textile'
99
+ assert_equal file_name, document_file.file_name_with_extension
100
+ end
101
+
102
+ it 'should show the right extension' do
103
+ document_file = MyDocument.new './test/documents/2010-08-08-test-document-file.textile'
104
+ extension = '.textile'
105
+ assert_equal extension, document_file.file_extension
106
+ end
107
+
108
+ it 'should show the right file path' do
109
+ file_path = './test/documents/2010-08-08-test-document-file.textile'
110
+ document_file = MyDocument.new file_path
111
+ assert_equal file_path, document_file.file_path
112
+ end
113
+ end
114
+
115
+ describe 'when calling a method that was not defined dynamically' do
116
+ it 'should throw an error on the class level' do
117
+ assert_raises(NoMethodError) { MyDocument.hululu }
118
+ end
119
+
120
+ it 'should throw an error on the instance level' do
121
+ document_file = MyDocument.new('./test/documents/2010-08-08-test-document-file.textile')
122
+ assert_raises(NoMethodError) { document_file.hululu }
123
+ end
124
+ end
125
+
126
+ describe 'when reloading all document_files' do
127
+ before do
128
+ @default_dir = testdir + '/documents'
129
+ MyDocument.documents_dir = @default_dir
130
+ MyDocument.reload!
131
+ @document_files_before = MyDocument.all
132
+ @tmp_dir = "#{@default_dir}-#{Time.now.to_i}-#{rand(999999)}-test"
133
+ FileUtils.cp_r @default_dir, @tmp_dir
134
+ end
135
+
136
+ after do
137
+ FileUtils.rm_r(@tmp_dir) if Dir.exist?(@tmp_dir)
138
+ end
139
+
140
+ it 'should get updated document_files' do
141
+ updated_document_file = <<-eos
142
+ ---
143
+ id: 1
144
+ title: The shuzzle!
145
+ tags: [tig]
146
+ number_of_foos: 48
147
+ ---
148
+
149
+ I like the foos.
150
+ eos
151
+ document_file_file_name = "#{@tmp_dir}/2010-08-08-test-document-file.textile"
152
+ File.open(document_file_file_name, 'w') {|f| f.write(updated_document_file) }
153
+ MyDocument.documents_dir = @tmp_dir
154
+ MyDocument.reload!
155
+ document_files_after = MyDocument.all
156
+
157
+ assert_equal @document_files_before.first.id, document_files_after.first.id
158
+ refute_equal @document_files_before.first.title, document_files_after.first.title
159
+ refute_equal @document_files_before.first.tags, document_files_after.first.tags
160
+ refute_equal @document_files_before.first.number_of_foos, document_files_after.first.number_of_foos
161
+ refute_equal @document_files_before.first.content, document_files_after.first.content
162
+ end
163
+
164
+ it 'should get new document_files' do
165
+ new_document_file = <<-eos
166
+ ---
167
+ id: 3
168
+ title: The shuzzle!
169
+ tags: [tig]
170
+ number_of_foos: 48
171
+ ---
172
+
173
+ I like the cows.
174
+ eos
175
+ document_file_file_name = "#{@tmp_dir}/2010-08-15-new-test-document_file.textile"
176
+ File.open(document_file_file_name, 'w') {|f| f.write(new_document_file) }
177
+ MyDocument.documents_dir = @tmp_dir
178
+ MyDocument.reload!
179
+ document_files_after = MyDocument.all
180
+
181
+ assert_equal @document_files_before.size + 1, document_files_after.size
182
+ assert_equal 'The shuzzle!', document_files_after.last.title
183
+ assert_equal "I like the cows.\n", document_files_after.last.content
184
+ end
185
+
186
+ it 'should not change if no document_files were changed' do
187
+ MyDocument.reload!
188
+ document_files_after = MyDocument.all
189
+ assert_equal @document_files_before.map(&:id), document_files_after.map(&:id)
190
+ end
191
+
192
+ it 'should not show deleted document_files' do
193
+ document_file_file_name = "#{@tmp_dir}/2010-08-08-test-document-file.textile"
194
+ FileUtils.rm document_file_file_name
195
+ MyDocument.documents_dir = @tmp_dir
196
+ MyDocument.reload!
197
+ document_files_after = MyDocument.all
198
+ refute_equal @document_files_before.map(&:id), document_files_after.map(&:id)
199
+ end
200
+ end
201
+ end
202
+
@@ -0,0 +1,8 @@
1
+ ---
2
+ id: 1
3
+ title: The shizzle!
4
+ tags: [tag]
5
+ number_of_foos: 42
6
+ ---
7
+
8
+ I like the flowers.
@@ -0,0 +1,7 @@
1
+ ---
2
+ id: 2
3
+ title: The big pink Oink!
4
+ tags: [tag, tug]
5
+ ---
6
+
7
+ I'm on a horse!
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: document_file
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Ralph von der Heyden
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-19 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: " Makes your plain text files accessible in Ruby. Supports YAML front matter.\n"
22
+ email: ralph@rvdh.de
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - README
31
+ - Rakefile
32
+ - LICENSE
33
+ - lib/document_file/base.rb
34
+ - lib/document_file/version.rb
35
+ - lib/document_file.rb
36
+ - test/document_file_test.rb
37
+ - test/documents/2010-08-08-test-document-file.textile
38
+ - test/documents/2010-08-09-oink-post.textile
39
+ has_rdoc: true
40
+ homepage: http://github.com/ralph/document_file
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.3.7
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Write documents in your fav editor. Read them in your Ruby app.
71
+ test_files: []
72
+