yaml_record 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,74 +2,174 @@
2
2
 
3
3
  ## Introduction ##
4
4
 
5
- YAML Record allows you to persist data to a yaml file and manage it with activemodel API
5
+ YAML Record is a data persistence library that complies with the ActiveModel API. Using YAMLRecord should be familiar to anyone that has used ActiveRecord before to manage your database. Using this library, the data is persisted in a YAML backed file.
6
+
7
+ ## Rationale ##
8
+
9
+ *Why a YAML-based persistence store?* In certain situations, there are collections of simple data in which there are very few records which are by nature infrequently accessed and that are ideally able to be scanned easily within a text file. These can include a simple contact form, landing page interest, feedback forms, surveys, team pages, etc where there is simply no need for the overhead of a fully persisted database solution.
10
+
11
+ There are many cases where YAMLRecord is **not the correct** persistence strategy. Any collection that is going to have substantial number of records, will be frequently updated, or is accessible by a large volume of users should not be stored in a YAML text file for obvious reasons. However, for specific cases, the convenience of storing things in a simple text file becomes apparent. Being able to access the text file data as if the records were in a familiar database ORM has many conveniences and advantages such as keeping the controllers standard and leveraging existing ORM knowledge.
6
12
 
7
13
  ## Installation ##
8
14
 
15
+ Install using rubygems:
16
+
9
17
  gem install yaml_record
10
-
11
- ## Usage ##
12
18
 
13
- To use YAML Record add gem to your Gemfile
19
+ Or add gem to your Gemfile:
14
20
 
15
21
  # Gemfile
16
22
  gem 'yaml_record'
17
-
18
- Next define your YAML Record class
19
-
20
- class Post < YamlRecord::Base
21
- # Declare your properties
22
- properties :title, :body, :user_id
23
-
24
- # Declare source file path
25
- source Rails.root.join("config/posts")
26
- end
27
-
28
- Use as any activerecord object.
29
-
30
- Retrieve all items:
23
+
24
+ ## Usage ##
25
+
26
+ ### Declaration ###
27
+
28
+ Create any ruby object and inherit from `YamlRecord:Base` to define a type:
29
+
30
+ ```ruby
31
+ class Post < YamlRecord::Base
32
+ # Declare your properties
33
+ properties :title, :body, :user_id
34
+
35
+ # Declare source file path
36
+ source Rails.root.join("config/posts")
37
+ end
38
+ ```
39
+
40
+ Use this new object the same way as any ActiveRecord object.
41
+
42
+ ### Retrieval ###
43
+
44
+ Retrieve the collection:
31
45
 
32
46
  Post.all => [@p1, @p2]
33
-
34
- Retrieve item by ID:
47
+
48
+ Retrieve item by id:
35
49
 
36
50
  Post.find("a1b2") => @p1
37
-
38
- Retrieve by attribute
51
+
52
+ Retrieve by attribute:
39
53
 
40
54
  Post.find_by_attribute(:title, "some title") => @p
41
-
55
+
56
+ ### Create ###
57
+
42
58
  Initialize post:
43
59
 
44
60
  @p = Post.new(:title => "...", :body => "...", :user_id => 5)
45
-
46
- Save post:
47
-
48
61
  @p.save
49
- # or Post.create(:title => "...", :body => "...", :user_id => 5)
50
-
51
- Access attributes
62
+
63
+ Create post:
64
+
65
+ @p = Post.create(:title => "...", :body => "...", :user_id => 6)
66
+
67
+ ### Update ###
68
+
69
+ Update attributes using the expected method:
70
+
71
+ @p.update_attributes(:title => "new title")
72
+
73
+ ### Destroy ###
74
+
75
+ Destroy a given record:
76
+
77
+ @p.destroy
78
+
79
+ ### Access ###
80
+
81
+ Access attributes:
52
82
 
53
83
  @p = Post.find("a1b2")
54
84
  @p.title => "..."
55
-
56
- Assign attributes
85
+
86
+ Assign attributes:
57
87
 
58
88
  @p.title = "new title"
89
+ @p.save
59
90
 
60
- Update attributes
91
+ ### Callbacks ###
61
92
 
