zilkey-active_hash 0.1.0 → 0.2.0

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/CHANGELOG ADDED
@@ -0,0 +1,5 @@
1
+ 2009-07-23
2
+ - Added support for auto-defining methods based on hash keys in ActiveHash::Base
3
+ - Changed the :field and :fields API so that they don't overwrite existing methods (useful when ActiveHash auto-defines methods)
4
+ - Fixed a bug where ActiveFile incorrectly set the root_path to be the path in the gem directory, not the current working directory
5
+
data/README.md CHANGED
@@ -22,8 +22,8 @@ ActiveHash also ships with:
22
22
  To use ActiveHash, you need to:
23
23
 
24
24
  * Inherit from ActiveHash::Base
25
- * Define your fields
26
25
  * Define your data
26
+ * (optionally) Define your fields and/or default values
27
27
 
28
28
  A quick example would be:
29
29
 
@@ -35,12 +35,33 @@ A quick example would be:
35
35
  ]
36
36
  end
37
37
 
38
- ## Defining Fields
38
+ ## Auto-Defined fields
39
39
 
40
- You can define fields in 2 ways, using the :fields method, or using the :field method, which allows you to specify a default value for the field:
40
+ ActiveHash will auto-define all fields for you when you load the hash. For example, if you have the following class:
41
+
42
+ class CustomField < ActiveYaml::Base
43
+ self.data = [
44
+ {:custom_field_1 => "foo"},
45
+ {:custom_field_2 => "foo"},
46
+ {:custom_field_3 => "foo"}
47
+ ]
48
+ end
49
+
50
+ Once you call CustomField.all it will define methods for :custom_field_1, :custom_field_2 etc...
51
+
52
+ If you need the fields at load time, as opposed to after .all is called, you can also define them manually, like so:
53
+
54
+ class CustomField < ActiveYaml::Base
55
+ fields :custom_field_1, :custom_field_2, :custom_field_3
56
+ end
57
+
58
+ NOTE: auto-defined fields will _not_ override fields you've defined, either on the class or on the instance.
59
+
60
+ ## Defining Fields with default values
61
+
62
+ If some of your hash values contain nil, and you want to provide a default, you can specify defaults with the :field method:
41
63
 
42
64
  class Country < ActiveHash::Base
43
- fields :name, :population
44
65
  field :is_axis_of_evil, :default => false
45
66
  end
46
67
 
@@ -50,7 +71,6 @@ You can define data inside your class or outside. For example, you might have a
50
71
 
51
72
  # app/models/country.rb
52
73
  class Country < ActiveHash::Base
53
- fields :name, :population
54
74
  end
55
75
 
56
76
  # config/initializers/data.rb
@@ -102,7 +122,6 @@ ActiveHash also gives you methods related to the fields you defined. For exampl
102
122
  You can create .belongs_to associations from rails objects, like so:
103
123
 
104
124
  class Country < ActiveHash::Base
105
- fields :name, :population
106
125
  end
107
126
 
108
127
  class Person < ActiveRecord::Base
@@ -118,7 +137,6 @@ You can also use standard rails view helpers, like #collection_select:
118
137
  If you want to store your data in YAML files, just inherit from ActiveYaml and specify your path information:
119
138
 
120
139
  class Country < ActiveYaml::Base
121
- field :name
122
140
  end
123
141
 
124
142
  By default, this class will look for a yml file named "countries.yml" in the same directory as the file. You can either change the directory it looks in, the filename it looks for, or both:
@@ -126,13 +144,14 @@ By default, this class will look for a yml file named "countries.yml" in the sam
126
144
  class Country < ActiveYaml::Base
127
145
  set_root_path "/u/data"
128
146
  set_filename "sample"
129
- field :name
130
147
  end
131
148
 
132
149
  The above example will look for the file "/u/data/sample.yml".
133
150
 
134
151
  ActiveYaml, as well as ActiveFile, check the mtime of the file you specified, and reloads the data if the mtime has changed. So you can replace the data in the files even if your app is running in production mode in rails.
135
152
 
153
+ Since ActiveYaml just creates a hash from the YAML file, you will have all fields specified in YAML auto-defined for you once you call all.
154
+
136
155
  ## ActiveFile
137
156
 
138
157
  If you store encrypted data, or you'd like to store your flat files as CSV or XML or any other format, you can easily extend ActiveHash to parse and load your file. Just add a custom ::load_file method, and define the extension you want the file to use:
@@ -140,7 +159,6 @@ If you store encrypted data, or you'd like to store your flat files as CSV or XM
140
159
  class Country < ActiveFile::Base
141
160
  set_root_path "/u/data"
142
161
  set_filename "sample"
143
- field :name
144
162
 
145
163
  class << self
146
164
  def extension
@@ -153,7 +171,9 @@ If you store encrypted data, or you'd like to store your flat files as CSV or XM
153
171
  end
154
172
  end
155
173
 
