mementus 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZDQ4NmJhMTc1NTk1ZTk3MWExOWQyMDdiNWFlODE0M2RhN2MyOGFjMA==
4
+ Mzg4MDg1OTYxZjllZGJiMWM1OGFjODc5MTcyNDQ1YzIyYmU3NzgwMA==
5
5
  data.tar.gz: !binary |-
6
- NWZiZjNiMzdjNzNjNjEzNDQ3MmZmOWRhZjI3OTAzOTI0YTk5ZWQzNg==
6
+ YjcyZWZiNGE0ZTllMWU2MzI1Nzc1Yjg1ZDlkMGUzZGQxMTkyOWJhNg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OTUxYjFiNzY1YzUxMjczMTQ5MzhkNjQzZTZhM2NjMGQwNzhhNjEyYzVkYzZm
10
- ZWNmMGYwNGZkZmU3ZDRkNWQ0ZmIzZWFlMTg1OGY1ZGUwOTlkOWNiOGVkZjcz
11
- OTcwMzdhMGE3YTU3MzkyYzhlZjExZDMxZTU1Mzk1NjJhNzk4ODc=
9
+ YjIyNDEyNTQyZWQwYzgzMzhlMzEzYTI5MzQyZDQ2YjE3MGQwYTZmMTljY2Vm
10
+ MTIxZGVhMDJmYjQ2NWE3MTFjYWU0YjQxYWUyYTgyZTAxODEwM2UwNmIyNzM5
11
+ YzI3MzI2ZmY4NjU5YmEyMTA4OTg3ZDBlNDM3MzFjZGY3MTVmZTg=
12
12
  data.tar.gz: !binary |-
13
- ZWRmYmIyMjVhYTdlM2QwYjU3OWJmMjMxOTQ1Mjc4MTE2NWQwZDM4NGZjMjI5
14
- NWFhMGU0ZWM4YmJmNjJjMTBkYWI2ZjgzYjBkYjhhZTU5NmNmNzEzODE5MDM0
15
- ZDM0ZjkxNmZjYjMwODdjNWU2M2MzNmU2N2IwOTE3ZGZjMzZkNWM=
13
+ NjZjOGRkZWZhMjE0MDVhM2NhZjk1NDg3NDE3N2JiYzBmMGVmMGY4NDQ5M2Zj
14
+ N2Y0ZWQ5Y2QyNGRmZWY4YzdhOTJkOWIxNzA5OTYyY2JmOGZmZTJhNWM0ZGU0
15
+ ZGI3N2NlYzYxYWQwZDUyMDY2NTU2ZjRkMmQwMmIxZTNkNTlhYzc=
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+
3
+ cache: bundler
4
+
5
+ rvm:
6
+ - 2.1
7
+ - 2.0
8
+ - 1.9.3
9
+
10
+ script: bundle exec rspec
data/README.md CHANGED
@@ -1,6 +1,21 @@
1
1
  # Mementus
2
2
 