62
- @p.update_attributes(:title => "new title")
63
-
64
- Destroy record
93
+ Create callbacks:
65
94
 
66
- @p.destroy
95
+ ```ruby
96
+ class Submission < YamlRecord::Base
97
+ # ...
98
+ before_create :do_something # or before_save, before_destroy, ...
99
+
100
+ def do_something
101
+ # something here
102
+ end
103
+ end
104
+ ```
105
+
106
+ ## Example ##
107
+
108
+ Imagine a simple contact form that accepts a name and email from a user along with a body:
109
+
110
+ ```ruby
111
+ class Submission < YamlRecord::Base
112
+ # Declare your properties
113
+ properties :name, :email, :body
114
+
115
+ # Declare source file path (config/contact.yml)
116
+ source Rails.root.join("config/contact")
117
+ end
118
+ ```
119
+
120
+ Once we define the Contact model, we can setup a controller and form just the same as in ActiveRecord:
121
+
122
+ ```ruby
123
+ class SubmissionsController < AC::Base
124
+ def create
125
+ @submission = Submission.create(params[:submission])
126
+ end
127
+
128
+ def index
129
+ @submissions = Submission.all
130
+ end
131
+
132
+ def show
133
+ @submission = Submission.find(params[:id])
134
+ end
135
+
136
+ def update
137
+ @submission = Submission.find(params[:id])
138
+ @submission.update_attributes(params[:submission])
139
+ end
140
+
141
+ def destroy
142
+ @submission = Submission.find(params[:id])
143
+ @submission.destroy
144
+ end
145
+ end
146
+ ```
147
+
148
+ As you can see the controller appears the same as any ActiveRecord controller would and this makes managing the YAML data easy and convenient. You can even define callbacks in your object as you would in ActiveRecord:
149
+
150
+ ```ruby
151
+ class Submission < YamlRecord::Base
152
+ # ...
153
+ before_create :do_something # or before_save, before_destroy, ...
154
+
155
+ def do_something
156
+ # something here
157
+ end
158
+ end
159
+ ```
160
+
161
+ And that's all! Each record will be persisted to the source file for easy access.
162
+
163
+ ## Issues ##
164
+
165
+ * Validations should be supported `validates_presence_of :name`
166
+ * Property type declarations should be available `property :age, Integer`
67
167
 
68
168
  ## Contributors ##
69
169
 
70
170
  Created at Miso by Nico Taing and Nathan Esquenazi
71
171
 
72
- Contributors are welcome!
172
+ Contributors and patches are welcome! Please send a pull request!
73
173
 
74
174
  ## License ##
75
175
 
@@ -1,17 +1,17 @@
1
1
  module YamlRecord
2
- class Base
3
- attr_accessor :attributes, :is_created, :is_destroyed
4
-
2
+ class Base
3
+ attr_accessor :attributes, :is_created, :is_destroyed
4
+
5
5
  include ActiveSupport::Callbacks
6
6
  define_callbacks :before_save, :after_save, :before_destroy, :after_destroy, :before_validation, :before_create, :after_create
7
-
7
+
8
8
  before_create :set_id!
9
9
 
10
10
  # Constructs a new YamlRecord instance based on specified attribute hash
11
11
  #
12
12
  # === Example:
13
13
  #
14
- # class Post < YamlRecord::Base; properties :foo; end
14
+ # class Post < YamlRecord::Base; properties :foo; end
15
15
  #
16
16
  # Post.new(:foo => "bar")
17
17
  #
@@ -21,14 +21,13 @@ module YamlRecord
21
21
 
22
22
  self.attributes ||= {}
23
23
  self.is_created = attr_hash.delete(:persisted) || false
24
- self.setup_properties!
25
24
  attr_hash.each do |k,v|
26
25
  self.send("#{k}=", v) # self.attributes[:media] = "foo"
27
26
  end
28
27
  end
29
28
 
30
29
  # Accesses given attribute from YamlRecord instance
31
- #
30
+ #
32
31
  # === Example:
33
32
  #
34
33
  # @post[:foo] => "bar"
@@ -38,7 +37,7 @@ module YamlRecord
38
37
  end
