honkster-active_hash 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
data/geminstaller.yml ADDED
@@ -0,0 +1,13 @@
1
+ defaults:
2
+ install_options: --source=http://gemcutter.org --source=http://gems.rubyforge.org --include-dependencies --no-ri --no-rdoc
3
+ gems:
4
+ - name: acts_as_fu
5
+ version: 0.0.5
6
+ - name: activesupport
7
+ version: >= 2.2.2
8
+ - name: rspec
9
+ version: >= 1.2.8
10
+ - name: fixjour
11
+ version: 0.2.0
12
+ - name: jeweler
13
+ version: 1.2.1
@@ -0,0 +1,50 @@
1
+ module ActiveFile
2
+
3
+ class Base < ActiveHash::Base
4
+ class_inheritable_accessor :filename, :root_path, :data_loaded
5
+
6
+ class << self
7
+
8
+ def all
9
+ reload unless data_loaded
10
+ super
11
+ end
12
+
13
+ def delete_all
14
+ self.data_loaded = true
15
+ super
16
+ end
17
+
18
+ def reload(foo = true)
19
+ self.data_loaded = true
20
+ self.data = load_file
21
+ end
22
+
23
+ def set_filename(name)
24
+ write_inheritable_attribute :filename, name
25
+ end
26
+
27
+ def set_root_path(path)
28
+ write_inheritable_attribute :root_path, path
29
+ end
30
+
31
+ def load_file
32
+ raise "Override Me"
33
+ end
34
+
35
+ def full_path
36
+ root_path = read_inheritable_attribute(:root_path) || Dir.pwd
37
+ filename = read_inheritable_attribute(:filename) || name.tableize
38
+ File.join(root_path, "#{filename}.#{extension}")
39
+ end
40
+
41
+ def extension
42
+ raise "Override Me"
43
+ end
44
+
45
+ protected :extension
46
+
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,5 @@
1
+ require 'activesupport'
2
+ require 'active_hash/base'
3
+ require 'active_file/base'
4
+ require 'active_yaml/base'
5
+ require 'associations/associations'
@@ -0,0 +1,265 @@
1
+ module ActiveHash
2
+ class Base
3
+ class_inheritable_accessor :data
4
+ class << self
5
+ attr_reader :field_names
6
+
7
+ def data=(array_of_hashes)
8
+ @records = nil
9
+ write_inheritable_attribute(:data, array_of_hashes)
10
+ if array_of_hashes
11
+ auto_assign_fields( array_of_hashes )
12
+ array_of_hashes.each do |hash|
13
+ insert new(hash)
14
+ end
15
+ end
16
+ end
17
+
18
+ def insert(record)
19
+ @records ||= []
20
+ record.attributes[:id] ||= next_id
21
+ @records << record
22
+ end
23
+
24
+ def next_id
25
+ max_record = all.max {|a, b| a.id <=> b.id }
26
+ if max_record.nil?
27
+ 1
28
+ elsif max_record.id.is_a?(Numeric)
29
+ max_record.id.succ
30
+ end
31
+ end
32
+
33
+ def create(attributes = {})
34
+ record = new(attributes)
35
+ record.save
36
+ record
37
+ end
38
+
39
+ def create!(attributes = {})
40
+ record = new(attributes)
41
+ record.save!
42
+ record
43
+ end
44
+
45
+ def all
46
+ @records || []
47
+ end
48
+
49
+ def count
50
+ all.length
51
+ end
52
+
53
+ def transaction
54
+ yield
55
+ rescue ActiveRecord::Rollback
56
+
57
+ end
58
+
59
+ def delete_all
60
+ @records = []
61
+ end
62
+
63
+ def find(id, *args)
64
+ case id
65
+ when :all
66
+ all
67
+ when Array
68
+ all.select {|record| id.map(&:to_i).include?(record.id) }
69
+ else
70
+ find_by_id(id)
71
+ end
72
+ end
73
+
74
+ def find_by_id(id)
75
+ all.detect {|record| record.id == id.to_i}
76
+ end
77
+
78
+ delegate :first, :last, :to => :all
79
+
80
+ def fields(*args)
81
+ options = args.extract_options!
82
+ args.each do |field|
83
+ field(field, options)
84
+ end
85
+ end
86
+
87
+ def field(field_name, options = {})
88
+ @field_names ||= []
89
+ @field_names << field_name
90
+
91
+ define_getter_method(field_name, options[:default])
92
+ define_setter_method(field_name)
93
+ define_interrogator_method(field_name)
94
+ define_custom_find_method(field_name)
95
+ define_custom_find_all_method(field_name)
96
+ end
97
+
98
+ def respond_to?(method_name, include_private=false)
99
+ super ||
100
+ begin
101
+ config = configuration_for_custom_finder(method_name)
102
+ config && config[:fields].all? do |field|
103
+ field_names.include?(field.to_sym) || field.to_sym == :id
104
+ end
105
+ end
106
+ end
107
+
108
+ def method_missing(method_name, *args)
109
+ return super unless respond_to? method_name
110
+
111
+ config = configuration_for_custom_finder(method_name)
112
+ attribute_pairs = config[:fields].zip(args)
113
+ matches = all.select { |base| attribute_pairs.all? { |field, value| base.send(field).to_s == value.to_s } }
114
+ config[:all?] ? matches : matches.first
115
+ end
116
+
117
+ def configuration_for_custom_finder(finder_name)
118
+ if finder_name.to_s.match(/^find_(all_)?by_(.*)/)
119
+ {
120
+ :all? => !!$1,
121
+ :fields => $2.split('_and_')
122
+ }
123
+ end
124
+ end
125
+
126
+ private :configuration_for_custom_finder
127
+
128
+ def define_getter_method(field, default_value)
129
+ unless instance_methods.include?(field.to_s)
130
+ define_method(field) do
131
+ attributes[field].nil? ? default_value : attributes[field]
132
+ end
133
+ end
134
+ end
135
+
136
+ private :define_getter_method
137
+
138
+ def define_setter_method(field)
139
+ method_name = "#{field}="
140
+ unless instance_methods.include?(method_name)
141
+ define_method(method_name) do |new_val|
142
+ attributes[field] = new_val
143
+ end
144
+ end
145
+ end
146
+
147
+ private :define_setter_method
148
+
149
+ def define_interrogator_method(field)
150
+ method_name = "#{field}?"
151
+ unless instance_methods.include?(method_name)
152
+ define_method(method_name) do
153
+ send(field).present?
154
+ end
155
+ end
156
+ end
157
+
158
+ private :define_interrogator_method
159
+
160
+ def define_custom_find_method(field_name)
161
+ method_name = "find_by_#{field_name}"
162
+ unless singleton_methods.include?(method_name)
163
+ metaclass.instance_eval do
164
+ define_method(method_name) do |name|
165
+ all.detect {|record| record.send(field_name) == name }
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ private :define_custom_find_method
172
+
173
+ def define_custom_find_all_method(field_name)
174
+ method_name = "find_all_by_#{field_name}"
175
+ unless singleton_methods.include?(method_name)
176
+ metaclass.instance_eval do
177
+ unless singleton_methods.include?(method_name)
178
+ define_method(method_name) do |name|
179
+ all.select {|record| record.send(field_name) == name }
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+
186
+ private :define_custom_find_all_method
187
+
188
+ def auto_assign_fields(array_of_hashes)
189
+ (array_of_hashes || []).inject([]) do |array, row|
190
+ row.symbolize_keys!
191
+ row.keys.each do |key|
192
+ unless key.to_s == "id"
193
+ array << key
194
+ end
195
+ end
196
+ array
197
+ end.uniq.each do |key|
198
+ field key
199
+ end
200
+ end
201
+
202
+ private :auto_assign_fields
203
+
204
+ # Needed for ActiveRecord polymorphic associations
205
+ def base_class
206
+ ActiveHash::Base
207
+ end
208
+
209
+ end
210
+
211
+ attr_reader :attributes
212
+
213
+ def initialize(options = {})
214
+ options.symbolize_keys!
215
+ @attributes = options
216
+ options.each do |key, value|
217
+ send "#{key}=", value
218
+ end
219
+ end
220
+
221
+ def id
222
+ attributes[:id] ? attributes[:id] : nil
223
+ end
224
+
225
+ def id=(id)
226
+ attributes[:id] = id
227
+ end
228
+
229
+ alias quoted_id id
230
+
231
+ def new_record?
232
+ ! self.class.all.include?(self)
233
+ end
234
+
235
+ def readonly?
236
+ true
237
+ end
238
+
239
+ def to_param
240
+ id.to_s
241
+ end
242
+
243
+ def eql?(other)
244
+ other.instance_of?(self.class) and not id.nil? and (id == other.id)
245
+ end
246
+
247
+ alias == eql?
248
+
249
+ def hash
250
+ id.hash
251
+ end
252
+
253
+ def save
254
+ self.class.insert(self)
255
+ true
256
+ end
257
+
258
+ alias save! save
259
+
260
+ def valid?
261
+ true
262
+ end
263
+
264
+ end
265
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveYaml
2
+
3
+ class Base < ActiveFile::Base
4
+ class << self
5
+ def load_file
6
+ if (data = raw_data).is_a?(Array)
7
+ data
8
+ else
9
+ data.values
10
+ end
11
+ end
12
+
13
+ def raw_data
14
+ YAML.load_file(full_path)
15
+ end
16
+
17
+ def extension
18
+ "yml"
19
+ end
20
+
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,40 @@
1
+ module ActiveHash
2
+ module Associations
3
+
4
+ def self.included(base)
5
+ base.send(:extend, ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def has_many(association_id, options = {})
10
+
11
+ define_method(association_id) do
12
+ options = {
13
+ :class_name => association_id.to_s.classify,
14
+ :foreign_key => self.class.to_s.foreign_key
15
+ }.merge(options)
16
+
17
+ options[:class_name].constantize.send("find_all_by_#{options[:foreign_key]}", id)
18
+ end
19
+
20
+ end
21
+
22
+ def belongs_to(association_id, options = {})
23
+
24
+ options = {
25
+ :class_name => association_id.to_s.classify,
26
+ :foreign_key => association_id.to_s.foreign_key
27
+ }.merge(options)
28
+
29
+ define_method(association_id) do
30
+ options[:class_name].constantize.find(send(options[:foreign_key]))
31
+ end
32
+
33
+ define_method("#{association_id}=") do |new_value|
34
+ attributes[ options[:foreign_key].to_sym ] = new_value.id
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ActiveFile::Base do
4
+ before do
5
+ class Country < ActiveFile::Base
6
+ end
7
+ end
8
+
9
+ after do
10
+ Object.send :remove_const, :Country
11
+ end
12
+
13
+ describe ".filename=" do
14
+ before do
15
+ Country.filename = "foo-izzle"
16
+
17
+ class Bar < ActiveFile::Base
18
+ self.filename = "bar-izzle"
19
+ end
20
+ end
21
+
22
+ it "sets the filename on a per-subclass basis" do
23
+ Country.filename.should == "foo-izzle"
24
+ Bar.filename.should == "bar-izzle"
25
+ end
26
+ end
27
+
28
+ describe ".set_filename" do
29
+ before do
30
+ Country.set_filename "foo-izzle"
31
+
32
+ class Bar < ActiveFile::Base
33
+ set_filename "bar-izzle"
34
+ end
35
+ end
36
+
37
+ it "sets the filename on a per-subclass basis" do
38
+ Country.filename.should == "foo-izzle"
39
+ Bar.filename.should == "bar-izzle"
40
+ end
41
+ end
42
+
43
+ describe ".root_path=" do
44
+ before do
45
+ Country.root_path = "foo-izzle"
46
+
47
+ class Bar < ActiveFile::Base
48
+ self.root_path = "bar-izzle"
49
+ end
50
+ end
51
+
52
+ it "sets the root_path on a per-subclass basis" do
53
+ Country.root_path.should == "foo-izzle"
54
+ Bar.root_path.should == "bar-izzle"
55
+ end
56
+ end
57
+
58
+ describe ".set_root_path" do
59
+ before do
60
+ Country.set_root_path "foo-izzle"
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
+ Country.root_path.should == "foo-izzle"
69
+ Bar.root_path.should == "bar-izzle"
70
+ end
71
+ end
72
+
73
+ describe ".full_path" do
74
+ it "defaults to the directory of the calling file" do
75
+ class Country
76
+ def self.extension() "foo" end
77
+ end
78
+
79
+ Country.full_path.should == "#{Dir.pwd}/countries.foo"
80
+ end
81
+ end
82
+
83
+ end