hash_db 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hash_db.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,3 @@
1
+ guard "rspec" do
2
+ watch(/^(lib|spec)/) { "spec" }
3
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Utkarsh Kukreti
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/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # HashDB
2
+
3
+ HashDB is a minimal, in-memory, ActiveRecord like database library,
4
+ backed by a Ruby Hash.
5
+
6
+ ## Installation
7
+
8
+ Gem releases are not done frequently as of now. I recommend installing through
9
+ the git repository.
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'hash_db', git: "https://github.com/utkarshkukreti/hash_db.git"
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ git clone https://github.com/utkarshkukreti/hash_db.git
22
+ $ cd hash_db
23
+ $ bundle install
24
+ $ rake install
25
+
26
+ ## Usage
27
+
28
+ First, require HashDB.
29
+
30
+ require 'hash_db'
31
+
32
+ To make your class a HashDB model, just include `HashDB::Model` module in
33
+ your class.
34
+
35
+ class Post
36
+ include HashDB::Model
37
+ end
38
+
39
+ Defining attributes is as easy as calling `keys`, with the key names you want.
40
+
41
+ keys :title, :content
42
+
43
+ HashDB will automatically create getters and setters for these keys, so you can
44
+ now assign `title` and `content` to objects of the Post class.
45
+
46
+ post = Post.new
47
+ post.title = "Introducing HashDB"
48
+ post.content = "..."
49
+
50
+ Just as with ActiveRecord, you can also pass a Hash with key/value pairs to .new
51
+
52
+ # Same as above
53
+ post = Post.new title: "Introducing HashDB", content: "..."
54
+
55
+ To be able to query the model, objects must be saved, using `.save`.
56
+
57
+ post.save
58
+
59
+ This saves the object into Class.all Hash, with auto generated `id` key as the
60
+ key, and object as the value.
61
+
62
+ Post.all == { 1 => post } #= true
63
+
64
+ Like ActiveRecord, `.new` and `.save` can be combined into just a `.create` call
65
+ on the class.
66
+
67
+ Post.create title: "Second Post"
68
+ Post.all.count == 2 #= true
69
+
70
+ ## Querying
71
+
72
+ HashDB currently has just 2 query methods, `.where`, which can filter objects
73
+ using key(s) = value(s), and also using custom method call, like `:<` and `:>`.
74
+
75
+ Let's [HN](http://news.ycombinator.com) as an example. :)
76
+
77
+ class Item
78
+ include HashDB::Model
79
+ keys :url, :title, :points, :comment_count
80
+ end
81
+
82
+ Item.create url: "http://blog.heroku.com/archives/2011/7/12/matz_joins_heroku/",
83
+ title: "Matz (creator of Ruby) joins Heroku",
84
+ points: 569,
85
+ comment_count: 79
86
+ Item.create url: "http://www.rubymotion.com/",
87
+ title: "RubyMotion - Ruby for iOS",
88
+ points: 466,
89
+ comment_count: 248
90
+ Item.create url: "http://repl.it/",
91
+ title: "Try Python, Ruby, Lua, Scheme, QBasic, Forth...",
92
+ points: 379,
93
+ comment_count: 80
94
+
95
+ You can query by key = value
96
+
97
+ Item.where(points: 569).count #= 1
98
+
99
+ by key = value pairs
100
+
101
+ Item.where(points: 569, comment_count: 79).count #= 1
102
+
103
+ by custom method call
104
+
105
+ Item.where(:points, :>, 400).count #= 2
106
+
107
+ and by custom method call pairs
108
+
109
+ Item.where([:points, :>, 400], [:title, :=~, /ios/i]).count #= 1
110
+
111
+ ## Associations
112
+
113
+ TODO: Add Documentation
114
+
115
+ ## License
116
+
117
+ MIT License. (c) 2013 Utkarsh Kukreti.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/hash_db.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hash_db/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "hash_db"
8
+ gem.version = HashDB::VERSION
9
+ gem.authors = ["Utkarsh Kukreti"]
10
+ gem.email = ["utkarshkukreti@gmail.com"]
11
+ gem.description = "A minimal, in-memory, ActiveRecord like database, " +
12
+ "backed by a Ruby Hash."
13
+ gem.summary = gem.description
14
+ gem.homepage = ""
15
+ gem.license = "MIT"
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ["lib"]
21
+
22
+ %w{rake rspec guard-rspec}.each do |name|
23
+ gem.add_development_dependency name
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ module HashDB
2
+ module Model
3
+ module ClassMethods
4
+ def has_many(name, args = {})
5
+ klass = args[:class]
6
+ foreign_key = args[:foreign_key]
7
+
8
+ klass.keys foreign_key
9
+
10
+ define_method "#{name}" do
11
+ base_id = id
12
+ @attributes[name] ||= begin
13
+ [].tap do |array|
14
+ array.define_singleton_method :<< do |object|
15
+ object.send("#{foreign_key}=", base_id)
16
+ super object
17
+ end
18
+
19
+ array.define_singleton_method :create do |*args|
20
+ klass.create(*args).tap do |object|
21
+ array << object
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def belongs_to(name, args = {})
30
+ klass = args[:class]
31
+ key = args[:key]
32
+
33
+ define_method "#{name}" do
34
+ @attributes[name]
35
+ end
36
+
37
+ define_method "#{name}=" do |object|
38
+ @attributes[name] = object
39
+ end
40
+
41
+ define_method "#{key}" do
42
+ @attributes[name] && @attributes[name].id
43
+ end
44
+
45
+ define_method "#{key}=" do |id|
46
+ @attributes[name] = klass.find_by(id: id)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,59 @@
1
+ module HashDB
2
+ module Model
3
+ attr_accessor :attributes
4
+
5
+ def self.included(klass)
6
+ klass.extend ClassMethods
7
+ klass.all = {}
8
+ klass.keys :id
9
+ end
10
+
11
+ def initialize(args = {})
12
+ @attributes = {}
13
+ args.each do |key, value|
14
+ if respond_to?("#{key}=")
15
+ send("#{key}=", value)
16
+ else
17
+ raise InvalidKeyError.new "Invalid key #{key}."
18
+ end
19
+ end
20
+ end
21
+
22
+ def save
23
+ if id.nil?
24
+ self.id = if self.class.all.any?
25
+ self.class.all.keys.last + 1
26
+ else
27
+ 1
28
+ end
29
+ end
30
+ self.class.all[id] = self
31
+ end
32
+
33
+ def destroy
34
+ self.class.all.delete id
35
+ end
36
+
37
+ module ClassMethods
38
+ attr_accessor :all
39
+
40
+ def keys(*keys)
41
+ keys.each do |key|
42
+ define_method "#{key}" do
43
+ @attributes[key]
44
+ end
45
+
46
+ define_method "#{key}=" do |value|
47
+ @attributes[key] = value
48
+ end
49
+ end
50
+ end
51
+
52
+ def create(args = {})
53
+ new(args).tap do |object|
54
+ object.save
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,29 @@
1
+ module HashDB
2
+ module Model
3
+ module ClassMethods
4
+ def where(*args)
5
+ # Doesn't really take all cases into account.
6
+ # Good enough for now. :)
7
+ if args.size == 1 && Hash === args.first
8
+ args = args.first.map do |key, value|
9
+ [key, :==, value]
10
+ end
11
+ elsif !(Array === args.first)
12
+ args = [args]
13
+ end
14
+
15
+ @all.values.select do |object|
16
+ args.all? do |key, method, value|
17
+ # TODO: Should access through the getter?
18
+ object.attributes[key].send(method, value)
19
+ end
20
+ end
21
+ end
22
+
23
+ def find_by(*args)
24
+ # Not efficient, and I know it.
25
+ where(*args).first
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module HashDB
2
+ VERSION = "0.0.1"
3
+ end
data/lib/hash_db.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "hash_db/model/core"
2
+ require "hash_db/model/query"
3
+ require "hash_db/model/association"
4
+
5
+ require "hash_db/version"
6
+
7
+ module HashDB
8
+ InvalidKeyError = Class.new ArgumentError
9
+ end
@@ -0,0 +1,51 @@
1
+ require "spec_helper"
2
+
3
+ describe HashDB::Model do
4
+ context ".has_many" do
5
+ it "should allow assigning and retrieving objects" do
6
+ post = Class.new do
7
+ include HashDB::Model
8
+ end
9
+
10
+ user = Class.new do
11
+ include HashDB::Model
12
+ has_many :posts, foreign_key: :user_id, class: post
13
+ end
14
+
15
+ u1 = user.create
16
+ u2 = user.create
17
+ p1 = post.create
18
+ p2 = post.create
19
+
20
+ u1.posts << p1
21
+ p1.user_id.should eq u1.id
22
+ u2.posts << p1
23
+ p1.user_id.should eq u2.id
24
+ end
25
+ end
26
+
27
+ context ".belongs_to" do
28
+ it "should allow assigning and retrieving objects" do
29
+ user = Class.new do
30
+ include HashDB::Model
31
+ end
32
+
33
+ post = Class.new do
34
+ include HashDB::Model
35
+ belongs_to :user, key: :user_id, class: user
36
+ end
37
+
38
+ u1 = user.create
39
+ u2 = user.create
40
+ p1 = post.create
41
+ p2 = post.create
42
+
43
+ p1.user = u1
44
+ p1.user_id.should eq u1.id
45
+ p1.user = u2
46
+ p1.user_id.should eq u2.id
47
+ p1.user_id = u1.id
48
+ p1.user.should eq u1
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,80 @@
1
+ require "spec_helper"
2
+
3
+ describe HashDB::Model do
4
+ let(:model) { Class.new { include HashDB::Model } }
5
+
6
+ context "include" do
7
+ it "should be includable" do
8
+ model.ancestors.should include HashDB::Model
9
+ end
10
+ end
11
+
12
+ context ".keys" do
13
+ it "should allow assigning and retrieving attributes" do
14
+ model.keys :integer, :string, :float, :object
15
+
16
+ m1 = model.new
17
+
18
+ m1.integer = 2
19
+ m1.string = "Hello"
20
+ m1.float = 4.2
21
+ m1.object = m1
22
+
23
+ m1.integer.should eq 2
24
+ m1.string.should eq "Hello"
25
+ m1.float.should eq 4.2
26
+ m1.object.should eq m1
27
+ end
28
+ end
29
+
30
+ context ".new" do
31
+ it "should assign passed args into keys" do
32
+ model.keys :integer, :string
33
+ m1 = model.new integer: 1, string: "test"
34
+ m1.integer.should eq 1
35
+ m1.string.should eq "test"
36
+ end
37
+
38
+ it "should raise ArgumentError if an invalid key is passed" do
39
+ expect { model.new invalid: 1 }.to raise_exception HashDB::InvalidKeyError
40
+ end
41
+ end
42
+
43
+ context ".create" do
44
+ it "should instantiate and save object" do
45
+ model.keys :integer
46
+ m1 = model.create
47
+ m2 = model.create integer: 10
48
+ model.all.should eq({1 => m1, 2 => m2})
49
+ m2.integer.should eq 10
50
+ end
51
+ end
52
+
53
+ context "#save" do
54
+ it "should assign id to models after they are saved" do
55
+ m1 = model.new.save
56
+ m1.id.should eq 1
57
+ m2 = model.new
58
+ m3 = model.new.save
59
+ m3.id.should eq 2
60
+ end
61
+
62
+ it "should save model into Class.all" do
63
+ m1 = model.new
64
+ m2 = model.new.save
65
+ m3 = model.new.save
66
+ model.all.should eq({ 1 => m2, 2 => m3 })
67
+ end
68
+ end
69
+
70
+ context "#destroy" do
71
+ it "should remove object from model.all" do
72
+ m1 = model.create
73
+ m2 = model.create
74
+ m1.destroy
75
+ model.all.should eq({ 2 => m2 })
76
+ m2.destroy
77
+ model.all.should be_empty
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+
3
+ describe HashDB::Model do
4
+ let :model do
5
+ Class.new do
6
+ include HashDB::Model
7
+ keys :integer, :string
8
+ end
9
+ end
10
+
11
+ context ".where" do
12
+ context "with hash" do
13
+ it "should filter by key(s) = value(s)" do
14
+ m1 = model.create integer: 4, string: "a"
15
+ m2 = model.create integer: 4, string: "b"
16
+ m3 = model.create string: "c"
17
+ m4 = model.create string: "b"
18
+
19
+ model.where(integer: 4).should eq [m1, m2]
20
+ model.where(string: "b").should eq [m2, m4]
21
+ model.where(string: "d").should eq []
22
+ model.where(integer: 4, string: "b").should eq [m2]
23
+ end
24
+ end
25
+
26
+ context "with array" do
27
+ it "should filter by [key, method, value](s)" do
28
+ m1 = model.create integer: 1
29
+ m2 = model.create integer: 2
30
+ m3 = model.create integer: 3
31
+ m4 = model.create integer: 4
32
+ model.where(:integer, :<, 2).should eq [m1]
33
+ model.where(:integer, :>, 2).should eq [m3, m4]
34
+ model.where([:integer, :>, 0], [:integer, :<, 3]).should eq [m1, m2]
35
+ end
36
+ end
37
+ end
38
+
39
+ context ".find_by" do
40
+ it "should delegate to .where.first" do
41
+ model.should_receive(:where).with(:integer, :<, 10).and_return([])
42
+ model.find_by(:integer, :<, 10).should eq nil
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+
3
+ describe HashDB do
4
+ it "should have a VERSION" do
5
+ HashDB::VERSION.should be_a String
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ require "bundler"
2
+ Bundler.require
3
+
4
+ RSpec.configure do |config|
5
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hash_db
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Utkarsh Kukreti
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: guard-rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A minimal, in-memory, ActiveRecord like database, backed by a Ruby Hash.
63
+ email:
64
+ - utkarshkukreti@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .rspec
71
+ - Gemfile
72
+ - Guardfile
73
+ - LICENSE.txt
74
+ - README.md
75
+ - Rakefile
76
+ - hash_db.gemspec
77
+ - lib/hash_db.rb
78
+ - lib/hash_db/model/association.rb
79
+ - lib/hash_db/model/core.rb
80
+ - lib/hash_db/model/query.rb
81
+ - lib/hash_db/version.rb
82
+ - spec/hash_db/model/association_spec.rb
83
+ - spec/hash_db/model/core_spec.rb
84
+ - spec/hash_db/model/query_spec.rb
85
+ - spec/hash_db_spec.rb
86
+ - spec/spec_helper.rb
87
+ homepage: ''
88
+ licenses:
89
+ - MIT
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ segments:
101
+ - 0
102
+ hash: 4016006914544296771
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ segments:
110
+ - 0
111
+ hash: 4016006914544296771
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 1.8.24
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: A minimal, in-memory, ActiveRecord like database, backed by a Ruby Hash.
118
+ test_files:
119
+ - spec/hash_db/model/association_spec.rb
120
+ - spec/hash_db/model/core_spec.rb
121
+ - spec/hash_db/model/query_spec.rb
122
+ - spec/hash_db_spec.rb
123
+ - spec/spec_helper.rb