39
38
 
40
39
  # Assign given attribute from YamlRecord instance with specified value
41
- #
40
+ #
42
41
  # === Example:
43
42
  #
44
43
  # @post[:foo] = "baz"
@@ -50,7 +49,7 @@ module YamlRecord
50
49
  # Saved YamlRecord instance to file
51
50
  # Executes save and create callbacks
52
51
  # Returns true if record saved; false otherwise
53
- #
52
+ #
54
53
  # === Example:
55
54
  #
56
55
  # @post.save => true
@@ -61,7 +60,7 @@ module YamlRecord
61
60
 
62
61
  existing_items = self.class.all
63
62
  if self.new_record?
64
- existing_items << self
63
+ existing_items << self
65
64
  else # update existing record
66
65
  updated_item = existing_items.find { |item| item.id == self.id }
67
66
  return false unless updated_item
@@ -69,7 +68,7 @@ module YamlRecord
69
68
  end
70
69
 
71
70
  raw_data = existing_items ? existing_items.map { |item| item.persisted_attributes } : []
72
- self.class.write_contents(raw_data) if self.valid?
71
+ self.class.write_contents(raw_data) if self.valid?
73
72
 
74
73
  run_callbacks(:after_create) unless self.is_created
75
74
  run_callbacks(:after_save)
@@ -77,10 +76,10 @@ module YamlRecord
77
76
  rescue IOError
78
77
  false
79
78
  end
80
-
79
+
81
80
  # Update YamlRecord instance with specified attributes
82
81
  # Returns true if record updated; false otherwise
83
- #
82
+ #
84
83
  # === Example:
85
84
  #
86
85
  # @post.update_attributes(:foo => "baz", :miso => "awesome") => true
@@ -89,9 +88,9 @@ module YamlRecord
89
88
  updated_attrs.each { |k,v| self.send("#{k}=", v) }
90
89
  self.save
91
90
  end
92
-
91
+
93
92
  # Returns array of instance attributes names; An attribute is a value stored for this record (persisted or not)
94
- #
93
+ #
95
94
  # === Example:
96
95
  #
97
96
  # @post.column_names => ["foo", "miso"]
@@ -101,22 +100,22 @@ module YamlRecord
101
100
  self.attributes.each_key { |k| array << k.to_s }
102
101
  array
103
102
  end
104
-
103
+
105
104
  # Returns hash of attributes to be persisted to file.
106
105
  # A persisted attribute is a value stored in the file (specified with the properties declaration)
107
- #
106
+ #
108
107
  # === Example:
109
108
  #
110
- # class Post < YamlRecord::Base; properties :foo, :miso; end
109
+ # class Post < YamlRecord::Base; properties :foo, :miso; end
111
110
  # @post = Post.create(:foo => "bar", :miso => "great")
112
111
  # @post.persisted_attributes => { :id => "a1b2c3", :foo => "bar", :miso => "great" }
113
112
  #
114
113
  def persisted_attributes
115
114
  self.attributes.slice(*self.class.properties).reject { |k, v| v.nil? }
116
115
  end
117
-
116
+
118
117
  # Returns true if YamlRecord instance hasn't persisted; false otherwise
119
- #
118
+ #
120
119
  # === Example:
121
120
  #
122
121
  # @post = Post.new(:foo => "bar", :miso => "great")
@@ -129,7 +128,7 @@ module YamlRecord
129
128
  end
130
129
 
131
130
  # Returns true if YamlRecord instance has been destroyed; false otherwise
132
- #
131
+ #
133
132
  # === Example:
134
133
  #
135
134
  # @post = Post.new(:foo => "bar", :miso => "great")
@@ -144,7 +143,7 @@ module YamlRecord
144
143
 
145
144
  # Remove a persisted YamlRecord object
146
145
  # Returns true if destroyed; false otherwise
147
- #
146
+ #
148
147
  # === Example:
149
148
  #
150
149
  # @post = Post.create(:foo => "bar", :miso => "great")
@@ -162,11 +161,11 @@ module YamlRecord
162
161
  rescue IOError
163
162
  false
164
163
  end
165
-
164
+
166
165
  # Execute validations for instance
