csun-student-affairs-supermodel 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ dump.db
2
+ test.rb
3
+ redis_test.rb
4
+ pkg
5
+ lib/supermodel/cassandra.rb
6
+ *.gem
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Alexander MacCaw (info@eribium.org)
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 ADDED
@@ -0,0 +1,64 @@
1
+
2
+ Simple in-memory database using ActiveModel.
3
+
4
+ Primarily developed for Bowline applications.
5
+ http://github.com/maccman/bowline
6
+
7
+ Supports:
8
+ * Serialisation
9
+ * Validations
10
+ * Callbacks
11
+ * Observers
12
+ * Dirty (Changes)
13
+ * Ruby Marshalling to disk
14
+ * Redis
15
+
16
+ Examples:
17
+
18
+ require "supermodel"
19
+
20
+ class Test < SuperModel::Base
21
+ end
22
+
23
+ t = Test.new
24
+ t.name = "foo"
25
+ t.save #=> true
26
+
27
+ Test.all
28
+ Test.first
29
+ Test.last
30
+ Test.find_by_name('foo)
31
+
32
+ You can use a random ID rather than the object ID:
33
+
34
+ class Test < SuperModel::Base
35
+ include SuperModel::RandomID
36
+ end
37
+
38
+ t = Test.create(:name => "test")
39
+ t.id #=> "7ee935377bb4aecc54ad4f9126"
40
+
41
+ You can marshal objects to disk on startup/shutdown
42
+
43
+ class Test < SuperModel::Base
44
+ include SuperModel::Marshal::Model
45
+ end
46
+
47
+ SuperModel::Marshal.path = "dump.db"
48
+ SuperModel::Marshal.load
49
+
50
+ at_exit {
51
+ SuperModel::Marshal.dump
52
+ }
53
+
54
+ You can use Redis, you need the Redis gem installed:
55
+
56
+ require "redis"
57
+ class Test < SuperModel::Base
58
+ include SuperModel::Redis::Model
59
+
60
+ attributes :name
61
+ indexes :name
62
+ end
63
+
64
+ Test.find_or_create_by_name("foo")
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "supermodel"
5
+ gemspec.summary = "In memory DB using ActiveModel"
6
+ gemspec.email = "info@eribium.org"
7
+ gemspec.homepage = "http://github.com/maccman/supermodel"
8
+ gemspec.description = "In memory DB using ActiveModel"
9
+ gemspec.authors = ["Alex MacCaw"]
10
+ gemspec.add_dependency("activemodel", "~> 3.0.0")
11
+ end
12
+ rescue LoadError
13
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
14
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.4
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "supermodel")
data/lib/supermodel.rb ADDED
@@ -0,0 +1,42 @@
1
+ gem "activesupport"
2
+ gem "activemodel"
3
+
4
+ require "active_support/core_ext/class/attribute_accessors"
5
+ require "active_support/core_ext/class/inheritable_attributes"
6
+ require "active_support/core_ext/hash/indifferent_access"
7
+ require "active_support/core_ext/kernel/reporting"
8
+ require "active_support/core_ext/module/attr_accessor_with_default"
9
+ require "active_support/core_ext/module/delegation"
10
+ require "active_support/core_ext/module/aliasing"
11
+ require "active_support/core_ext/object/blank"
12
+ require "active_support/core_ext/object/try"
13
+ require "active_support/core_ext/object/to_query"
14
+ require "active_support/json"
15
+
16
+ require "active_model"
17
+
18
+ module SuperModel
19
+ class SuperModelError < StandardError; end
20
+ class UnknownRecord < SuperModelError; end
21
+ class InvalidRecord < SuperModelError; end
22
+ end
23
+
24
+ $:.unshift(File.dirname(__FILE__))
25
+ require "supermodel/ext/array"
26
+
27
+ module SuperModel
28
+ autoload :Association, "supermodel/association"
29
+ autoload :Callbacks, "supermodel/callbacks"
30
+ autoload :Observing, "supermodel/observing"
31
+ autoload :Marshal, "supermodel/marshal"
32
+ autoload :RandomID, "supermodel/random_id"
33
+ autoload :Timestamp, "supermodel/timestamp"
34
+ autoload :Validations, "supermodel/validations"
35
+ autoload :Dirty, "supermodel/dirty"
36
+ autoload :Redis, "supermodel/redis"
37
+ autoload :Base, "supermodel/base"
38
+ end
39
+
40
+ module ActiveModel
41
+ autoload :SerializeOptions, "active_model/serialize_options"
42
+ end
@@ -0,0 +1,51 @@
1
+ require "active_support/core_ext/string/inflections.rb"
2
+
3
+ module SuperModel
4
+ module Association
5
+ module ClassMethods
6
+ def belongs_to(to_model, options = {})
7
+ to_model = to_model.to_s
8
+ class_name = options[:class_name] || to_model.classify
9
+ foreign_key = options[:foreign_key] || "#{to_model}_id"
10
+ primary_key = options[:primary_key] || "id"
11
+
12
+ attributes foreign_key
13
+
14
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
15
+ def #{to_model} # def user
16
+ #{foreign_key} && #{class_name}.find(#{foreign_key}) # user_id && User.find(user_id)
17
+ end # end
18
+ #
19
+ def #{to_model}? # def user?
20
+ #{foreign_key} && #{class_name}.exists?(#{foreign_key}) # user_id && User.exists?(user_id)
21
+ end # end
22
+ #
23
+ def #{to_model}=(object) # def user=(model)
24
+ self.#{foreign_key} = (object && object.#{primary_key}) # self.user_id = (model && model.id)
25
+ end # end
26
+ EOS
27
+ end
28
+
29
+ def has_many(to_model, options = {})
30
+ to_model = to_model.to_s
31
+ class_name = options[:class_name] || to_model.classify
32
+ foreign_key = options[:foreign_key] || "#{model_name.singular}_id"
33
+ primary_key = options[:primary_key] || "id"
34
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
35
+ def #{to_model} # def user
36
+ #{class_name}.find_all_by_attribute( # User.find_all_by_attribute(
37
+ :#{foreign_key}, # :task_id,
38
+ #{primary_key} # id
39
+ ) # )
40
+ end # end
41
+ EOS
42
+ end
43
+ end
44
+
45
+ module Model
46
+ def self.included(base)
47
+ base.extend(ClassMethods)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,309 @@
1
+ module SuperModel
2
+ class Base
3
+ class_inheritable_array :known_attributes
4
+ self.known_attributes = []
5
+
6
+ class << self
7
+ attr_accessor_with_default(:primary_key, 'id') #:nodoc:
8
+
9
+ def collection(&block)
10
+ @collection ||= Class.new(Array)
11
+ @collection.class_eval(&block) if block_given?
12
+ @collection
13
+ end
14
+
15
+ def attributes(*attributes)
16
+ self.known_attributes += attributes.map(&:to_s)
17
+ end
18
+
19
+ def records
20
+ @records ||= {}
21
+ end
22
+
23
+ def find_by_attribute(name, value) #:nodoc:
24
+ item = records.values.find {|r| r.send(name) == value }
25
+ item && item.dup
26
+ end
27
+
28
+ def find_all_by_attribute(name, value) #:nodoc:
29
+ items = records.values.select {|r| r.send(name) == value }
30
+ collection.new(items.deep_dup)
31
+ end
32
+
33
+ def raw_find(id) #:nodoc:
34
+ records[id] || raise(UnknownRecord, "Couldn't find #{self.name} with ID=#{id}")
35
+ end
36
+
37
+ # Find record by ID, or raise.
38
+ def find(id)
39
+ item = raw_find(id)
40
+ item && item.dup
41
+ end
42
+ alias :[] :find
43
+
44
+ def first
45
+ item = records.values[0]
46
+ item && item.dup
47
+ end
48
+
49
+ def last
50
+ item = records.values[-1]
51
+ item && item.dup
52
+ end
53
+
54
+ def exists?(id)
55
+ records.has_key?(id)
56
+ end
57
+
58
+ def count
59
+ records.length
60
+ end
61
+
62
+ def all
63
+ collection.new(records.values.deep_dup)
64
+ end
65
+
66
+ def select(&block)
67
+ collection.new(records.values.select(&block).deep_dup)
68
+ end
69
+
70
+ def update(id, atts)
71
+ find(id).update_attributes(atts)
72
+ end
73
+
74
+ def destroy(id)
75
+ find(id).destroy
76
+ end
77
+
78
+ # Removes all records and executes
79
+ # destory callbacks.
80
+ def destroy_all
81
+ all.each {|r| r.destroy }
82
+ end
83
+
84
+ # Removes all records without executing
85
+ # destroy callbacks.
86
+ def delete_all
87
+ records.clear
88
+ end
89
+
90
+ # Create a new record.
91
+ # Example:
92
+ # create(:name => "foo", :id => 1)
93
+ def create(atts = {})
94
+ rec = self.new(atts)
95
+ rec.save && rec
96
+ end
97
+
98
+ def create!(*args)
99
+ create(*args) || raise(InvalidRecord)
100
+ end
101
+
102
+ def method_missing(method_symbol, *args) #:nodoc:
103
+ method_name = method_symbol.to_s
104
+
105
+ if method_name =~ /^find_by_(\w+)!/
106
+ send("find_by_#{$1}", *args) || raise(UnknownRecord)
107
+ elsif method_name =~ /^find_by_(\w+)/
108
+ find_by_attribute($1, args.first)
109
+ elsif method_name =~ /^find_or_create_by_(\w+)/
110
+ send("find_by_#{$1}", *args) || create($1 => args.first)
111
+ elsif method_name =~ /^find_all_by_(\w+)/
112
+ find_all_by_attribute($1, args.first)
113
+ else
114
+ super
115
+ end
116
+ end
117
+ end
118
+
119
+ attr_accessor :attributes
120
+ attr_writer :new_record
121
+
122
+ def known_attributes
123
+ self.class.known_attributes + self.attributes.keys.map(&:to_s)
124
+ end
125
+
126
+ def initialize(attributes = {})
127
+ @new_record = true
128
+ @attributes = {}.with_indifferent_access
129
+ @attributes.merge!(known_attributes.inject({}) {|h, n| h[n] = nil; h })
130
+ @changed_attributes = {}
131
+ load(attributes)
132
+ end
133
+
134
+ def clone
135
+ cloned = attributes.reject {|k,v| k == self.class.primary_key }
136
+ cloned = cloned.inject({}) do |attrs, (k, v)|
137
+ attrs[k] = v.clone
138
+ attrs
139
+ end
140
+ self.class.new(cloned)
141
+ end
142
+
143
+ def new?
144
+ @new_record || false
145
+ end
146
+ alias :new_record? :new?
147
+
148
+ # Gets the <tt>\id</tt> attribute of the item.
149
+ def id
150
+ attributes[self.class.primary_key]
151
+ end
152
+
153
+ # Sets the <tt>\id</tt> attribute of the item.
154
+ def id=(id)
155
+ attributes[self.class.primary_key] = id
156
+ end
157
+
158
+ def ==(other)
159
+ other.equal?(self) || (other.instance_of?(self.class) && other.id == id)
160
+ end
161
+
162
+ # Tests for equality (delegates to ==).
163
+ def eql?(other)
164
+ self == other
165
+ end
166
+
167
+ def hash
168
+ id.hash
169
+ end
170
+
171
+ def dup
172
+ self.class.new.tap do |base|
173
+ base.attributes = attributes
174
+ base.new_record = new_record?
175
+ end
176
+ end
177
+
178
+ def save
179
+ new? ? create : update
180
+ end
181
+
182
+ def save!
183
+ save || raise(InvalidRecord)
184
+ end
185
+
186
+ def exists?
187
+ !new?
188
+ end
189
+ alias_method :persisted?, :exists?
190
+
191
+ def load(attributes) #:nodoc:
192
+ return unless attributes
193
+ attributes.each do |(name, value)|
194
+ self.send("#{name}=".to_sym, value)
195
+ end
196
+ end
197
+
198
+ def reload
199
+ return self if new?
200
+ item = self.class.find(id)
201
+ load(item.attributes)
202
+ return self
203
+ end
204
+
205
+ def update_attribute(name, value)
206
+ self.send("#{name}=".to_sym, value)
207
+ self.save
208
+ end
209
+
210
+ def update_attributes(attributes)
211
+ load(attributes) && save
212
+ end
213
+
214
+ def update_attributes!(attributes)
215
+ update_attributes(attributes) || raise(InvalidRecord)
216
+ end
217
+
218
+ def has_attribute?(name)
219
+ @attributes.has_key?(name)
220
+ end
221
+
222
+ alias_method :respond_to_without_attributes?, :respond_to?
223
+
224
+ def respond_to?(method, include_priv = false)
225
+ method_name = method.to_s
226
+ if attributes.nil?
227
+ super
228
+ elsif known_attributes.include?(method_name)
229
+ true
230
+ elsif method_name =~ /(?:=|\?)$/ && attributes.include?($`)
231
+ true
232
+ else
233
+ super
234
+ end
235
+ end
236
+
237
+ def destroy
238
+ raw_destroy
239
+ self
240
+ end
241
+
242
+ protected
243
+ def read_attribute(name)
244
+ @attributes[name]
245
+ end
246
+
247
+ def write_attribute(name, value)
248
+ @attributes[name] = value
249
+ end
250
+
251
+ def generate_id
252
+ object_id
253
+ end
254
+
255
+ def raw_destroy
256
+ self.class.records.delete(self.id)
257
+ end
258
+
259
+ def raw_create
260
+ self.class.records[self.id] = self.dup
261
+ end
262
+
263
+ def create
264
+ self.id ||= generate_id
265
+ self.new_record = false
266
+ raw_create
267
+ self.id
268
+ end
269
+
270
+ def raw_update
271
+ item = self.class.raw_find(id)
272
+ item.load(attributes)
273
+ end
274
+
275
+ def update
276
+ raw_update
277
+ true
278
+ end
279
+
280
+ private
281
+
282
+ def method_missing(method_symbol, *arguments) #:nodoc:
283
+ method_name = method_symbol.to_s
284
+
285
+ if method_name =~ /(=|\?)$/
286
+ case $1
287
+ when "="
288
+ attribute_will_change!($`)
289
+ attributes[$`] = arguments.first
290
+ when "?"
291
+ attributes[$`]
292
+ end
293
+ else
294
+ return attributes[method_name] if attributes.include?(method_name)
295
+ return nil if known_attributes.include?(method_name)
296
+ super
297
+ end
298
+ end
299
+ end
300
+
301
+ class Base
302
+ extend ActiveModel::Naming
303
+ include ActiveModel::Conversion
304
+ include ActiveModel::Serializers::JSON
305
+ include ActiveModel::Serializers::Xml
306
+ include Dirty, Observing, Callbacks, Validations
307
+ include Association::Model
308
+ end
309
+ end
@@ -0,0 +1,23 @@
1
+ module SuperModel
2
+ module Callbacks
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ instance_eval do
7
+ extend ActiveModel::Callbacks
8
+ define_model_callbacks :create, :save, :update, :destroy
9
+ end
10
+
11
+ %w( create save update destroy ).each do |method|
12
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
13
+ def #{method}_with_callbacks(*args, &block)
14
+ _run_#{method}_callbacks do
15
+ #{method}_without_callbacks(*args, &block)
16
+ end
17
+ end
18
+ EOS
19
+ alias_method_chain(method, :callbacks)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module SuperModel
2
+ module Dirty
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Dirty
5
+
6
+ included do
7
+ %w( create update ).each do |method|
8
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
9
+ def #{method}_with_dirty(*args, &block)
10
+ result = #{method}_without_dirty(*args, &block)
11
+ save_previous_changes
12
+ result
13
+ end
14
+ EOS
15
+ alias_method_chain(method, :dirty)
16
+ end
17
+ end
18
+
19
+ def save_previous_changes
20
+ @previously_changed = changes
21
+ @changed_attributes.clear
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ class Array
2
+ unless defined?(deep_dup)
3
+ def deep_dup
4
+ Marshal.load(Marshal.dump(self))
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,91 @@
1
+ require "tempfile"
2
+ require "fileutils"
3
+
4
+ module SuperModel
5
+ module Marshal
6
+ def path
7
+ @path || raise("Provide a path")
8
+ end
9
+
10
+ def path=(p)
11
+ @path = p
12
+ end
13
+
14
+ def klasses
15
+ @klasses ||= []
16
+ end
17
+
18
+ def load
19
+ return unless path
20
+ return unless File.exist?(path)
21
+ data = []
22
+ File.open(path, "rb") do |file|
23
+ begin
24
+ data = ::Marshal.load(file)
25
+ rescue => e
26
+ if defined?(Bowline)
27
+ Bowline::Logging.log_error(e)
28
+ end
29
+ # Lots of errors can occur during
30
+ # marshaling - such as EOF etc
31
+ return false
32
+ end
33
+ end
34
+ data.each do |klass, records|
35
+ klass.marshal_records = records
36
+ end
37
+ true
38
+ end
39
+
40
+ def dump
41
+ return unless path
42
+ tmp_file = Tempfile.new("rbdump")
43
+ tmp_file.binmode
44
+ data = klasses.inject({}) {|hash, klass|
45
+ hash[klass] = klass.marshal_records
46
+ hash
47
+ }
48
+ ::Marshal.dump(data, tmp_file)
49
+ tmp_file.close
50
+ # Atomic serialization - so we never corrupt the db
51
+ FileUtils.mv(tmp_file.path, path)
52
+ true
53
+ end
54
+
55
+ extend self
56
+
57
+ module Model
58
+ def self.included(base)
59
+ base.extend ClassMethods
60
+ Marshal.klasses << base
61
+ end
62
+
63
+ def marshal_dump
64
+ serializable_hash(self.class.marshal)
65
+ end
66
+
67
+ def marshal_load(atts)
68
+ # Can't call load, since class
69
+ # isn't setup properly
70
+ @attributes = atts.with_indifferent_access
71
+ @changed_attributes = {}
72
+ end
73
+
74
+ module ClassMethods
75
+ def marshal(options = nil)
76
+ @marshal = options if options
77
+ @marshal ||= {}
78
+ end
79
+ alias_method :marshal=, :marshal
80
+
81
+ def marshal_records=(records)
82
+ @records = records
83
+ end
84
+
85
+ def marshal_records
86
+ @records
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,21 @@
1
+ module SuperModel
2
+ module Observing
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Observing
5
+
6
+ included do
7
+ %w( create save update destroy ).each do |method|
8
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
9
+ def #{method}_with_notifications(*args, &block)
10
+ notify_observers(:before_#{method})
11
+ if result = #{method}_without_notifications(*args, &block)
12
+ notify_observers(:after_#{method})
13
+ end
14
+ result
15
+ end
16
+ EOS
17
+ alias_method_chain(method, :notifications)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ module SuperModel
2
+ module RandomID
3
+ protected
4
+ def generate_id
5
+ ActiveSupport::SecureRandom.hex(13)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,167 @@
1
+ module SuperModel
2
+ module Redis
3
+ module ClassMethods
4
+ def self.extended(base)
5
+ base.class_eval do
6
+ class_inheritable_array :indexed_attributes
7
+ self.indexed_attributes = []
8
+
9
+ class_inheritable_hash :redis_options
10
+ self.redis_options = {}
11
+ end
12
+ end
13
+
14
+ def namespace
15
+ @namespace ||= self.name.downcase
16
+ end
17
+
18
+ def namespace=(namespace)
19
+ @namespace = namespace
20
+ end
21
+
22
+ def redis
23
+ @redis ||= ::Redis.connect(redis_options)
24
+ end
25
+
26
+ def indexes(*indexes)
27
+ self.indexed_attributes += indexes.map(&:to_s)
28
+ end
29
+
30
+ def redis_key(*args)
31
+ args.unshift(self.namespace)
32
+ args.join(":")
33
+ end
34
+
35
+ def find(id)
36
+ if redis.sismember(redis_key, id.to_s)
37
+ existing(:id => id)
38
+ else
39
+ raise UnknownRecord, "Couldn't find #{self.name} with ID=#{id}"
40
+ end
41
+ end
42
+
43
+ def first
44
+ item_ids = redis.sort(redis_key, :order => "ASC", :limit => [0, 1])
45
+ item_id = item_ids.first
46
+ item_id && existing(:id => item_id)
47
+ end
48
+
49
+ def last
50
+ item_ids = redis.sort(redis_key, :order => "DESC", :limit => [0, 1])
51
+ item_id = item_ids.first
52
+ item_id && existing(:id => item_id)
53
+ end
54
+
55
+ def exists?(id)
56
+ redis.sismember(redis_key, id.to_s)
57
+ end
58
+
59
+ def count
60
+ redis.scard(redis_key)
61
+ end
62
+
63
+ def all
64
+ from_ids(redis.sort(redis_key))
65
+ end
66
+
67
+ def select
68
+ raise "Not implemented"
69
+ end
70
+
71
+ def delete_all
72
+ raise "Not implemented"
73
+ end
74
+
75
+ def find_by_attribute(key, value)
76
+ item_ids = redis.sort(redis_key(key, value.to_s))
77
+ item_id = item_ids.first
78
+ item_id && existing(:id => item_id)
79
+ end
80
+
81
+ def find_all_by_attribute(key, value)
82
+ from_ids(redis.sort(redis_key(key, value.to_s)))
83
+ end
84
+
85
+ protected
86
+ def from_ids(ids)
87
+ ids.map {|id| existing(:id => id) }
88
+ end
89
+
90
+ def existing(atts = {})
91
+ item = self.new(atts)
92
+ item.new_record = false
93
+ item.redis_get
94
+ item
95
+ end
96
+ end
97
+
98
+ module InstanceMethods
99
+ protected
100
+ def raw_destroy
101
+ return if new?
102
+
103
+ destroy_indexes
104
+ redis.srem(self.class.redis_key, self.id)
105
+ redis.del(redis_key)
106
+ end
107
+
108
+ def destroy_indexes
109
+ indexed_attributes.each do |index|
110
+ old_attribute = changes[index].try(:first) || send(index)
111
+ redis.srem(self.class.redis_key(index, old_attribute), id)
112
+ end
113
+ end
114
+
115
+ def create_indexes
116
+ indexed_attributes.each do |index|
117
+ new_attribute = send(index)
118
+ redis.sadd(self.class.redis_key(index, new_attribute), id)
119
+ end
120
+ end
121
+
122
+ def generate_id
123
+ redis.incr(self.class.redis_key(:uid))
124
+ end
125
+
126
+ def indexed_attributes
127
+ attributes.keys & self.class.indexed_attributes
128
+ end
129
+
130
+ def redis
131
+ self.class.redis
132
+ end
133
+
134
+ def redis_key(*args)
135
+ self.class.redis_key(id, *args)
136
+ end
137
+
138
+ def redis_set
139
+ redis.set(redis_key, serializable_hash.to_json)
140
+ end
141
+
142
+ def redis_get
143
+ load(ActiveSupport::JSON.decode(redis.get(redis_key)))
144
+ end
145
+ public :redis_get
146
+
147
+ def raw_create
148
+ redis_set
149
+ create_indexes
150
+ redis.sadd(self.class.redis_key, self.id)
151
+ end
152
+
153
+ def raw_update
154
+ destroy_indexes
155
+ redis_set
156
+ create_indexes
157
+ end
158
+ end
159
+
160
+ module Model
161
+ def self.included(base)
162
+ base.send :include, InstanceMethods
163
+ base.send :extend, ClassMethods
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,53 @@
1
+ module SuperModel
2
+ module Timestamp
3
+ module Model
4
+ def self.included(base)
5
+ base.class_eval do
6
+ attributes :created_at, :updated_at
7
+
8
+ before_create :set_created_at
9
+ before_save :set_updated_at
10
+ end
11
+ end
12
+
13
+ def touch
14
+ set_updated_at
15
+ save!
16
+ end
17
+
18
+ def created_at=(time)
19
+ write_attribute(:created_at, parse_time(time))
20
+ end
21
+
22
+ def updated_at=(time)
23
+ write_attribute(:updated_at, parse_time(time))
24
+ end
25
+
26
+ private
27
+ def parse_time(time)
28
+ return time unless time.is_a?(String)
29
+ if Time.respond_to?(:zone) && Time.zone
30
+ Time.zone.parse(time)
31
+ else
32
+ Time.parse(time)
33
+ end
34
+ end
35
+
36
+ def current_time
37
+ if Time.respond_to?(:current)
38
+ Time.current
39
+ else
40
+ Time.now
41
+ end
42
+ end
43
+
44
+ def set_created_at
45
+ self.created_at = current_time
46
+ end
47
+
48
+ def set_updated_at
49
+ self.updated_at = current_time
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,32 @@
1
+ module SuperModel
2
+ module Validations
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Validations
5
+
6
+ included do
7
+ alias_method_chain :save, :validation
8
+ end
9
+
10
+ def save_with_validation(options = nil)
11
+ perform_validation = case options
12
+ when Hash
13
+ options[:validate] != false
14
+ when NilClass
15
+ true
16
+ else
17
+ options
18
+ end
19
+
20
+ if perform_validation && valid? || !perform_validation
21
+ save_without_validation
22
+ true
23
+ else
24
+ false
25
+ end
26
+ rescue InvalidRecord => error
27
+ false
28
+ end
29
+ end
30
+ end
31
+
32
+ require "supermodel/validations/uniqueness"
@@ -0,0 +1,40 @@
1
+ module SuperModel
2
+ module Validations
3
+ class UniquenessValidator < ActiveModel::EachValidator
4
+ attr_reader :klass
5
+
6
+ def validate_each(record, attribute, value)
7
+ alternate = klass.find_by_attribute(attribute, value)
8
+ return unless alternate
9
+ record.errors.add(attribute, "must be unique", :default => options[:message])
10
+ end
11
+
12
+ def setup(klass)
13
+ @klass = klass
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ # Validates that the specified attribute is unique.
20
+ # class Person < ActiveRecord::Base
21
+ # validates_uniquness_of :essay
22
+ # end
23
+ #
24
+ # Configuration options:
25
+ # * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
26
+ # * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
27
+ # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
28
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
29
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
30
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
31
+ # method, proc or string should return or evaluate to a true or false value.
32
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
33
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
34
+ # method, proc or string should return or evaluate to a true or false value.
35
+ def validates_uniqueness_of(*attr_names)
36
+ validates_with UniquenessValidator, _merge_attributes(attr_names)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{csun-student-affairs-supermodel}
8
+ s.version = "0.1.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Alex MacCaw", "Mani Tadayon"]
12
+ s.date = %q{2010-08-30}
13
+ s.description = %q{In memory DB using ActiveModel}
14
+ s.email = %q{info@eribium.org}
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "MIT-LICENSE",
21
+ "README",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "lib/super_model.rb",
25
+ "lib/supermodel.rb",
26
+ "lib/supermodel/association.rb",
27
+ "lib/supermodel/base.rb",
28
+ "lib/supermodel/callbacks.rb",
29
+ "lib/supermodel/dirty.rb",
30
+ "lib/supermodel/ext/array.rb",
31
+ "lib/supermodel/marshal.rb",
32
+ "lib/supermodel/observing.rb",
33
+ "lib/supermodel/random_id.rb",
34
+ "lib/supermodel/redis.rb",
35
+ "lib/supermodel/timestamp.rb",
36
+ "lib/supermodel/validations.rb",
37
+ "lib/supermodel/validations/uniqueness.rb",
38
+ "supermodel.gemspec"
39
+ ]
40
+ s.homepage = %q{https://github.com/csun-student-affairs/supermodel}
41
+ s.rdoc_options = ["--charset=UTF-8"]
42
+ s.require_paths = ["lib"]
43
+ s.rubygems_version = %q{1.3.7}
44
+ s.summary = %q{In memory DB using ActiveModel}
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::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<activemodel>, ["~> 3.0.0"])
52
+ else
53
+ s.add_dependency(%q<activemodel>, ["~> 3.0.0"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<activemodel>, ["~> 3.0.0"])
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: csun-student-affairs-supermodel
3
+ version: !ruby/object:Gem::Version
4
+ hash: 17
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 5
10
+ version: 0.1.5
11
+ platform: ruby
12
+ authors:
13
+ - Alex MacCaw
14
+ - Mani Tadayon
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-08-30 00:00:00 -07:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: activemodel
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 7
31
+ segments:
32
+ - 3
33
+ - 0
34
+ - 0
35
+ version: 3.0.0
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ description: In memory DB using ActiveModel
39
+ email: info@eribium.org
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files:
45
+ - README
46
+ files:
47
+ - .gitignore
48
+ - MIT-LICENSE
49
+ - README
50
+ - Rakefile
51
+ - VERSION
52
+ - lib/super_model.rb
53
+ - lib/supermodel.rb
54
+ - lib/supermodel/association.rb
55
+ - lib/supermodel/base.rb
56
+ - lib/supermodel/callbacks.rb
57
+ - lib/supermodel/dirty.rb
58
+ - lib/supermodel/ext/array.rb
59
+ - lib/supermodel/marshal.rb
60
+ - lib/supermodel/observing.rb
61
+ - lib/supermodel/random_id.rb
62
+ - lib/supermodel/redis.rb
63
+ - lib/supermodel/timestamp.rb
64
+ - lib/supermodel/validations.rb
65
+ - lib/supermodel/validations/uniqueness.rb
66
+ - supermodel.gemspec
67
+ has_rdoc: true
68
+ homepage: https://github.com/csun-student-affairs/supermodel
69
+ licenses: []
70
+
71
+ post_install_message:
72
+ rdoc_options:
73
+ - --charset=UTF-8
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ requirements: []
95
+
96
+ rubyforge_project:
97
+ rubygems_version: 1.3.7
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: In memory DB using ActiveModel
101
+ test_files: []
102
+