3
- TODO: Write a gem description
3
+ [![Build Status](https://travis-ci.org/maetl/mementus.svg?branch=master)](https://travis-ci.org/maetl/mementus)
4
+
5
+ ```ruby
6
+ # Proof of concept for a toy ORM that combines some aspects of the
7
+ # ActiveRecord API with an in-memory query model based on the Axiom
8
+ # relational algebra API.
9
+ #
10
+ # The weirdest trick is that the data stored in the relational model
11
+ # is a read-only index that never gets re-materialised back into the
12
+ # model objects themselves (though Axiom does seem to be capable of
13
+ # doing this).
14
+ #
15
+ # Instead of returning mapped data from queries, the Ruby object_id
16
+ # is used as a reference to point to the existing instance in the
17
+ # runtime object space.
18
+ ```
4
19
 
5
20
  ## Installation
6
21
 
@@ -18,7 +33,50 @@ Or install it yourself as:
18
33
 
19
34
  ## Usage
20
35
 
21
- TODO: Write usage instructions here
36
+ To define model classes, inherit from the `Mementus::Model` base class and add attributes using the [Virtus API](https://github.com/solnic/virtus):
37
+
38
+ ```ruby
39
+ class Book < Mementus::Model
40
+ attribute :title, String
41
+ attribute :author, String
42
+ end
43
+ ```
44
+
45
+ Create new instances by passing data through the constructor or assigning attributes directly:
46
+
47
+ ```ruby
48
+ book1 = Book.new(
49
+ :title => "Gravity's Rainbow",
50
+ :author => "Thomas Pynchon"
51
+ )
52
+
53
+ book2 = Book.new
54
+ book2.title = "The Golden Notebook"
55
+ book2.author = "Doris Lessing"
56
+ ```
57
+
58
+ To write objects to the in-memory index, call their `create` method:
59
+
60
+ ```ruby
61
+ book3 = Book.new(
62
+ :title => "Crash",
63
+ :author => "J.G. Ballard"
64
+ )
65
+
66
+ book3.create
67
+ ```
68
+
69
+ The query API is currently being worked on. Right now to test it out, you can access the relational collection for each model through the `collection` method, and execute basic matching queries using the `where` method:
70
+
71
+ ```ruby
72
+ Book.collection.count
73
+
74
+ Book.collection.first
75
+
76
+ Book.where(author: "Doris Lessing")
77
+
78
+ Book.where(title: "Crash")
79
+ ```
22
80
 
23
81
  ## Contributing
24
82
 
@@ -0,0 +1,105 @@
1
+ require 'virtus'
2
+ require 'axiom-memory-adapter'
3
+
4
+ module Mementus
5
+
6
+ # Proof of concept for a toy ORM that combines some aspects of the
7
+ # ActiveRecord API with an in-memory query model based on the Axiom
8
+ # relational algebra API.
9
+ #
10
+ # The weirdest trick is that the data stored in the relational model
11
+ # is a read-only index that never gets re-materialised back into the
12
+ # model objects themselves (though Axiom does seem to be capable of
13
+ # doing this).
14
+ #
15
+ # Instead of returning mapped data from queries, the Ruby object_id
16
+ # is used as a reference to point to the existing instance in the
17
+ # runtime object space.
18
+ #
19
+ class Model
20
+
21
+ @@local_storage = nil
22
+ @@model_registry = {}
23
+
24
+ def self.name_to_sym
25
+ name_without_namespace = name.split("::").last
26
+ name_without_namespace.gsub(/([^\^])([A-Z])/,'\1_\2').downcase.to_sym
27
+ end
28
+
29
+ # TODO: this mapping could be based on declared indexes,
30
+ # rather than dumping the entire attribute schema here.
31
+ #
32
+ def schema_tuple
33
+ tuple = [[:__object_id, String]]
34
+ attribute_set.each do |attribute_type|
35
+ tuple << [attribute_type.name.to_sym, attribute_type.primitive]
36
+ end
37
+ tuple
38
+ end
39
+
40
+ # TODO: this mapping could be based on declared indexes,
41
+ # rather than dumping the entire attribute data set here.
42
+ #
43
+ def values_tuple
44
+ tuple = [object_id.to_s]
45
+ attributes.each do |_, attribute_value|
46
+ tuple << attribute_value
47
+ end
48
+ tuple
49
+ end
50
+
51
+ def self.inherited(concrete_model)
52
+ concrete_model.send(:include, Virtus.model)
53
+ end
54
+
55
+ # Create only writes to memory. It's required in order to
56
+ # index the object's attributes in the query model.
57
+ #
58
+ def create
59
+ ensure_registered(self)
60
+ local_storage[self.class.name_to_sym].insert([values_tuple])
61
+ end
62
+
63
+ def ensure_registered(model)
64
+ register(model) unless registered?(model)
65
+ end
66
+
67
+ def registered?(model)
68
+ model_registry.has_key? model.class.name_to_sym
69
+ end
70
+
71
+ def register(model)
72
+ model_id = model.class.name_to_sym
73
+ model_registry[model_id] = true
74
+ local_storage[model_id] = Axiom::Relation.new(model.schema_tuple)
75
+ end
76
+
77
+ def model_registry
78
+ @@model_registry
79
+ end
80
+
81
+ def local_storage
82
+ unless @@local_storage
83
+ @@local_storage = Axiom::Adapter::Memory.new
84
+ end
85
+ @@local_storage
86
+ end
87
+
88
+ def self.collection
89
+ @@local_storage[name_to_sym]
90
+ end
91
+
92
+ # Stub implementation that demonstrates the ObjectSpace
93
+ # reference grabbing.
94
+ #
95
+ # Currently just splats to an array instead of handing back
96
+ # a chainable scope object.
97
+ #
98
+ def self.where(constraints)
99
+ self.collection.restrict(constraints).inject([]) do |list, relation|
100
+ list << ObjectSpace._id2ref(relation[:__object_id].to_i)
101
+ end
102
+ end
103
+
104
+ end
105
+ end
@@ -1,3 +1,3 @@
1
1
  module Mementus
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.1"
3
3
  end
data/lib/mementus.rb CHANGED
@@ -1,5 +1,5 @@
1
- require "mementus/version"
1
+ require 'virtus'
2
+ require 'axiom-memory-adapter'
2
3
 
3
- module Mementus
4
- # Your code goes here...
5
- end
4
+ require_relative 'mementus/version'
5
+ require_relative 'mementus/model'
data/mementus.gemspec CHANGED
@@ -18,6 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_runtime_dependency "virtus"
22
+ spec.add_runtime_dependency "axiom-memory-adapter"
23
+
21
24
  spec.add_development_dependency "bundler", "~> 1.3"
22
25
  spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec"
23
27
  end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mementus::Model do
4
+
5
+ describe "Attributes" do
6
+
7
+ class PrimaryAttributes < Mementus::Model
8
+ attribute :name, String
9
+ attribute :count, Integer
10
+ attribute :switch, Boolean
11
+ end
12
+
13
+ let(:model) {
14
+ PrimaryAttributes.new(
15
+ :name => "a string",
16
+ :count => 500,
17
+ :switch => false
18
+ )
19
+ }
20
+
21
+ it "reads attributes" do
22
+ expect(model.name).to eq "a string"
23
+ expect(model.count).to eq 500
24
+ expect(model.switch).to eq false
25
+ end
26
+
27
+ it "writes attributes" do
28
+ model.name = "stringstringstring"
29
+ model.count = 488
30
+ model.switch = true
31
+
32
+ expect(model.name).to eq "stringstringstring"
33
+ expect(model.count).to eq 488
34
+ expect(model.switch).to eq true
35
+ end
36
+
37
+ end
38
+
39
+
40
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mementus::Model do
4
+
5
+ describe "Collection" do
6
+
7
+ class Item < Mementus::Model
8
+ attribute :name, String
9
+ attribute :order, Integer
10
+ end
11
+
12
+ before(:all) do
13
+ 20.times do |i|
14
+ item = Item.new
15
+ item.name = "Item: #{i.to_s}"
16
+ item.order = i + 1
17
+ item.create
18
+ end
19
+ end
20
+
21
+ it "counts created items" do
22
+ expect(Item.collection.count).to eq 20
23
+ end
24
+
25
+ it "finds item by matching attribute" do
26
+ expect(Item.where(order: 10).count).to eq 1
27
+ expect(Item.where(order: 10).first.name).to eq "Item: 9"
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mementus::Model do
4
+
5
+ describe "Mapping" do
6
+
7
+ class Mapping < Mementus::Model
8
+ attribute :description, String
9
+ attribute :number, Integer
10
+ end
11
+
12
+ let(:mapping) {
13
+ Mapping.new(
14
+ :description => "Hello world.",
15
+ :number => 2014
16
+ )
17
+ }
18
+
19
+ let(:object_id) {
20
+ mapping.object_id.to_s
21
+ }
22
+
23
+ it "maps attribute schema to tuple" do
24
+ schema = [[:__object_id, String], [:description, String], [:number, Integer]]
25
+ expect(mapping.schema_tuple).to eq schema
26
+ end
27
+
28
+ it "maps attribute values to tuple" do
29
+ values = [object_id, "Hello world.", 2014]
30
+ expect(mapping.values_tuple).to eq values
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,6 @@
1
+ require 'rspec'
2
+ require 'mementus'
3
+
4
+ RSpec.configure do |config|
5
+ config.formatter = 'documentation'
6
+ end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mementus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - maetl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-10 00:00:00.000000000 Z
11
+ date: 2014-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: virtus
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: axiom-memory-adapter
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: bundler
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +66,20 @@ dependencies:
38
66
  - - ! '>='
39
67
  - !ruby/object:Gem::Version
40
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
41
83
  description: In-memory data model
42
84
  email:
43
85
  - me@maetl.net
@@ -46,13 +88,19 @@ extensions: []
46
88
  extra_rdoc_files: []
47
89
  files:
48
90
  - .gitignore
91
+ - .travis.yml
49
92
  - Gemfile
50
93
  - LICENSE.txt
51
94
  - README.md
52
95
  - Rakefile
53
96
  - lib/mementus.rb
97
+ - lib/mementus/model.rb
54
98
  - lib/mementus/version.rb
55
99
  - mementus.gemspec
100
+ - spec/attribute_spec.rb
101
+ - spec/collection_spec.rb
102
+ - spec/mapping_spec.rb
103
+ - spec/spec_helper.rb
56
104
  homepage: ''
57
105
  licenses:
58
106
  - MIT
@@ -77,4 +125,8 @@ rubygems_version: 2.1.8
77
125
  signing_key:
78
126
  specification_version: 4
79
127
  summary: In-memory data model
80
- test_files: []
128
+ test_files:
129
+ - spec/attribute_spec.rb
130
+ - spec/collection_spec.rb
131
+ - spec/mapping_spec.rb
132
+ - spec/spec_helper.rb