167
166
  # Returns true if record is valid; false otherwise
168
167
  # TODO Implement validation
169
- #
168
+ #
170
169
  # === Example:
171
170
  #
172
171
  # @post.valid? => true
@@ -174,10 +173,10 @@ module YamlRecord
174
173
  def valid?
175
174
  true
176
175
  end
177
-
176
+
178
177
  # Returns errors messages if record isn't valid; empty array otherwise
179
178
  # TODO Implement validation
180
- #
179
+ #
181
180
  # === Example:
182
181
  #
183
182
  # @post.errors => ["Foo can't be blank"]
@@ -185,10 +184,10 @@ module YamlRecord
185
184
  def errors
186
185
  []
187
186
  end
188
-
187
+
189
188
  # Returns YamlRecord Instance
190
189
  # Complies with ActiveModel api
191
- #
190
+ #
192
191
  # === Example:
193
192
  #
194
193
  # @post.to_model => @post
@@ -196,9 +195,9 @@ module YamlRecord
196
195
  def to_model
197
196
  self
198
197
  end
199
-
198
+
200
199
  # Reload YamlRecord instance attributes from file
201
- #
200
+ #
202
201
  # === Example:
203
202
  #
204
203
  # @post = Post.create(:foo => "bar", :miso => "great")
@@ -214,7 +213,7 @@ module YamlRecord
214
213
 
215
214
  # Find YamlRecord instance given attribute name and expected value
216
215
  # Returns instance if found; false otherwise
217
- #
216
+ #
218
217
  # === Example:
219
218
  #
220
219
  # Post.find_by_attribute(:foo, "bar") => @post
@@ -222,17 +221,17 @@ module YamlRecord
222
221
  def self.find_by_attribute(attribute, expected_value)
223
222
  self.all.find do |record|
224
223
  value = record.send(attribute) if record.respond_to?(attribute)
225
- value.is_a?(Array) ?
224
+ value.is_a?(Array) ?
226
225
  value.include?(expected_value) :
227
226
  value == expected_value
228
227
  end
229
228
  end
230
-
229
+
231
230
  class << self;
232
-
231
+
233
232
  # Find YamlRecord instance given id
234
233
  # Returns instance if found; false otherwise
235
- #
234
+ #
236
235
  # === Example:
237
236
  #
238
237
  # Post.find_by_id("a1b2c3") => @post
@@ -242,10 +241,10 @@ module YamlRecord
242
241
  end
243
242
  alias :find :find_by_id
244
243
  end
245
-
244
+
246
245
  # Returns collection of all YamlRecord instances
247
246
  # Caches results during request
248
- #
247
+ #
249
248
  # === Example:
250
249
  #
251
250
  # Post.all => [@post1, @post2, ...]
@@ -259,7 +258,7 @@ module YamlRecord
259
258
 
260
259
  # Find last YamlRecord instance given a limit
261
260
  # Returns an array of instances if found; empty otherwise
262
- #
261
+ #
263
262
  # === Example:
264
263
  #
265
264
  # Post.last => @post6
@@ -271,7 +270,7 @@ module YamlRecord
271
270
 
272
271
  # Find first YamlRecord instance given a limit
273
272
  # Returns an array of instances if found; empty otherwise
274
- #
273
+ #
275
274
  # === Example:
276
275
  #
277
276
  # Post.first => @post
@@ -280,60 +279,66 @@ module YamlRecord
280
279
  def self.first(limit=1)
281
280
  limit == 1 ? self.all.first : self.all.first(limit)
282
281
  end
283
-
282
+
284
283
  # Initializes YamlRecord instance given an attribute hash and saves afterwards
285
284
  # Returns instance if successfully saved; false otherwise
286
- #
285
+ #
287
286
  # === Example:
288
287
  #
289
288
  # Post.create(:foo => "bar", :miso => "great") => @post
290
289
  #
291
290
  def self.create(attributes={})
292
291
  @fs = self.new(attributes)
293
- if @fs.save == true
292
+ if @fs.save == true
294
293
  @fs.is_created = true;
295
294
  @fs
296
295
  else
297
296
  false
298
297
  end
299
298
  end