156
- The two methods you need to implement are load_file, which needs to return a hash, and .extension, which returns the file extension you are using. You have full_path available to you if you wish, or you can provide your own path.
174
+ The two methods you need to implement are load_file, which needs to return an array of hashes, and .extension, which returns the file extension you are using. You have full_path available to you if you wish, or you can provide your own path.
175
+
176
+ NOTE: By default, .full_path refers to the current working directory. In a rails app, this will be RAILS_ROOT.
157
177
 
158
178
  ## Authors
159
179
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -0,0 +1,58 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{active_hash}
5
+ s.version = "0.2.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Jeff Dean", "Mike Dalessio"]
9
+ s.date = %q{2009-07-23}
10
+ s.email = %q{jeff@zilkey.com}
11
+ s.extra_rdoc_files = [
12
+ "LICENSE",
13
+ "README.md"
14
+ ]
15
+ s.files = [
16
+ ".document",
17
+ ".gitignore",
18
+ "CHANGELOG",
19
+ "LICENSE",
20
+ "README.md",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "active_hash.gemspec",
24
+ "lib/active_file/base.rb",
25
+ "lib/active_hash.rb",
26
+ "lib/active_hash/base.rb",
27
+ "lib/active_yaml/base.rb",
28
+ "spec/active_file/base_spec.rb",
29
+ "spec/active_hash/base_spec.rb",
30
+ "spec/active_yaml/base_spec.rb",
31
+ "spec/active_yaml/sample.yml",
32
+ "spec/spec_helper.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/zilkey/active_hash}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.3}
38
+ s.summary = %q{An ActiveRecord-like model that uses a hash as a datasource}
39
+ s.test_files = [
40
+ "spec/active_file/base_spec.rb",
41
+ "spec/active_hash/base_spec.rb",
42
+ "spec/active_yaml/base_spec.rb",
43
+ "spec/spec_helper.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
52
+ else
53
+ s.add_dependency(%q<activesupport>, [">= 0"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<activesupport>, [">= 0"])
57
+ end
58
+ end
@@ -33,8 +33,6 @@ module ActiveFile
33
33
  raise "Override Me"
34
34
  end
35
35
 
36
- protected :load_file
37
-
38
36
  def extension
39
37
  raise "Override Me"
40
38
  end
@@ -42,13 +40,11 @@ module ActiveFile
42
40
  protected :extension
43
41
 
44
42
  def full_path
45
- root_path = read_inheritable_attribute(:root_path) || File.dirname(__FILE__)
43
+ root_path = read_inheritable_attribute(:root_path) || Dir.pwd
46
44
  filename = read_inheritable_attribute(:filename) || name.tableize
47
45
  File.join(root_path, "#{filename}.#{extension}")
48
46
  end
49
47
 
50
- private :full_path
51
-
52
48
  def should_reload?
53
49
  if (mtime = File.mtime(full_path)) == read_inheritable_attribute(:cached_mtime)
54
50
  false
@@ -9,7 +9,12 @@ module ActiveHash
9
9
  end
10
10
 
11
11
  def all
12
- @records ||= read_inheritable_attribute(:data).collect {|hash| new(hash)}
12
+ unless @records
13
+ records = read_inheritable_attribute(:data)
14
+ @records = records.collect {|hash| new(hash)}
15
+ auto_assign_fields( records )
16
+ end
17
+ @records
13
18
  end
14
19
 
15
20
  def count
@@ -48,25 +53,33 @@ module ActiveHash
48
53
  end
49
54
 
50
55
  def define_getter_method(field, default_value)
51
- define_method field do
52
- attributes[field] || default_value
56
+ unless instance_methods.include?(field.to_s)
57
+ define_method(field) do
58
+ attributes[field] || default_value
59
+ end
53
60
  end
54
61
  end
55
62
 
56
63
  private :define_getter_method
57
64
 
58
65
  def define_interrogator_method(field)
59
- define_method "#{field}?" do
60
- attributes[field].present?
66
+ method_name = "#{field}?"
67
+ unless instance_methods.include?(method_name)
68
+ define_method(method_name) do
69
+ attributes[field].present?
70
+ end
61
71
  end
62
72
  end
63
73
 
64
74
  private :define_interrogator_method
65
75
 
66
76
  def define_custom_find_method(field_name)
67
- meta_class.instance_eval do
68
- define_method "find_by_#{field_name}" do |name|
69
- all.detect {|record| record.send(field_name) == name }
77
+ method_name = "find_by_#{field_name}"
78
+ unless singleton_methods.include?(method_name)
79
+ metaclass.instance_eval do
80
+ define_method(method_name) do |name|
81
+ all.detect {|record| record.send(field_name) == name }
82
+ end
70
83
  end
71
84
  end
72
85
  end
@@ -74,22 +87,35 @@ module ActiveHash
74
87
  private :define_custom_find_method
75
88
 
76
89
  def define_custom_find_all_method(field_name)
77
- meta_class.instance_eval do
78
- define_method "find_all_by_#{field_name}" do |name|
79
- all.select {|record| record.send(field_name) == name }
90
+ method_name = "find_all_by_#{field_name}"
91
+ unless singleton_methods.include?(method_name)
92
+ metaclass.instance_eval do
93
+ unless singleton_methods.include?(method_name)
94
+ define_method(method_name) do |name|
95
+ all.select {|record| record.send(field_name) == name }
96
+ end
97
+ end
80
98
  end
81
99
  end
82
100
  end
83
101
 
84
102
  private :define_custom_find_all_method
85
103
 
86
- def meta_class
87
- class << self
88
- self
104
+ def auto_assign_fields(array_of_hashes)
105
+ array_of_hashes.inject([]) do |array, row|
106
+ row.symbolize_keys!
107
+ row.keys.each do |key|
108
+ unless key.to_s == "id"
109
+ array << key
110
+ end
111
+ end
112
+ array
113
+ end.uniq.each do |key|
114
+ field key
89
115
  end
90
116
  end
91
117
 
92
- private :meta_class
118
+ private :auto_assign_fields
93
119
 
94
120
  end
95
121
 
@@ -9,6 +9,7 @@ module ActiveYaml
9
9
  def extension
10
10
  "yml"
11
11
  end
12
+
12
13
  end
13
14
  end
14
15
 
@@ -70,6 +70,16 @@ describe ActiveFile::Base do
70
70
  end
71
71
  end
72
72
 
73
+ describe ".full_path" do
74
+ it "defaults to the directory of the calling file" do
75
+ class FileShouldBeHere < ActiveFile::Base
76
+ def self.extension() "foo" end
77
+ end
78
+
79
+ FileShouldBeHere.full_path.should == "#{Dir.pwd}/file_should_be_heres.foo"
80
+ end
81
+ end
82
+
73
83
  describe ".all" do
74
84
  before do
75
85
  class MyClass
@@ -1,12 +1,16 @@
1
1
  require 'spec/spec_helper'
2
2
 
3
- describe ActiveHash::Base do
3
+ describe ActiveHash, "Base" do
4
4
 
5
5
  before do
6
6
  class Country < ActiveHash::Base
7
7
  end
8
8
  end
9
9
 
10
+ after do
11
+ Object.send :remove_const, :Country
12
+ end
13
+
10
14
  describe ".fields" do
11
15
  before do
12
16
  Country.fields :name, :iso_name
@@ -411,4 +415,55 @@ describe ActiveHash::Base do
411
415
  end
412
416
  end
413
417
 
418
+ describe "auto-discovery of fields" do
419
+ it "dynamically creates fields for all keys in the hash" do
420
+ Country.data = [
421
+ {:field1 => "foo"},
422
+ {:field2 => "bar"},
423
+ {:field3 => "biz"}
424
+ ]
425
+
426
+ Country.all
427
+
428
+ [:field1, :field2, :field3].each do |field|
429
+ Country.should respond_to("find_by_#{field}")
430
+ Country.should respond_to("find_all_by_#{field}")
431
+ Country.new.should respond_to(field)
432
+ Country.new.should respond_to("#{field}?")
433
+ end
434
+ end
435
+
436
+ it "doesn't override methods already defined" do
437
+ Country.class_eval do
438
+ class << self
439
+ def find_by_name(name)
440
+ "find_by_name defined manually"
441
+ end
442
+
443
+ def find_all_by_name(name)
444
+ "find_all_by_name defined manually"
445
+ end
446
+ end
447
+
448
+ def name
449
+ "name defined manually"
450
+ end
451
+
452
+ def name?
453
+ "name? defined manually"
454
+ end
455
+ end
456
+
457
+ Country.data = [
458
+ {:name => "foo"}
459
+ ]
460
+
461
+ Country.all
462
+ Country.find_by_name("foo").should == "find_by_name defined manually"
463
+ Country.find_all_by_name("foo").should == "find_all_by_name defined manually"
464
+ Country.new.name.should == "name defined manually"
465
+ Country.new.name?.should == "name? defined manually"
466
+ end
467
+ end
468
+
414
469
  end
@@ -16,4 +16,5 @@ describe ActiveYaml::Base do
16
16
  records.first.name.should == "US"
17
17
  end
18
18
  end
19
+
19
20
  end
@@ -1,6 +1,15 @@
1
1
  - id: 1
2
2
  name: US
3
+ independence_date: 1776-07-04
4
+ created_at: Wed Jul 22 22:41:44 -0400 2009
5
+ custom_field_1: value1
3
6
  - id: 2
4
7
  name: Canada
8
+ independence_date: 1867-07-01
9
+ created_at: Wed Jul 22 22:41:44 -0400 2009
10
+ custom_field_2: value2
5
11
  - id: 3
6
12
  name: Mexico
13
+ independence_date: 1810-09-16
14
+ created_at: Wed Jul 22 22:41:44 -0400 2009
15
+ custom_field_3: value3
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zilkey-active_hash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dean
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-07-22 00:00:00 -07:00
13
+ date: 2009-07-23 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -35,10 +35,12 @@ extra_rdoc_files:
35
35
  files:
36
36
  - .document
37
37
  - .gitignore
38
+ - CHANGELOG
38
39
  - LICENSE
39
40
  - README.md
40
41
  - Rakefile
41
42
  - VERSION
43
+ - active_hash.gemspec
42
44
  - lib/active_file/base.rb
43
45
  - lib/active_hash.rb
44
46
  - lib/active_hash/base.rb