zilkey-active_hash 0.1.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/.document +5 -0
- data/.gitignore +6 -0
- data/LICENSE +20 -0
- data/README.md +168 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/lib/active_file/base.rb +66 -0
- data/lib/active_hash.rb +5 -0
- data/lib/active_hash/base.rb +132 -0
- data/lib/active_yaml/base.rb +15 -0
- data/spec/active_file/base_spec.rb +142 -0
- data/spec/active_hash/base_spec.rb +414 -0
- data/spec/active_yaml/base_spec.rb +19 -0
- data/spec/active_yaml/sample.yml +6 -0
- data/spec/spec_helper.rb +9 -0
- metadata +81 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 Jeff Dean
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# ActiveHash
|
|
2
|
+
|
|
3
|
+
ActiveHash is a simple base class that allows you to use a ruby hash as a readonly datasource for an ActiveRecord-like model.
|
|
4
|
+
|
|
5
|
+
ActiveHash assumes that every hash has an :id key, which is what you would probably store in a database. This allows you to seemlessly upgrade from ActiveHash objects to full ActiveRecord objects without having to change any code in your app, or any foreign keys in your database.
|
|
6
|
+
|
|
7
|
+
It also allows you to use #belongs_to in your AR objects.
|
|
8
|
+
|
|
9
|
+
ActiveHash can also be useful to create simple test classes that run without a database - ideal for testing plugins or gems that rely on simple AR behavior, but don't want to deal with databases or migrations for the spec suite.
|
|
10
|
+
|
|
11
|
+
ActiveHash also ships with:
|
|
12
|
+
|
|
13
|
+
* ActiveFile: a base class that will reload data from a flat file every time the flat file is changed
|
|
14
|
+
* ActiveYaml: a base class that will turn YAML into a hash and load the data into an ActiveHash object
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
sudo gem install zilkey-active_hash
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
To use ActiveHash, you need to:
|
|
23
|
+
|
|
24
|
+
* Inherit from ActiveHash::Base
|
|
25
|
+
* Define your fields
|
|
26
|
+
* Define your data
|
|
27
|
+
|
|
28
|
+
A quick example would be:
|
|
29
|
+
|
|
30
|
+
class Country < ActiveHash::Base
|
|
31
|
+
field :name
|
|
32
|
+
self.data = [
|
|
33
|
+
{:id => 1, :name => "US"},
|
|
34
|
+
{:id => 2, :name => "Canada"}
|
|
35
|
+
]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
## Defining Fields
|
|
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:
|
|
41
|
+
|
|
42
|
+
class Country < ActiveHash::Base
|
|
43
|
+
fields :name, :population
|
|
44
|
+
field :is_axis_of_evil, :default => false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
## Defining Data
|
|
48
|
+
|
|
49
|
+
You can define data inside your class or outside. For example, you might have a class like this:
|
|
50
|
+
|
|
51
|
+
# app/models/country.rb
|
|
52
|
+
class Country < ActiveHash::Base
|
|
53
|
+
fields :name, :population
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# config/initializers/data.rb
|
|
57
|
+
Country.data = [
|
|
58
|
+
{:id => 1, :name => "US"},
|
|
59
|
+
{:id => 2, :name => "Canada"}
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
If you prefer to store your data in YAML, see below.
|
|
63
|
+
|
|
64
|
+
## Class Methods
|
|
65
|
+
|
|
66
|
+
ActiveHash gives you ActiveRecord-esque methods like:
|
|
67
|
+
|
|
68
|
+
Country.all # => returns all Country objects
|
|
69
|
+
Country.count # => returns the length of the .data array
|
|
70
|
+
Country.first # => returns the first country object
|
|
71
|
+
Country.last # => returns the last country object
|
|
72
|
+
Country.find 1 # => returns the first country object with that id
|
|
73
|
+
Country.find [1,2] # => returns all Country objects with ids in the array
|
|
74
|
+
Country.find :all # => same as .all
|
|
75
|
+
Country.find :all, args # => the second argument is totally ignored, but allows it to play nicely with AR
|
|
76
|
+
Country.find_by_id 1 # => find the first object that matches the id
|
|
77
|
+
|
|
78
|
+
It also gives you a few dynamic finder methods. For example, if you defined :name as a field, you'd get:
|
|
79
|
+
|
|
80
|
+
Country.find_by_name "foo" # => returns the first object matching that name
|
|
81
|
+
Country.find_all_by_name "foo" # => returns an array of the objects with matching names
|
|
82
|
+
|
|
83
|
+
## Instance Methods
|
|
84
|
+
|
|
85
|
+
ActiveHash objects implement enough of the ActiveRecord api to satisfy most common needs. For example:
|
|
86
|
+
|
|
87
|
+
Country#id # => returns the numeric id or nil
|
|
88
|
+
Country#quoted_id # => returns the numeric id
|
|
89
|
+
Country#to_param # => returns the id as a string
|
|
90
|
+
Country#new_record? # => false
|
|
91
|
+
Country#readonly? # => true
|
|
92
|
+
Country#hash # => the hash of the id (or the hash of nil)
|
|
93
|
+
Country#eql? # => compares type and id, returns false if id is nil
|
|
94
|
+
|
|
95
|
+
ActiveHash also gives you methods related to the fields you defined. For example, if you defined :name as a field, you'd get:
|
|
96
|
+
|
|
97
|
+
Country#name # => returns the passed in name
|
|
98
|
+
Country#name? # => returns true if the name is not blank
|
|
99
|
+
|
|
100
|
+
## Integration with Rails
|
|
101
|
+
|
|
102
|
+
You can create .belongs_to associations from rails objects, like so:
|
|
103
|
+
|
|
104
|
+
class Country < ActiveHash::Base
|
|
105
|
+
fields :name, :population
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class Person < ActiveRecord::Base
|
|
109
|
+
belongs_to :country
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
You can also use standard rails view helpers, like #collection_select:
|
|
113
|
+
|
|
114
|
+
<%= collection_select :person, :country_id, Country.all, :id, :name %>
|
|
115
|
+
|
|
116
|
+
## ActiveYaml
|
|
117
|
+
|
|
118
|
+
If you want to store your data in YAML files, just inherit from ActiveYaml and specify your path information:
|
|
119
|
+
|
|
120
|
+
class Country < ActiveYaml::Base
|
|
121
|
+
field :name
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
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:
|
|
125
|
+
|
|
126
|
+
class Country < ActiveYaml::Base
|
|
127
|
+
set_root_path "/u/data"
|
|
128
|
+
set_filename "sample"
|
|
129
|
+
field :name
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
The above example will look for the file "/u/data/sample.yml".
|
|
133
|
+
|
|
134
|
+
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
|
+
|
|
136
|
+
## ActiveFile
|
|
137
|
+
|
|
138
|
+
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:
|
|
139
|
+
|
|
140
|
+
class Country < ActiveFile::Base
|
|
141
|
+
set_root_path "/u/data"
|
|
142
|
+
set_filename "sample"
|
|
143
|
+
field :name
|
|
144
|
+
|
|
145
|
+
class << self
|
|
146
|
+
def extension
|
|
147
|
+
".super_secret"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def load_file
|
|
151
|
+
MyAwesomeDecoder.load_file(full_path)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
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.
|
|
157
|
+
|
|
158
|
+
## Authors
|
|
159
|
+
|
|
160
|
+
Written by Mike Dalessio and Jeff Dean
|
|
161
|
+
|
|
162
|
+
## Development
|
|
163
|
+
|
|
164
|
+
The only thing I think I'd really like to add here is support for typecasting the fields.
|
|
165
|
+
|
|
166
|
+
== Copyright
|
|
167
|
+
|
|
168
|
+
Copyright (c) 2009 Jeff Dean. See LICENSE for details.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'jeweler'
|
|
6
|
+
Jeweler::Tasks.new do |gem|
|
|
7
|
+
gem.name = "active_hash"
|
|
8
|
+
gem.summary = %Q{An ActiveRecord-like model that uses a hash as a datasource}
|
|
9
|
+
gem.email = "jeff@zilkey.com"
|
|
10
|
+
gem.homepage = "http://github.com/zilkey/active_hash"
|
|
11
|
+
gem.authors = ["Jeff Dean", "Mike Dalessio"]
|
|
12
|
+
gem.add_dependency('activesupport')
|
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
rescue LoadError
|
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
require 'spec/rake/spectask'
|
|
21
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
|
22
|
+
spec.libs << 'lib' << 'spec'
|
|
23
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
|
27
|
+
spec.libs << 'lib' << 'spec'
|
|
28
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
|
29
|
+
spec.rcov = true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
task :default => :spec
|
|
34
|
+
|
|
35
|
+
require 'rake/rdoctask'
|
|
36
|
+
Rake::RDocTask.new do |rdoc|
|
|
37
|
+
if File.exist?('VERSION.yml')
|
|
38
|
+
config = YAML.load(File.read('VERSION.yml'))
|
|
39
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
|
40
|
+
else
|
|
41
|
+
version = ""
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
45
|
+
rdoc.title = "active_hash #{version}"
|
|
46
|
+
rdoc.rdoc_files.include('README*')
|
|
47
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
48
|
+
end
|
|
49
|
+
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.0
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module ActiveFile
|
|
2
|
+
|
|
3
|
+
class Base < ActiveHash::Base
|
|
4
|
+
class_inheritable_accessor :filename, :root_path, :cached_mtime
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
def all
|
|
8
|
+
reload
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def reload
|
|
13
|
+
if should_reload?
|
|
14
|
+
self.data = load_file
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
protected :reload
|
|
19
|
+
|
|
20
|
+
def set_filename(name)
|
|
21
|
+
write_inheritable_attribute :filename, name
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected :set_filename
|
|
25
|
+
|
|
26
|
+
def set_root_path(path)
|
|
27
|
+
write_inheritable_attribute :root_path, path
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
protected :set_root_path
|
|
31
|
+
|
|
32
|
+
def load_file
|
|
33
|
+
raise "Override Me"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
protected :load_file
|
|
37
|
+
|
|
38
|
+
def extension
|
|
39
|
+
raise "Override Me"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected :extension
|
|
43
|
+
|
|
44
|
+
def full_path
|
|
45
|
+
root_path = read_inheritable_attribute(:root_path) || File.dirname(__FILE__)
|
|
46
|
+
filename = read_inheritable_attribute(:filename) || name.tableize
|
|
47
|
+
File.join(root_path, "#{filename}.#{extension}")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private :full_path
|
|
51
|
+
|
|
52
|
+
def should_reload?
|
|
53
|
+
if (mtime = File.mtime(full_path)) == read_inheritable_attribute(:cached_mtime)
|
|
54
|
+
false
|
|
55
|
+
else
|
|
56
|
+
write_inheritable_attribute :cached_mtime, mtime
|
|
57
|
+
true
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private :should_reload?
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
data/lib/active_hash.rb
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module ActiveHash
|
|
2
|
+
class Base
|
|
3
|
+
class_inheritable_accessor :data
|
|
4
|
+
class << self
|
|
5
|
+
|
|
6
|
+
def data=(array_of_hashes)
|
|
7
|
+
@records = nil
|
|
8
|
+
write_inheritable_attribute(:data, array_of_hashes)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def all
|
|
12
|
+
@records ||= read_inheritable_attribute(:data).collect {|hash| new(hash)}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def count
|
|
16
|
+
all.length
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def find(id, *args)
|
|
20
|
+
case id
|
|
21
|
+
when :all
|
|
22
|
+
all
|
|
23
|
+
when Array
|
|
24
|
+
all.select {|record| id.map(&:to_i).include?(record.id) }
|
|
25
|
+
else
|
|
26
|
+
find_by_id(id)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def find_by_id(id)
|
|
31
|
+
all.detect {|record| record.id == id.to_i}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
delegate :first, :last, :to => :all
|
|
35
|
+
|
|
36
|
+
def fields(*args)
|
|
37
|
+
options = args.extract_options!
|
|
38
|
+
args.each do |field|
|
|
39
|
+
field(field, options)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def field(field_name, options = {})
|
|
44
|
+
define_getter_method(field_name, options[:default])
|
|
45
|
+
define_interrogator_method(field_name)
|
|
46
|
+
define_custom_find_method(field_name)
|
|
47
|
+
define_custom_find_all_method(field_name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def define_getter_method(field, default_value)
|
|
51
|
+
define_method field do
|
|
52
|
+
attributes[field] || default_value
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private :define_getter_method
|
|
57
|
+
|
|
58
|
+
def define_interrogator_method(field)
|
|
59
|
+
define_method "#{field}?" do
|
|
60
|
+
attributes[field].present?
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private :define_interrogator_method
|
|
65
|
+
|
|
66
|
+
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 }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private :define_custom_find_method
|
|
75
|
+
|
|
76
|
+
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 }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private :define_custom_find_all_method
|
|
85
|
+
|
|
86
|
+
def meta_class
|
|
87
|
+
class << self
|
|
88
|
+
self
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private :meta_class
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
attr_reader :attributes
|
|
97
|
+
|
|
98
|
+
def initialize(options = {})
|
|
99
|
+
options.symbolize_keys!
|
|
100
|
+
@attributes = options
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def id
|
|
104
|
+
attributes[:id] ? attributes[:id].to_i : nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
alias quoted_id id
|
|
108
|
+
|
|
109
|
+
def new_record?
|
|
110
|
+
false
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def readonly?
|
|
114
|
+
true
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def to_param
|
|
118
|
+
id.to_s
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def eql?(other)
|
|
122
|
+
other.instance_of?(self.class) and not id.nil? and (id == other.id)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
alias == eql?
|
|
126
|
+
|
|
127
|
+
def hash
|
|
128
|
+
id.hash
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ActiveFile::Base do
|
|
4
|
+
|
|
5
|
+
describe ".filename=" do
|
|
6
|
+
before do
|
|
7
|
+
class Foo < ActiveFile::Base
|
|
8
|
+
self.filename = "foo-izzle"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Bar < ActiveFile::Base
|
|
12
|
+
self.filename = "bar-izzle"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "sets the filename on a per-subclass basis" do
|
|
17
|
+
Foo.filename.should == "foo-izzle"
|
|
18
|
+
Bar.filename.should == "bar-izzle"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe ".set_filename" do
|
|
23
|
+
before do
|
|
24
|
+
class Foo < ActiveFile::Base
|
|
25
|
+
set_filename "foo-izzle"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Bar < ActiveFile::Base
|
|
29
|
+
set_filename "bar-izzle"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "sets the filename on a per-subclass basis" do
|
|
34
|
+
Foo.filename.should == "foo-izzle"
|
|
35
|
+
Bar.filename.should == "bar-izzle"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe ".root_path=" do
|
|
40
|
+
before do
|
|
41
|
+
class Foo < ActiveFile::Base
|
|
42
|
+
self.root_path = "foo-izzle"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Bar < ActiveFile::Base
|
|
46
|
+
self.root_path = "bar-izzle"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "sets the root_path on a per-subclass basis" do
|
|
51
|
+
Foo.root_path.should == "foo-izzle"
|
|
52
|
+
Bar.root_path.should == "bar-izzle"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe ".set_root_path" do
|
|
57
|
+
before do
|
|
58
|
+
class Foo < ActiveFile::Base
|
|
59
|
+
set_root_path "foo-izzle"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class Bar < ActiveFile::Base
|
|
63
|
+
set_root_path "bar-izzle"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "sets the root_path on a per-subclass basis" do
|
|
68
|
+
Foo.root_path.should == "foo-izzle"
|
|
69
|
+
Bar.root_path.should == "bar-izzle"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe ".all" do
|
|
74
|
+
before do
|
|
75
|
+
class MyClass
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "loads the data from the load_file method" do
|
|
80
|
+
class Foo01 < ActiveFile::Base
|
|
81
|
+
class << self
|
|
82
|
+
def extension
|
|
83
|
+
"myfile"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def load_file
|
|
87
|
+
MyClass.load_file(full_path)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
File.stub!(:mtime).and_return(1234)
|
|
93
|
+
MyClass.should_receive(:load_file).and_return([{:id => 1}, {:id => 2}, {:id => 3}])
|
|
94
|
+
|
|
95
|
+
records = Foo01.all
|
|
96
|
+
records.length.should == 3
|
|
97
|
+
records.should =~ [Foo01.new(:id => 1), Foo01.new(:id => 2), Foo01.new(:id => 3)]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "does not re-fetch the data if the file's mtime has not changed" do
|
|
101
|
+
class SomeSampleClass < ActiveFile::Base
|
|
102
|
+
class << self
|
|
103
|
+
def extension
|
|
104
|
+
"myfile"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def load_file
|
|
108
|
+
MyClass.load_file(full_path)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
File.stub!(:mtime).and_return(1234)
|
|
114
|
+
MyClass.should_receive(:load_file).once.and_return([{:foo => :bar}])
|
|
115
|
+
SomeSampleClass.all
|
|
116
|
+
SomeSampleClass.all
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "does re-fetch the data if the yaml file's mtime has changed" do
|
|
120
|
+
class SomeSampleClass2 < ActiveFile::Base
|
|
121
|
+
class << self
|
|
122
|
+
def extension
|
|
123
|
+
"myfile"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def load_file
|
|
127
|
+
MyClass.load_file(full_path)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
MyClass.should_receive(:load_file).twice.and_return([{:foo => :bar}])
|
|
133
|
+
|
|
134
|
+
File.stub!(:mtime).and_return(1234)
|
|
135
|
+
SomeSampleClass2.all
|
|
136
|
+
|
|
137
|
+
File.stub!(:mtime).and_return(3456)
|
|
138
|
+
SomeSampleClass2.all
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
end
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ActiveHash::Base do
|
|
4
|
+
|
|
5
|
+
before do
|
|
6
|
+
class Country < ActiveHash::Base
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe ".fields" do
|
|
11
|
+
before do
|
|
12
|
+
Country.fields :name, :iso_name
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "defines a reader for each field" do
|
|
16
|
+
Country.new.should respond_to(:name)
|
|
17
|
+
Country.new.should respond_to(:iso_name)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "defines interrogator methods for each field" do
|
|
21
|
+
Country.new.should respond_to(:name?)
|
|
22
|
+
Country.new.should respond_to(:iso_name?)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "defines single finder methods for each field" do
|
|
26
|
+
Country.should respond_to(:find_by_name)
|
|
27
|
+
Country.should respond_to(:find_by_iso_name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "defines array finder methods for each field" do
|
|
31
|
+
Country.should respond_to(:find_all_by_name)
|
|
32
|
+
Country.should respond_to(:find_all_by_iso_name)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe ".data=" do
|
|
37
|
+
before do
|
|
38
|
+
class Region < ActiveHash::Base
|
|
39
|
+
field :description
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "populates the object with data" do
|
|
44
|
+
Country.data = [{:name => "US"}, {:name => "Canada"}]
|
|
45
|
+
Country.data.should == [{:name => "US"}, {:name => "Canada"}]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "allows each of it's subclasses to have it's own data" do
|
|
49
|
+
Country.data = [{:name => "US"}, {:name => "Canada"}]
|
|
50
|
+
Region.data = [{:description => "A big region"}, {:description => "A remote region"}]
|
|
51
|
+
|
|
52
|
+
Country.data.should == [{:name => "US"}, {:name => "Canada"}]
|
|
53
|
+
Region.data.should == [{:description => "A big region"}, {:description => "A remote region"}]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe ".all" do
|
|
58
|
+
before do
|
|
59
|
+
Country.field :name
|
|
60
|
+
Country.data = [
|
|
61
|
+
{:id => 1, :name => "US"},
|
|
62
|
+
{:id => 2, :name => "Canada"}
|
|
63
|
+
]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "returns all data as inflated objects" do
|
|
67
|
+
Country.all.all?{|country| country.should be_kind_of(Country)}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "populates the data correctly" do
|
|
71
|
+
records = Country.all
|
|
72
|
+
records.first.id.should == 1
|
|
73
|
+
records.first.name.should == "US"
|
|
74
|
+
records.last.id.should == 2
|
|
75
|
+
records.last.name.should == "Canada"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "re-populates the records after data= is called" do
|
|
79
|
+
Country.data = [
|
|
80
|
+
{:id => 45, :name => "Canada"}
|
|
81
|
+
]
|
|
82
|
+
records = Country.all
|
|
83
|
+
records.first.id.should == 45
|
|
84
|
+
records.first.name.should == "Canada"
|
|
85
|
+
records.length.should == 1
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe ".count" do
|
|
90
|
+
before do
|
|
91
|
+
Country.data = [
|
|
92
|
+
{:id => 1, :name => "US"},
|
|
93
|
+
{:id => 2, :name => "Canada"}
|
|
94
|
+
]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "returns the number of elements in the array" do
|
|
98
|
+
Country.count.should == 2
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe ".first" do
|
|
103
|
+
before do
|
|
104
|
+
Country.data = [
|
|
105
|
+
{:id => 1, :name => "US"},
|
|
106
|
+
{:id => 2, :name => "Canada"}
|
|
107
|
+
]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "returns the first object" do
|
|
111
|
+
Country.first.should == Country.new(:id => 1)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe ".last" do
|
|
116
|
+
before do
|
|
117
|
+
Country.data = [
|
|
118
|
+
{:id => 1, :name => "US"},
|
|
119
|
+
{:id => 2, :name => "Canada"}
|
|
120
|
+
]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "returns the last object" do
|
|
124
|
+
Country.last.should == Country.new(:id => 2)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe ".find" do
|
|
129
|
+
before do
|
|
130
|
+
Country.data = [
|
|
131
|
+
{:id => 1, :name => "US"},
|
|
132
|
+
{:id => 2, :name => "Canada"}
|
|
133
|
+
]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
context "with an id" do
|
|
137
|
+
it "finds the record with the specified id" do
|
|
138
|
+
Country.find(2).id.should == 2
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "finds the record with the specified id as a string" do
|
|
142
|
+
Country.find("2").id.should == 2
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
context "with :all" do
|
|
147
|
+
it "returns all records" do
|
|
148
|
+
Country.find(:all).should =~ [Country.new(:id => 1), Country.new(:id => 2)]
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
context "with 2 arguments" do
|
|
153
|
+
it "returns the record with the given id and ignores the conditions" do
|
|
154
|
+
Country.find(1, :conditions => "foo=bar").should == Country.new(:id => 1)
|
|
155
|
+
Country.find(:all, :conditions => "foo=bar").length.should == 2
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
context "with an array of ids" do
|
|
160
|
+
before do
|
|
161
|
+
Country.data = [
|
|
162
|
+
{:id => 1},
|
|
163
|
+
{:id => 2},
|
|
164
|
+
{:id => 3}
|
|
165
|
+
]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it "returns all matching ids" do
|
|
169
|
+
Country.find([1,3]).should =~ [Country.new(:id => 1), Country.new(:id => 3)]
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
describe ".find_by_id" do
|
|
175
|
+
before do
|
|
176
|
+
Country.data = [
|
|
177
|
+
{:id => 1, :name => "US"},
|
|
178
|
+
{:id => 2, :name => "Canada"}
|
|
179
|
+
]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
context "with an id" do
|
|
183
|
+
it "finds the record with the specified id" do
|
|
184
|
+
Country.find_by_id(2).id.should == 2
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
context "with nil" do
|
|
189
|
+
it "returns nil" do
|
|
190
|
+
Country.find_by_id(nil).should be_nil
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
context "with an id not present" do
|
|
195
|
+
it "returns nil" do
|
|
196
|
+
Country.find_by_id(4567).should be_nil
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
describe "custom finders" do
|
|
202
|
+
before do
|
|
203
|
+
Country.field :name
|
|
204
|
+
Country.data = [
|
|
205
|
+
{:id => 1, :name => "US"},
|
|
206
|
+
{:id => 2, :name => "US"},
|
|
207
|
+
{:id => 3, :name => "Canada"}
|
|
208
|
+
]
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
describe "find_by_<field_name>" do
|
|
212
|
+
context "with a name" do
|
|
213
|
+
it "returns the first record matching that name" do
|
|
214
|
+
Country.find_by_name("US").id.should == 1
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
context "with nil" do
|
|
219
|
+
it "returns nil" do
|
|
220
|
+
Country.find_by_name(nil).should be_nil
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
context "with a name not present" do
|
|
225
|
+
it "returns nil" do
|
|
226
|
+
Country.find_by_name("foo").should be_nil
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
describe "find_all_by_<field_name>" do
|
|
232
|
+
context "with a name" do
|
|
233
|
+
it "returns the records matching that name" do
|
|
234
|
+
countries = Country.find_all_by_name("US")
|
|
235
|
+
countries.length.should == 2
|
|
236
|
+
countries.first.name.should == "US"
|
|
237
|
+
countries.last.name.should == "US"
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
context "with a name not present" do
|
|
242
|
+
it "returns an empty array" do
|
|
243
|
+
Country.find_all_by_name("foo").should be_empty
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
describe "#attributes" do
|
|
251
|
+
it "returns the hash passed in the initializer" do
|
|
252
|
+
country = Country.new(:foo => :bar)
|
|
253
|
+
country.attributes.should == {:foo => :bar}
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
it "symbolizes keys" do
|
|
257
|
+
country = Country.new("foo" => :bar)
|
|
258
|
+
country.attributes.should == {:foo => :bar}
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
describe "reader methods" do
|
|
263
|
+
context "for regular fields" do
|
|
264
|
+
before do
|
|
265
|
+
Country.fields :name, :iso_name
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
it "returns the given attribute when present" do
|
|
269
|
+
country = Country.new(:name => "Spain")
|
|
270
|
+
country.name.should == "Spain"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
it "returns nil when not present" do
|
|
274
|
+
country = Country.new
|
|
275
|
+
country.name.should be_nil
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
context "for fields with default values" do
|
|
280
|
+
before do
|
|
281
|
+
Country.field :name, :default => "foobar"
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
it "returns the given attribute when present" do
|
|
285
|
+
country = Country.new(:name => "Spain")
|
|
286
|
+
country.name.should == "Spain"
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
it "returns the default value when not present" do
|
|
290
|
+
country = Country.new
|
|
291
|
+
country.name.should == "foobar"
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
describe "interrogator methods" do
|
|
297
|
+
before do
|
|
298
|
+
Country.fields :name, :iso_name
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
it "returns true if the given attribute is non-blank" do
|
|
302
|
+
country = Country.new(:name => "Spain")
|
|
303
|
+
country.should be_name
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it "returns false if the given attribute is blank" do
|
|
307
|
+
country = Country.new(:name => " ")
|
|
308
|
+
country.should_not be_name
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
it "returns false if the given attribute was not passed" do
|
|
312
|
+
country = Country.new
|
|
313
|
+
country.should_not be_name
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
describe "#id" do
|
|
318
|
+
context "when passed an id" do
|
|
319
|
+
it "returns the id as an integer" do
|
|
320
|
+
country = Country.new :id => "1"
|
|
321
|
+
country.id.should == 1
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
context "when not passed an id" do
|
|
325
|
+
it "returns nil" do
|
|
326
|
+
country = Country.new
|
|
327
|
+
country.id.should be_nil
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
describe "#new_record?" do
|
|
333
|
+
it "should be false" do
|
|
334
|
+
Country.new.should_not be_new_record
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
describe "#quoted_id" do
|
|
339
|
+
it "should return id" do
|
|
340
|
+
Country.new(:id => 2).quoted_id.should == 2
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
describe "#to_param" do
|
|
345
|
+
it "should return id as a string" do
|
|
346
|
+
Country.new(:id => 2).to_param.should == "2"
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
describe "#eql?" do
|
|
351
|
+
before do
|
|
352
|
+
class Region < ActiveHash::Base
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
it "should return true with the same class and id" do
|
|
357
|
+
Country.new(:id => 23).eql?(Country.new(:id => 23)).should be_true
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
it "should return false with the same class and different ids" do
|
|
361
|
+
Country.new(:id => 24).eql?(Country.new(:id => 23)).should be_false
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
it "should return false with the different classes and the same id" do
|
|
365
|
+
Country.new(:id => 23).eql?(Region.new(:id => 23)).should be_false
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
it "returns false when id is nil" do
|
|
369
|
+
Country.new.eql?(Country.new).should be_false
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
describe "#==" do
|
|
374
|
+
before do
|
|
375
|
+
class Region < ActiveHash::Base
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it "should return true with the same class and id" do
|
|
380
|
+
Country.new(:id => 23).should == Country.new(:id => 23)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
it "should return false with the same class and different ids" do
|
|
384
|
+
Country.new(:id => 24).should_not == Country.new(:id => 23)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
it "should return false with the different classes and the same id" do
|
|
388
|
+
Country.new(:id => 23).should_not == Region.new(:id => 23)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
it "returns false when id is nil" do
|
|
392
|
+
Country.new.should_not == Country.new
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
describe "#hash" do
|
|
397
|
+
it "returns id for hash" do
|
|
398
|
+
Country.new(:id => 45).hash.should == 45.hash
|
|
399
|
+
Country.new.hash.should == nil.hash
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
it "is hashable" do
|
|
403
|
+
{ Country.new(:id => 4) => "bar"}.should == {Country.new(:id => 4) => "bar" }
|
|
404
|
+
{ Country.new(:id => 3) => "bar"}.should_not == {Country.new(:id => 4) => "bar" }
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
describe "#readonly?" do
|
|
409
|
+
it "returns true" do
|
|
410
|
+
Country.new.should be_readonly
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'spec/spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ActiveYaml::Base do
|
|
4
|
+
|
|
5
|
+
describe ".all" do
|
|
6
|
+
it "loads the data from the yml file" do
|
|
7
|
+
class SomeArbitraryClass < ActiveYaml::Base
|
|
8
|
+
set_root_path File.dirname(__FILE__)
|
|
9
|
+
set_filename "sample"
|
|
10
|
+
field :name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
records = SomeArbitraryClass.all
|
|
14
|
+
records.length.should == 3
|
|
15
|
+
records.should =~ [SomeArbitraryClass.new(:id => 1), SomeArbitraryClass.new(:id => 2), SomeArbitraryClass.new(:id => 3)]
|
|
16
|
+
records.first.name.should == "US"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: zilkey-active_hash
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jeff Dean
|
|
8
|
+
- Mike Dalessio
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2009-07-22 00:00:00 -07:00
|
|
14
|
+
default_executable:
|
|
15
|
+
dependencies:
|
|
16
|
+
- !ruby/object:Gem::Dependency
|
|
17
|
+
name: activesupport
|
|
18
|
+
type: :runtime
|
|
19
|
+
version_requirement:
|
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
21
|
+
requirements:
|
|
22
|
+
- - ">="
|
|
23
|
+
- !ruby/object:Gem::Version
|
|
24
|
+
version: "0"
|
|
25
|
+
version:
|
|
26
|
+
description:
|
|
27
|
+
email: jeff@zilkey.com
|
|
28
|
+
executables: []
|
|
29
|
+
|
|
30
|
+
extensions: []
|
|
31
|
+
|
|
32
|
+
extra_rdoc_files:
|
|
33
|
+
- LICENSE
|
|
34
|
+
- README.md
|
|
35
|
+
files:
|
|
36
|
+
- .document
|
|
37
|
+
- .gitignore
|
|
38
|
+
- LICENSE
|
|
39
|
+
- README.md
|
|
40
|
+
- Rakefile
|
|
41
|
+
- VERSION
|
|
42
|
+
- lib/active_file/base.rb
|
|
43
|
+
- lib/active_hash.rb
|
|
44
|
+
- lib/active_hash/base.rb
|
|
45
|
+
- lib/active_yaml/base.rb
|
|
46
|
+
- spec/active_file/base_spec.rb
|
|
47
|
+
- spec/active_hash/base_spec.rb
|
|
48
|
+
- spec/active_yaml/base_spec.rb
|
|
49
|
+
- spec/active_yaml/sample.yml
|
|
50
|
+
- spec/spec_helper.rb
|
|
51
|
+
has_rdoc: false
|
|
52
|
+
homepage: http://github.com/zilkey/active_hash
|
|
53
|
+
post_install_message:
|
|
54
|
+
rdoc_options:
|
|
55
|
+
- --charset=UTF-8
|
|
56
|
+
require_paths:
|
|
57
|
+
- lib
|
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: "0"
|
|
63
|
+
version:
|
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: "0"
|
|
69
|
+
version:
|
|
70
|
+
requirements: []
|
|
71
|
+
|
|
72
|
+
rubyforge_project:
|
|
73
|
+
rubygems_version: 1.2.0
|
|
74
|
+
signing_key:
|
|
75
|
+
specification_version: 3
|
|
76
|
+
summary: An ActiveRecord-like model that uses a hash as a datasource
|
|
77
|
+
test_files:
|
|
78
|
+
- spec/active_file/base_spec.rb
|
|
79
|
+
- spec/active_hash/base_spec.rb
|
|
80
|
+
- spec/active_yaml/base_spec.rb
|
|
81
|
+
- spec/spec_helper.rb
|