300
-
299
+
301
300
  # Declares persisted attributes for YamlRecord class
302
- #
301
+ #
303
302
  # === Example:
304
303
  #
305
304
  # class Post < YamlRecord::Base; properties :foo, :miso; end
306
305
  # Post.create(:foo => "bar", :miso => "great") => @post
307
306
  #
308
307
  def self.properties(*names)
309
- names = names | [:id] if names.size > 0
310
- names.size == 0 ? @_properties : @_properties = names
308
+ @_properties ||= []
309
+ if names.size == 0 # getter
310
+ @_properties
311
+ elsif names.size > 0 # setter
312
+ names = names | [:id]
313
+ setup_properties!(*names)
314
+ @_properties += names
315
+ end
311
316
  end
312
-
317
+
313
318
  # Declares source file for YamlRecord class
314
- #
319
+ #
315
320
  # === Example:
316
321
  #
317
- # class Post < YamlRecord::Base
322
+ # class Post < YamlRecord::Base
318
323
  # source "path/to/yaml/file"
319
324
  # end
320
325
  #
321
326
  def self.source(file=nil)
322
327
  file ? @file = (file.to_s + ".yml") : @file
323
328
  end
324
-
329
+
325
330
  protected
326
-
331
+
327
332
  # Validates each persisted attributes
328
333
  # TODO Implement validation
329
334
  #
330
335
  def self.validates_each(*args, &block)
331
336
  true
332
337
  end
333
-
338
+
334
339
  # Write raw yaml data to file
335
340
  # Protected method, not called during usage
336
- #
341
+ #
337
342
  # === Example:
338
343
  #
339
344
  # Post.write_content([{ :foo => "bar"}, { :foo => "baz"}, ...]) # writes to source file
@@ -342,27 +347,26 @@ module YamlRecord
342
347
  File.open(self.source, 'w') {|f| f.write(raw_data.to_yaml) }
343
348
  @records = nil
344
349
  end
345
-
350
+
346
351
  # Creates reader and writer methods for each persisted attribute
347
352
  # Protected method, not called during usage
348
- #
353
+ #
349
354
  # === Example:
350
355
  #
351
- # Post.setup_properties!
356
+ # Post.setup_properties!(:foo)
352
357
  # @post.foo = "baz"
353
358
  # @post.foo => "baz"
354
359
  #
355
- def setup_properties!
356
- self.class.properties.each do |name|
357
- self.class_eval do
358
- define_method(name) { self[name.to_sym] } # def media; self.attributes[:media]; end
359
- define_method("#{name}=") { |val| self[name.to_sym] = val } # def media; self.attributes[:media]; end
360
- end
360
+ def self.setup_properties!(*names)
361
+ names.each do |name|
362
+ define_method(name) { self[name.to_sym] }
363
+ define_method("#{name}=") { |val| self[name.to_sym] = val }
361
364
  end
362
365
  end
363
-
366
+
364
367
  # Assign YamlRecord a unique id if not set
365
368
  # Invoke before create of an instance
369
+ # Protected method, not called during usage
366
370
  #
367
371
  def set_id!
368
372
  self.id = ActiveSupport::SecureRandom.hex(15)
@@ -1,3 +1,3 @@
1
1
  module YamlRecord
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yaml_record
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nico Taing
@@ -16,8 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-07-11 00:00:00 -07:00
20
- default_executable:
19
+ date: 2011-07-12 00:00:00 Z
21
20
  dependencies:
22
21
  - !ruby/object:Gem::Dependency
23
22
  name: activesupport
@@ -86,7 +85,6 @@ files:
86
85
  - test/base_test.rb
87
86
  - test/test_helper.rb
88
87
  - yaml_record.gemspec
89
- has_rdoc: true
90
88
  homepage: https://github.com/nico-taing/yaml_record
91
89
  licenses: []
92
90
 
@@ -116,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
114
  requirements: []
117
115
 
118
116
  rubyforge_project: yaml_record
119
- rubygems_version: 1.6.2
117
+ rubygems_version: 1.7.2
120
118
  signing_key:
121
119
  specification_version: 3
122
120
  summary: YAML file persistence engine