mem_model 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.
- checksums.yaml +15 -0
- data/.gemtest +0 -0
- data/CHANGELOG.md +5 -0
- data/CONTRIBUTING.md +46 -0
- data/LICENSE.md +22 -0
- data/Manifest.txt +15 -0
- data/README.md +106 -0
- data/Rakefile +32 -0
- data/lib/mem_model/base.rb +439 -0
- data/lib/mem_model/version.rb +3 -0
- data/lib/mem_model.rb +10 -0
- data/mem_model.gemspec +47 -0
- data/test/helper.rb +7 -0
- data/test/mem_model/test_active_model_lint.rb +13 -0
- data/test/mem_model/test_base.rb +11 -0
- data/test/mem_model/test_persistence.rb +37 -0
- data/test/test_version.rb +7 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MGM2ZmU3YzU3NDA4MjRlNjcyMTBmYzcxN2UyNmM1OGJhNDA0NGJkMA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZDg1OWNiMDVlNjQ4YWNhOTI3Mzc1Y2FhMzdiZDI4ZDhkOGYzNWEwNg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YjA1N2JlZTlhMDg2MzJhZjQ2N2I5ZDdhZmE1YWQxNDE3ZjMxZmU4OTkzYjky
|
10
|
+
ZmNkYTg3NDA1Zjk3OWJmOGUyYmEzNzhmZTg3NGVjNWI2NTRlNTk2Nzk1NmUz
|
11
|
+
Njk2ZDdmMzYzMDg3ZDM0YTVlOTZkMGQzZGY3MGJhMDEwODllMzM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
M2UwODI1OGY5ZjNkZmUwNWNiNjY2N2U2NGRmODc2OWY5ZGI3YzA3YmRlNzli
|
14
|
+
NDE4OTIxMjc5NTlmZWZlZjU0NzVjOGYxNWVmZjJkNGQ5ZTQ0MTA1YTE4MDlj
|
15
|
+
MmFjNTA2YTFmY2MwMjM5ZTE5ZTcwNDkyMTM3YzQ4MGUwYTY3ZTU=
|
data/.gemtest
ADDED
File without changes
|
data/CHANGELOG.md
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
## Contributing
|
2
|
+
In the spirit of [free software][free-sw], **everyone** is encouraged to help
|
3
|
+
improve this project.
|
4
|
+
|
5
|
+
[free-sw]: http://www.fsf.org/licensing/essays/free-sw.html
|
6
|
+
|
7
|
+
Here are some ways *you* can contribute:
|
8
|
+
|
9
|
+
* by using alpha, beta, and prerelease versions
|
10
|
+
* by reporting bugs
|
11
|
+
* by suggesting new features
|
12
|
+
* by writing or editing documentation
|
13
|
+
* by writing specifications
|
14
|
+
* by writing code ( **no patch is too small**: fix typos, add comments, clean up
|
15
|
+
inconsistent whitespace)
|
16
|
+
* by refactoring code
|
17
|
+
* by closing [issues][]
|
18
|
+
* by reviewing patches
|
19
|
+
|
20
|
+
[issues]: https://github.com/johnnyt/mem_model/issues
|
21
|
+
|
22
|
+
## Submitting an Issue
|
23
|
+
We use the [GitHub issue tracker][issues] to track bugs and features. Before
|
24
|
+
submitting a bug report or feature request, check to make sure it hasn't
|
25
|
+
already been submitted. When submitting a bug report, please include a [Gist][]
|
26
|
+
that includes a stack trace and any details that may be necessary to reproduce
|
27
|
+
the bug, including your gem version, Ruby version, and operating system.
|
28
|
+
Ideally, a bug report should include a pull request with failing specs.
|
29
|
+
|
30
|
+
[gist]: https://gist.github.com/
|
31
|
+
|
32
|
+
## Submitting a Pull Request
|
33
|
+
1. [Fork the repository.][fork]
|
34
|
+
2. [Create a topic branch.][branch]
|
35
|
+
3. Add specs for your unimplemented feature or bug fix.
|
36
|
+
4. Run `bundle exec rake spec`. If your specs pass, return to step 3.
|
37
|
+
5. Implement your feature or bug fix.
|
38
|
+
6. Run `bundle exec rake spec`. If your specs fail, return to step 5.
|
39
|
+
7. Run `open coverage/index.html`. If your changes are not completely covered
|
40
|
+
by your tests, return to step 3.
|
41
|
+
8. Add, commit, and push your changes.
|
42
|
+
9. [Submit a pull request.][pr]
|
43
|
+
|
44
|
+
[fork]: http://help.github.com/fork-a-repo/
|
45
|
+
[branch]: http://learn.github.com/p/branching.html
|
46
|
+
[pr]: http://help.github.com/send-pull-requests/
|
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 JohnnyT
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
CHANGELOG.md
|
2
|
+
CONTRIBUTING.md
|
3
|
+
LICENSE.md
|
4
|
+
Manifest.txt
|
5
|
+
README.md
|
6
|
+
Rakefile
|
7
|
+
lib/mem_model.rb
|
8
|
+
lib/mem_model/base.rb
|
9
|
+
lib/mem_model/version.rb
|
10
|
+
mem_model.gemspec
|
11
|
+
test/helper.rb
|
12
|
+
test/mem_model/test_active_model_lint.rb
|
13
|
+
test/mem_model/test_base.rb
|
14
|
+
test/mem_model/test_persistence.rb
|
15
|
+
test/test_version.rb
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# MemModel
|
2
|
+
|
3
|
+
MemModel persists Ruby objects using MagLev as a data storage engine. It is an
|
4
|
+
ActiveModel compliant implementation so it works stand-alone or in Rails 3 as a
|
5
|
+
drop-in replacement for ActiveRecord or DataMapper.
|
6
|
+
|
7
|
+
If MemModel is used in non-MagLev platforms, objects will be persisted to
|
8
|
+
in-memory sets.
|
9
|
+
|
10
|
+
|
11
|
+
### Installation
|
12
|
+
|
13
|
+
Run `gem install mem_model` to install the gem on its own.
|
14
|
+
|
15
|
+
Or you can add the following to your Gemfile and run the `bundle` command to
|
16
|
+
install it.
|
17
|
+
|
18
|
+
gem 'mem_model'
|
19
|
+
|
20
|
+
|
21
|
+
##### Development
|
22
|
+
|
23
|
+
If you'd like to work on this project first make sure you have the `hoe` gem
|
24
|
+
installed (`gem install hoe` if not), then run `rake newb` from the
|
25
|
+
`mem_model` project directory.
|
26
|
+
|
27
|
+
|
28
|
+
### Example
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require 'mem_model'
|
32
|
+
|
33
|
+
class Account
|
34
|
+
include MemModel::Base
|
35
|
+
attribute :balance
|
36
|
+
validates_presence_of :balance
|
37
|
+
end
|
38
|
+
|
39
|
+
checking = Account.new #=> #<Account balance: nil, id: "ACC-123...">
|
40
|
+
checking.valid? #=> false
|
41
|
+
checking.balance = 100 #=> 100
|
42
|
+
checking.valid? #=> true
|
43
|
+
|
44
|
+
checking.save #=> "ACC-123..."
|
45
|
+
checking.new? #=> false
|
46
|
+
Account.size #=> 1
|
47
|
+
Account.first #=> #<Account balance: 100, id: "ACC-123...">
|
48
|
+
```
|
49
|
+
|
50
|
+
|
51
|
+
### Similar stuff
|
52
|
+
|
53
|
+
This stuff has been very helpful in working on MemModel.
|
54
|
+
|
55
|
+
##### Projects
|
56
|
+
|
57
|
+
* [Tesla][tesla]: MagLev niceties like models... and signs
|
58
|
+
* [Supermodel][supermodel]. Simple in-memory database using ActiveModel. Primarily
|
59
|
+
developed for Bowline applications.
|
60
|
+
* [ActiveAttr][active_attr]: What ActiveModel left out
|
61
|
+
* [MagLevRecord][maglevrecord]: MagLev persistence with an
|
62
|
+
ActiveRecord-like interface!
|
63
|
+
|
64
|
+
##### Docs
|
65
|
+
|
66
|
+
* [MagLev Persistence API][persistence_api]: Overview of the persistence API inMagLev
|
67
|
+
* [Persistence README][persistence_readme]: MagLev Persistence Examples overview
|
68
|
+
* [Migrations][migrations]: The Problem: Evolution of Persistent Data Formats (Schema Changes)
|
69
|
+
* [Migrations DSL][migrations_dsl]: Requirements for a Migration DSL which would allow
|
70
|
+
"rails-like" migrations for MagLev.
|
71
|
+
|
72
|
+
##### Examples
|
73
|
+
|
74
|
+
* [Blog Data Migration Example][blog]: create a blog, add attributes, migrate existing
|
75
|
+
instances
|
76
|
+
* [K-d Tree][kd_tree]: includes a Google Maps example using zipcodes
|
77
|
+
* [Indexing][indexing]: use unordered collections with MagLev's built-in indexing support
|
78
|
+
* [Points][points]: shows two approaches of handling data migration using a Point class.
|
79
|
+
|
80
|
+
|
81
|
+
### Contributing
|
82
|
+
|
83
|
+
If you'd like to contribute to MemModel, that's awesome. There's a guide to contributing
|
84
|
+
(both code and general help) over in [CONTRIBUTING.md](CONTRIBUTING.md)
|
85
|
+
|
86
|
+
### Development
|
87
|
+
|
88
|
+
To see what has changed in recent versions, see the [CHANGELOG.md](CHANGELOG.md).
|
89
|
+
|
90
|
+
[hoe]: http://www.zenspider.com/projects/hoe.html
|
91
|
+
|
92
|
+
[tesla]: https://github.com/jc00ke/tesla
|
93
|
+
[supermodel]: https://github.com/maccman/supermodel
|
94
|
+
[active_attr]: https://github.com/cgriego/active_attr
|
95
|
+
[maglevrecord]: https://github.com/knub/maglevrecord
|
96
|
+
|
97
|
+
[persistence_api]: https://github.com/MagLev/maglev/blob/master-1.9/docs/persistence-api.rdoc
|
98
|
+
[persistence_readme]: https://github.com/MagLev/maglev/blob/master-1.9/examples/persistence/Persistence-README.rdoc
|
99
|
+
[migrations]: https://github.com/MagLev/maglev/blob/master-1.9/examples/persistence/migrations/migrations.org
|
100
|
+
[migrations_dsl]: https://github.com/MagLev/maglev/blob/master-1.9/examples/persistence/migrations/migration-dsl.org
|
101
|
+
|
102
|
+
[migration_examples]: https://github.com/MagLev/maglev/blob/master-1.9/examples/persistence/migrations/example/example.rdoc
|
103
|
+
[kd_tree]: https://github.com/MagLev/maglev/blob/master-1.9/examples/persistence/kdtree/KDTree-README.rdoc
|
104
|
+
[indexing]: https://github.com/MagLev/maglev/blob/master-1.9/examples/persistence/indexing/Indexing-README.rdoc
|
105
|
+
[blog]: https://github.com/MagLev/maglev/blob/master-1.9/examples/persistence/migrations/example/example.rdoc
|
106
|
+
[points]: https://github.com/MagLev/maglev/blob/master-1.9/examples/persistence/migrations/example2/README.rdoc
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
#require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.plugin :git
|
7
|
+
Hoe.plugin :gemspec
|
8
|
+
|
9
|
+
Hoe.spec 'mem_model' do
|
10
|
+
developer 'JohnnyT', 'ubergeek3141@gmail.com'
|
11
|
+
|
12
|
+
self.summary = 'ActiveModel-compliant MagLev persistence'
|
13
|
+
self.description = <<-DESC.strip.gsub(/\n\s+/, " ")
|
14
|
+
MemModel persists Ruby objects using MagLev as a data storage engine. It's an
|
15
|
+
ActiveModel implementation so it works stand-alone or in Rails 3 as a drop-in
|
16
|
+
replacement for ActiveRecord or DataMapper. If MemModel is used in non-MagLev
|
17
|
+
platforms, objects will be persisted to in-memory sets.
|
18
|
+
DESC
|
19
|
+
self.urls = ['https://github.com/johnnyt/mem_model']
|
20
|
+
|
21
|
+
self.history_file = 'CHANGELOG.md'
|
22
|
+
self.readme_file = 'README.md'
|
23
|
+
self.testlib = :none # actually minitest. this helps the gem to be loaded
|
24
|
+
|
25
|
+
license 'MIT'
|
26
|
+
|
27
|
+
dependency 'active_attr', '>= 0.8.2'
|
28
|
+
dependency 'activesupport', '= 3.2.15' # MagLev optimized version
|
29
|
+
dependency 'minitest', '= 5.0.2', :dev
|
30
|
+
end
|
31
|
+
|
32
|
+
# vim: syntax=ruby
|
@@ -0,0 +1,439 @@
|
|
1
|
+
module MemModel
|
2
|
+
class UnknownRecord < ArgumentError; end
|
3
|
+
class InvalidRecord < StandardError; end
|
4
|
+
|
5
|
+
module Base
|
6
|
+
def self.included(base)
|
7
|
+
base.send :include, ActiveAttr::Model
|
8
|
+
base.send :extend, Enumerable
|
9
|
+
base.send :extend, ClassMethods
|
10
|
+
base.class_eval do
|
11
|
+
attribute :id
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
extend Forwardable
|
17
|
+
def_delegators :store, :each, :size, :entries
|
18
|
+
alias_method :all, :entries
|
19
|
+
|
20
|
+
def store
|
21
|
+
@store ||= Set.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def generate_id
|
25
|
+
[guid_prefix, '-', SecureRandom.uuid].join.upcase
|
26
|
+
end
|
27
|
+
|
28
|
+
def guid_prefix
|
29
|
+
self.name[0...3]
|
30
|
+
end
|
31
|
+
|
32
|
+
def store
|
33
|
+
@store ||= Set.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def find_by_id(id)
|
37
|
+
store.detect{ |r| id == r.id } || raise(UnknownRecord, "Couldn't find #{self.name} with ID=#{id}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_all_by_id(substring)
|
41
|
+
records = store.select{ |r| r.id.include?(substring.to_s.upcase) }
|
42
|
+
records && records.size == 1 ?
|
43
|
+
records.first :
|
44
|
+
records || raise(UnknownRecord, "Couldn't find #{self.name} with ID=#{id}")
|
45
|
+
end
|
46
|
+
|
47
|
+
def [](substring)
|
48
|
+
store.detect{ |r| r.id.include?(substring.to_s.upcase) } || raise(UnknownRecord, "Couldn't find #{self.name} with ID=~#{substring}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def find(id)
|
52
|
+
store.detect{ |r| r.id == id } || raise(UnknownRecord, "Couldn't find #{self.name} with ID=#{id}")
|
53
|
+
end
|
54
|
+
|
55
|
+
def exists?(id)
|
56
|
+
store.any?{ |record| record.id == id }
|
57
|
+
end
|
58
|
+
|
59
|
+
def update(id, atts)
|
60
|
+
find(id).update_attributes(atts)
|
61
|
+
end
|
62
|
+
|
63
|
+
def destroy(id)
|
64
|
+
find(id).destroy
|
65
|
+
end
|
66
|
+
|
67
|
+
# Removes all records and executes
|
68
|
+
# destroy callbacks.
|
69
|
+
def destroy_all
|
70
|
+
all.each {|r| r.destroy }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Removes all records without executing
|
74
|
+
# destroy callbacks.
|
75
|
+
def delete_all
|
76
|
+
store.clear
|
77
|
+
end
|
78
|
+
|
79
|
+
# Create a new record.
|
80
|
+
# Example:
|
81
|
+
# create(:name => "foo", :id => 1)
|
82
|
+
def create(atts = {})
|
83
|
+
rec = self.new(atts)
|
84
|
+
rec.save && rec
|
85
|
+
end
|
86
|
+
|
87
|
+
def create!(*args)
|
88
|
+
create(*args) || raise(InvalidRecord)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize(attributes = {})
|
93
|
+
@persisted = false
|
94
|
+
self.id = self.class.generate_id
|
95
|
+
load(attributes)
|
96
|
+
end
|
97
|
+
|
98
|
+
def load(attributes) #:nodoc:
|
99
|
+
return unless attributes
|
100
|
+
attributes.each do |(name, value)|
|
101
|
+
self.send("#{name}=".to_sym, value)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_model
|
106
|
+
return self
|
107
|
+
end
|
108
|
+
|
109
|
+
def new?
|
110
|
+
!exists?
|
111
|
+
end
|
112
|
+
alias_method :new_record?, :new?
|
113
|
+
|
114
|
+
def exists?
|
115
|
+
@persisted == true
|
116
|
+
end
|
117
|
+
|
118
|
+
def save
|
119
|
+
new? ? create : update
|
120
|
+
end
|
121
|
+
|
122
|
+
def save!
|
123
|
+
save || raise(InvalidRecord)
|
124
|
+
end
|
125
|
+
|
126
|
+
def destroy
|
127
|
+
self.class.records.delete(self.id)
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
def create
|
132
|
+
self.id ||= generate_id
|
133
|
+
@persisted = true
|
134
|
+
self.class.store << self
|
135
|
+
self.id
|
136
|
+
end
|
137
|
+
|
138
|
+
def update
|
139
|
+
item = self.class.raw_find(id)
|
140
|
+
item.load(attributes)
|
141
|
+
true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class SuperModel
|
146
|
+
|
147
|
+
class << self
|
148
|
+
attr_accessor(:primary_key) #:nodoc:
|
149
|
+
|
150
|
+
def primary_key
|
151
|
+
@primary_key ||= 'id'
|
152
|
+
end
|
153
|
+
|
154
|
+
def collection(&block)
|
155
|
+
@collection ||= Class.new(Array)
|
156
|
+
@collection.class_eval(&block) if block_given?
|
157
|
+
@collection
|
158
|
+
end
|
159
|
+
|
160
|
+
def attributes(*attributes)
|
161
|
+
self.known_attributes |= attributes.map(&:to_s)
|
162
|
+
end
|
163
|
+
|
164
|
+
def records
|
165
|
+
@records ||= {}
|
166
|
+
end
|
167
|
+
|
168
|
+
def find_by_attribute(name, value) #:nodoc:
|
169
|
+
item = records.values.find {|r| r.send(name) == value }
|
170
|
+
item && item.dup
|
171
|
+
end
|
172
|
+
|
173
|
+
def find_all_by_attribute(name, value) #:nodoc:
|
174
|
+
items = records.values.select {|r| r.send(name) == value }
|
175
|
+
collection.new(items.deep_dup)
|
176
|
+
end
|
177
|
+
|
178
|
+
def raw_find(id) #:nodoc:
|
179
|
+
records[id] || raise(UnknownRecord, "Couldn't find #{self.name} with ID=#{id}")
|
180
|
+
end
|
181
|
+
|
182
|
+
# Find record by ID, or raise.
|
183
|
+
def find(id)
|
184
|
+
item = raw_find(id)
|
185
|
+
item && item.dup
|
186
|
+
end
|
187
|
+
alias :[] :find
|
188
|
+
|
189
|
+
def first
|
190
|
+
item = records.values[0]
|
191
|
+
item && item.dup
|
192
|
+
end
|
193
|
+
|
194
|
+
def last
|
195
|
+
item = records.values[-1]
|
196
|
+
item && item.dup
|
197
|
+
end
|
198
|
+
|
199
|
+
def where(options)
|
200
|
+
items = records.values.select do |r|
|
201
|
+
options.all? do |k, v|
|
202
|
+
if v.is_a?(Enumerable)
|
203
|
+
v.include?(r.send(k))
|
204
|
+
else
|
205
|
+
r.send(k) == v
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
collection.new(items.deep_dup)
|
210
|
+
end
|
211
|
+
|
212
|
+
def exists?(id)
|
213
|
+
records.has_key?(id)
|
214
|
+
end
|
215
|
+
|
216
|
+
def count
|
217
|
+
records.length
|
218
|
+
end
|
219
|
+
|
220
|
+
def all
|
221
|
+
collection.new(records.values.deep_dup)
|
222
|
+
end
|
223
|
+
|
224
|
+
def select(&block)
|
225
|
+
collection.new(records.values.select(&block).deep_dup)
|
226
|
+
end
|
227
|
+
|
228
|
+
def update(id, atts)
|
229
|
+
find(id).update_attributes(atts)
|
230
|
+
end
|
231
|
+
|
232
|
+
def destroy(id)
|
233
|
+
find(id).destroy
|
234
|
+
end
|
235
|
+
|
236
|
+
# Removes all records and executes
|
237
|
+
# destroy callbacks.
|
238
|
+
def destroy_all
|
239
|
+
all.each {|r| r.destroy }
|
240
|
+
end
|
241
|
+
|
242
|
+
# Removes all records without executing
|
243
|
+
# destroy callbacks.
|
244
|
+
def delete_all
|
245
|
+
records.clear
|
246
|
+
end
|
247
|
+
|
248
|
+
# Create a new record.
|
249
|
+
# Example:
|
250
|
+
# create(:name => "foo", :id => 1)
|
251
|
+
def create(atts = {})
|
252
|
+
rec = self.new(atts)
|
253
|
+
rec.save && rec
|
254
|
+
end
|
255
|
+
|
256
|
+
def create!(*args)
|
257
|
+
create(*args) || raise(InvalidRecord)
|
258
|
+
end
|
259
|
+
|
260
|
+
def method_missing(method_symbol, *args) #:nodoc:
|
261
|
+
method_name = method_symbol.to_s
|
262
|
+
|
263
|
+
if method_name =~ /^find_by_(\w+)!/
|
264
|
+
send("find_by_#{$1}", *args) || raise(UnknownRecord)
|
265
|
+
elsif method_name =~ /^find_by_(\w+)/
|
266
|
+
find_by_attribute($1, args.first)
|
267
|
+
elsif method_name =~ /^find_or_create_by_(\w+)/
|
268
|
+
send("find_by_#{$1}", *args) || create($1 => args.first)
|
269
|
+
elsif method_name =~ /^find_all_by_(\w+)/
|
270
|
+
find_all_by_attribute($1, args.first)
|
271
|
+
else
|
272
|
+
super
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
attr_accessor :attributes
|
278
|
+
attr_writer :new_record
|
279
|
+
|
280
|
+
def known_attributes
|
281
|
+
self.class.known_attributes | self.attributes.keys.map(&:to_s)
|
282
|
+
end
|
283
|
+
|
284
|
+
def initialize(attributes = {})
|
285
|
+
@new_record = true
|
286
|
+
@attributes = {}.with_indifferent_access
|
287
|
+
@attributes.merge!(known_attributes.inject({}) {|h, n| h[n] = nil; h })
|
288
|
+
@changed_attributes = {}
|
289
|
+
load(attributes)
|
290
|
+
end
|
291
|
+
|
292
|
+
def clone
|
293
|
+
cloned = attributes.reject {|k,v| k == self.class.primary_key }
|
294
|
+
cloned = cloned.inject({}) do |attrs, (k, v)|
|
295
|
+
attrs[k] = v.clone
|
296
|
+
attrs
|
297
|
+
end
|
298
|
+
self.class.new(cloned)
|
299
|
+
end
|
300
|
+
|
301
|
+
def new?
|
302
|
+
@new_record || false
|
303
|
+
end
|
304
|
+
alias :new_record? :new?
|
305
|
+
|
306
|
+
# Gets the <tt>\id</tt> attribute of the item.
|
307
|
+
def id
|
308
|
+
attributes[self.class.primary_key]
|
309
|
+
end
|
310
|
+
|
311
|
+
# Sets the <tt>\id</tt> attribute of the item.
|
312
|
+
def id=(id)
|
313
|
+
attributes[self.class.primary_key] = id
|
314
|
+
end
|
315
|
+
|
316
|
+
def ==(other)
|
317
|
+
other.equal?(self) || (other.instance_of?(self.class) && other.id == id)
|
318
|
+
end
|
319
|
+
|
320
|
+
# Tests for equality (delegates to ==).
|
321
|
+
def eql?(other)
|
322
|
+
self == other
|
323
|
+
end
|
324
|
+
|
325
|
+
def hash
|
326
|
+
id.hash
|
327
|
+
end
|
328
|
+
|
329
|
+
def dup
|
330
|
+
self.class.new.tap do |base|
|
331
|
+
base.attributes = attributes
|
332
|
+
base.new_record = new_record?
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def save
|
337
|
+
new? ? create : update
|
338
|
+
end
|
339
|
+
|
340
|
+
def save!
|
341
|
+
save || raise(InvalidRecord)
|
342
|
+
end
|
343
|
+
|
344
|
+
def exists?
|
345
|
+
!new?
|
346
|
+
end
|
347
|
+
alias_method :persisted?, :exists?
|
348
|
+
|
349
|
+
def load(attributes) #:nodoc:
|
350
|
+
return unless attributes
|
351
|
+
attributes.each do |(name, value)|
|
352
|
+
self.send("#{name}=".to_sym, value)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def reload
|
357
|
+
return self if new?
|
358
|
+
item = self.class.find(id)
|
359
|
+
load(item.attributes)
|
360
|
+
return self
|
361
|
+
end
|
362
|
+
|
363
|
+
def update_attribute(name, value)
|
364
|
+
self.send("#{name}=".to_sym, value)
|
365
|
+
self.save
|
366
|
+
end
|
367
|
+
|
368
|
+
def update_attributes(attributes)
|
369
|
+
load(attributes) && save
|
370
|
+
end
|
371
|
+
|
372
|
+
def update_attributes!(attributes)
|
373
|
+
update_attributes(attributes) || raise(InvalidRecord)
|
374
|
+
end
|
375
|
+
|
376
|
+
def has_attribute?(name)
|
377
|
+
@attributes.has_key?(name)
|
378
|
+
end
|
379
|
+
|
380
|
+
alias_method :respond_to_without_attributes?, :respond_to?
|
381
|
+
|
382
|
+
def respond_to?(method, include_priv = false)
|
383
|
+
method_name = method.to_s
|
384
|
+
if attributes.nil?
|
385
|
+
super
|
386
|
+
elsif known_attributes.include?(method_name)
|
387
|
+
true
|
388
|
+
elsif method_name =~ /(?:=|\?)$/ && attributes.include?($`)
|
389
|
+
true
|
390
|
+
else
|
391
|
+
super
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def destroy
|
396
|
+
raw_destroy
|
397
|
+
self
|
398
|
+
end
|
399
|
+
|
400
|
+
protected
|
401
|
+
def read_attribute(name)
|
402
|
+
@attributes[name]
|
403
|
+
end
|
404
|
+
|
405
|
+
def write_attribute(name, value)
|
406
|
+
@attributes[name] = value
|
407
|
+
end
|
408
|
+
|
409
|
+
def generate_id
|
410
|
+
object_id
|
411
|
+
end
|
412
|
+
|
413
|
+
def raw_destroy
|
414
|
+
self.class.records.delete(self.id)
|
415
|
+
end
|
416
|
+
|
417
|
+
def raw_create
|
418
|
+
self.class.records[self.id] = self.dup
|
419
|
+
end
|
420
|
+
|
421
|
+
def create
|
422
|
+
self.id ||= generate_id
|
423
|
+
self.new_record = false
|
424
|
+
raw_create
|
425
|
+
self.id
|
426
|
+
end
|
427
|
+
|
428
|
+
def raw_update
|
429
|
+
item = self.class.raw_find(id)
|
430
|
+
item.load(attributes)
|
431
|
+
end
|
432
|
+
|
433
|
+
def update
|
434
|
+
raw_update
|
435
|
+
true
|
436
|
+
end
|
437
|
+
|
438
|
+
end
|
439
|
+
end
|
data/lib/mem_model.rb
ADDED
data/mem_model.gemspec
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# stub: mem_model 0.1.0.20131220134709 ruby lib
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "mem_model"
|
6
|
+
s.version = "0.1.0.20131220134709"
|
7
|
+
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
|
+
s.authors = ["JohnnyT"]
|
10
|
+
s.date = "2013-12-20"
|
11
|
+
s.description = "MemModel persists Ruby objects using MagLev as a data storage engine. It's an ActiveModel implementation so it works stand-alone or in Rails 3 as a drop-in replacement for ActiveRecord or DataMapper. If MemModel is used in non-MagLev platforms, objects will be persisted to in-memory sets."
|
12
|
+
s.email = ["ubergeek3141@gmail.com"]
|
13
|
+
s.extra_rdoc_files = ["CHANGELOG.md", "CONTRIBUTING.md", "LICENSE.md", "Manifest.txt", "README.md"]
|
14
|
+
s.files = ["CHANGELOG.md", "CONTRIBUTING.md", "LICENSE.md", "Manifest.txt", "README.md", "Rakefile", "lib/mem_model.rb", "lib/mem_model/base.rb", "lib/mem_model/version.rb", "mem_model.gemspec", "test/helper.rb", "test/mem_model/test_active_model_lint.rb", "test/mem_model/test_base.rb", "test/mem_model/test_persistence.rb", "test/test_version.rb", ".gemtest"]
|
15
|
+
s.homepage = "https://github.com/johnnyt/mem_model"
|
16
|
+
s.licenses = ["MIT"]
|
17
|
+
s.rdoc_options = ["--main", "README.md"]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.rubyforge_project = "mem_model"
|
20
|
+
s.rubygems_version = "2.1.11"
|
21
|
+
s.summary = "ActiveModel-compliant MagLev persistence"
|
22
|
+
s.test_files = ["test/mem_model/test_active_model_lint.rb", "test/mem_model/test_base.rb", "test/mem_model/test_persistence.rb", "test/test_version.rb"]
|
23
|
+
|
24
|
+
if s.respond_to? :specification_version then
|
25
|
+
s.specification_version = 4
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
28
|
+
s.add_runtime_dependency(%q<active_attr>, [">= 0.8.2"])
|
29
|
+
s.add_runtime_dependency(%q<activesupport>, ["= 3.2.15"])
|
30
|
+
s.add_development_dependency(%q<rdoc>, ["~> 4.0"])
|
31
|
+
s.add_development_dependency(%q<minitest>, ["= 5.0.2"])
|
32
|
+
s.add_development_dependency(%q<hoe>, ["~> 3.7"])
|
33
|
+
else
|
34
|
+
s.add_dependency(%q<active_attr>, [">= 0.8.2"])
|
35
|
+
s.add_dependency(%q<activesupport>, ["= 3.2.15"])
|
36
|
+
s.add_dependency(%q<rdoc>, ["~> 4.0"])
|
37
|
+
s.add_dependency(%q<minitest>, ["= 5.0.2"])
|
38
|
+
s.add_dependency(%q<hoe>, ["~> 3.7"])
|
39
|
+
end
|
40
|
+
else
|
41
|
+
s.add_dependency(%q<active_attr>, [">= 0.8.2"])
|
42
|
+
s.add_dependency(%q<activesupport>, ["= 3.2.15"])
|
43
|
+
s.add_dependency(%q<rdoc>, ["~> 4.0"])
|
44
|
+
s.add_dependency(%q<minitest>, ["= 5.0.2"])
|
45
|
+
s.add_dependency(%q<hoe>, ["~> 3.7"])
|
46
|
+
end
|
47
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path('../../helper', __FILE__)
|
2
|
+
|
3
|
+
class TestPersistence < Minitest::Test
|
4
|
+
class Account
|
5
|
+
include MemModel::Base
|
6
|
+
attribute :balance
|
7
|
+
end
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@account = Account.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
@account = nil
|
15
|
+
Account.delete_all
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_new_model_is_new_record
|
19
|
+
assert @account.new_record?
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_saved_model_is_persisted
|
23
|
+
@account.save
|
24
|
+
refute @account.new_record?
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_find_nonexistent_record
|
28
|
+
assert_raises(MemModel::UnknownRecord) do
|
29
|
+
Account.find 'wat'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_find
|
34
|
+
@account.save
|
35
|
+
assert_equal @account, Account.find(@account.id)
|
36
|
+
end
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mem_model
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- JohnnyT
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: active_attr
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.2.15
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.2.15
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rdoc
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 5.0.2
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 5.0.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hoe
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.7'
|
83
|
+
description: MemModel persists Ruby objects using MagLev as a data storage engine.
|
84
|
+
It's an ActiveModel implementation so it works stand-alone or in Rails 3 as a drop-in
|
85
|
+
replacement for ActiveRecord or DataMapper. If MemModel is used in non-MagLev platforms,
|
86
|
+
objects will be persisted to in-memory sets.
|
87
|
+
email:
|
88
|
+
- ubergeek3141@gmail.com
|
89
|
+
executables: []
|
90
|
+
extensions: []
|
91
|
+
extra_rdoc_files:
|
92
|
+
- CHANGELOG.md
|
93
|
+
- CONTRIBUTING.md
|
94
|
+
- LICENSE.md
|
95
|
+
- Manifest.txt
|
96
|
+
- README.md
|
97
|
+
files:
|
98
|
+
- CHANGELOG.md
|
99
|
+
- CONTRIBUTING.md
|
100
|
+
- LICENSE.md
|
101
|
+
- Manifest.txt
|
102
|
+
- README.md
|
103
|
+
- Rakefile
|
104
|
+
- lib/mem_model.rb
|
105
|
+
- lib/mem_model/base.rb
|
106
|
+
- lib/mem_model/version.rb
|
107
|
+
- mem_model.gemspec
|
108
|
+
- test/helper.rb
|
109
|
+
- test/mem_model/test_active_model_lint.rb
|
110
|
+
- test/mem_model/test_base.rb
|
111
|
+
- test/mem_model/test_persistence.rb
|
112
|
+
- test/test_version.rb
|
113
|
+
- .gemtest
|
114
|
+
homepage: https://github.com/johnnyt/mem_model
|
115
|
+
licenses:
|
116
|
+
- MIT
|
117
|
+
metadata: {}
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options:
|
120
|
+
- --main
|
121
|
+
- README.md
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project: mem_model
|
136
|
+
rubygems_version: 2.1.11
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: ActiveModel-compliant MagLev persistence
|
140
|
+
test_files:
|
141
|
+
- test/mem_model/test_active_model_lint.rb
|
142
|
+
- test/mem_model/test_base.rb
|
143
|
+
- test/mem_model/test_persistence.rb
|
144
|
+
- test/test_version.rb
|