redisant 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,55 @@
1
+ module RelationBuilder
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ def find_relation name, &block
7
+ return unless name
8
+ relation = relations[name.to_s]
9
+ return relation if relation
10
+ relations[name.to_s] = block.call if block
11
+ end
12
+
13
+ def relations
14
+ @relations ||= {}
15
+ end
16
+
17
+ def setup_relations
18
+ self.class.relation_definitions.each_pair do |name,klass|
19
+ relations[name] = klass.new(name, self)
20
+ end
21
+ end
22
+
23
+
24
+ module ClassMethods
25
+ def relation_definitions
26
+ @relation_definitions ||= {}
27
+ end
28
+
29
+ def add_relation_definition name, klass
30
+ raise Redisant::InvalidArgument.new("Relation #{name} already exists") if relation_definitions[name.to_s]
31
+ relation_definitions[name.to_s] = klass
32
+ end
33
+
34
+ def has_many name
35
+ add_relation_definition name, HasMany
36
+ send :define_method, name do
37
+ relation = find_relation(name) { HasMany.new(name, self) }
38
+ end
39
+ end
40
+
41
+ def belongs_to name
42
+ add_relation_definition name, BelongsTo
43
+ send :define_method, name do
44
+ relation = find_relation(name) { BelongsTo.new(name, self) }
45
+ relation.owner
46
+ end
47
+ send :define_method, "#{name}=" do |item, reprocitate=true|
48
+ relation = find_relation(name) { BelongsTo.new( name, self ) }
49
+ relation.set_owner item, reprocitate
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,201 @@
1
+ class Relation
2
+ attr_reader :name, :object
3
+
4
+ def initialize name, object
5
+ @name = name.to_s
6
+ @object = object
7
+ @class = Inflector.constantize Inflector.singularize(name)
8
+ end
9
+
10
+ def object_class
11
+ @class
12
+ end
13
+ end
14
+
15
+ class BelongsTo < Relation
16
+
17
+ def initialize name, object
18
+ super name, object
19
+ @reverse_name = Inflector.pluralize @object.class.name.downcase
20
+ end
21
+
22
+ def destroy
23
+ set_owner nil, true
24
+ end
25
+
26
+ # keys
27
+ def redis_key
28
+ raise Redisant::InvalidArgument.new('Cannot make key without id') unless @object && @object.id
29
+ "#{@object.class_name}:#{@object.id}:belongs_to:#{@name}"
30
+ end
31
+
32
+ # query
33
+ def owner_id
34
+ @owner_id ||= $redis.get redis_key
35
+ end
36
+
37
+ def owner
38
+ @owner ||= @class.find(owner_id) if owner_id
39
+ end
40
+
41
+ def set_owner item, reprocitate=true
42
+ if owner
43
+ if owner.respond_to? @reverse_name
44
+ owner.send(@reverse_name).remove @object, false if reprocitate
45
+ end
46
+ end
47
+ if item
48
+ raise Redisant::InvalidArgument.new('Wrong object type') unless item.class_name == @name
49
+ raise Redisant::InvalidArgument.new('Missing id') unless item.id
50
+ $redis.set redis_key, item.id
51
+ @owner = item
52
+ @owner_id = item.id
53
+ if owner.respond_to? @reverse_name
54
+ owner.send(@reverse_name).add @object, false if reprocitate
55
+ end
56
+ else
57
+ $redis.del redis_key
58
+ @owner_id = nil
59
+ @owner = nil
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ class HasMany < Relation
66
+
67
+ def initialize name, object
68
+ super name, object
69
+ @reverse_name = @object.class.name.downcase
70
+ end
71
+
72
+ def destroy
73
+ all.each do |item|
74
+ item.send("#{@reverse_name}=", nil, true)
75
+ end
76
+ $redis.del redis_key
77
+ end
78
+
79
+ # keys
80
+ def redis_key
81
+ raise Redisant::InvalidArgument.new('Cannot make key without id') unless @object && @object.id
82
+ "#{@object.class_name}:#{@object.id}:has_many:#{@name}"
83
+ end
84
+
85
+ # query
86
+ def ids
87
+ Criteria.new(self).ids
88
+ end
89
+
90
+ def count
91
+ Criteria.new(self).count
92
+ end
93
+
94
+ def where attributes
95
+ Criteria.new(self).where(attributes)
96
+ end
97
+
98
+ def first attributes={}
99
+ Criteria.new(self).first attributes
100
+ end
101
+
102
+ def last attributes={}
103
+ Criteria.new(self).last attributes
104
+ end
105
+
106
+ def sort options
107
+ Criteria.new(self).sort options
108
+ end
109
+
110
+ def order options
111
+ Criteria.new(self).order options
112
+ end
113
+
114
+ def random
115
+ Criteria.new(self).random
116
+ end
117
+
118
+ def build options={}
119
+ item = @class.new options
120
+ item.save
121
+ add_item item
122
+ item
123
+ end
124
+
125
+ def add item, reprocitate=true
126
+ if item.is_a? Array
127
+ item.each { |i| add_item i }
128
+ else
129
+ add_item item
130
+ end
131
+ end
132
+
133
+ def << object, reprocitate=true
134
+ add object, reprocitate
135
+ end
136
+
137
+ def remove item, reprocitate=true
138
+ return unless item
139
+ if item.is_a? Array
140
+ item.each {|i| remove_item i }
141
+ else
142
+ remove_item item
143
+ end
144
+ end
145
+
146
+ def remove_all reprocitate=true
147
+ $redis.del redis_key
148
+ dirty
149
+ end
150
+
151
+ def all
152
+ @objects ||= ids.map { |id| @class.find id }
153
+ end
154
+
155
+
156
+ private
157
+
158
+ def dirty
159
+ @ids = nil
160
+ @count = nil
161
+ @objects = nil
162
+ end
163
+
164
+ def add_item item, reprocitate=true
165
+ return unless item
166
+ raise Redisant::InvalidArgument.new("Wrong object type, expected #{@class.name}, got #{item.class}") unless item.is_a? Record
167
+ raise Redisant::InvalidArgument.new("Wrong object type, expected #{@class.name}, got #{item.class}") unless item.class == @class
168
+ $redis.sadd redis_key, item.id
169
+ dirty
170
+ #update reverse relation
171
+ if reprocitate
172
+ if item.respond_to? @reverse_name
173
+ current_owner = item.send(@reverse_name)
174
+ current_has_many = current_owner.send(@name) if current_owner
175
+ if current_has_many && current_has_many != self
176
+ current_has_many.remove( item, false )
177
+ end
178
+ item.send("#{@reverse_name}=", @object, false )
179
+ end
180
+ end
181
+ end
182
+
183
+ def remove_item item, reprocitate=true
184
+ if item.is_a? Integer
185
+ id = item
186
+ else
187
+ klass = Inflector.pluralize(item.class_name)
188
+ raise Redisant::InvalidArgument.new("Wrong object type, expected #{@name}, got #{klass}") unless klass == @name
189
+ id = item.id
190
+ end
191
+ $redis.srem redis_key, id
192
+ dirty
193
+ #update reverse relation
194
+ if reprocitate
195
+ if item.respond_to? "#{@reverse_name}="
196
+ item.send("#{@reverse_name}=", nil, false )
197
+ end
198
+ end
199
+ end
200
+
201
+ end
@@ -0,0 +1,29 @@
1
+ class Search
2
+
3
+ attr_reader :name, :class, :options, :type, :order
4
+ def initialize name, klass, options
5
+ @name = name.to_s
6
+ @class = klass
7
+ @options = options
8
+ end
9
+
10
+ def key value
11
+ "#{@class.name.downcase}:search:#{@name}:#{value}"
12
+ end
13
+
14
+ def add record, value
15
+ $redis.sadd key(value), record.id.to_s
16
+ end
17
+
18
+ def update record, prev_value, cur_value
19
+ if prev_value != cur_value
20
+ remove record, prev_value
21
+ add record, cur_value
22
+ end
23
+ end
24
+
25
+ def remove record, value
26
+ $redis.srem key(value), record.id.to_s
27
+ end
28
+
29
+ end
@@ -0,0 +1,21 @@
1
+ module SearchBuilder
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ def finder name, options={}
8
+ searches[name.to_s] ||= Search.new name, self, options
9
+ end
10
+
11
+ def find_search name
12
+ searches[name.to_s]
13
+ end
14
+
15
+ def searches
16
+ @searches ||= {}
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,6 @@
1
+ class Set
2
+ attr_reader :key
3
+ def initialize keys
4
+ @key = key
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module Redisant
2
+ VERSION = "0.1.1"
3
+ end
data/lib/redisant.rb ADDED
@@ -0,0 +1,17 @@
1
+ require "redisant/version"
2
+
3
+ require 'redis'
4
+
5
+ require_relative 'redisant/connection'
6
+ require_relative 'redisant/errors.rb'
7
+ require_relative 'redisant/inflector'
8
+ require_relative 'redisant/attribute_builder'
9
+ require_relative 'redisant/relation_builder'
10
+ require_relative 'redisant/index_builder'
11
+ require_relative 'redisant/search_builder'
12
+ require_relative 'redisant/relations'
13
+ require_relative 'redisant/index'
14
+ require_relative 'redisant/search'
15
+ require_relative 'redisant/query'
16
+ require_relative 'redisant/criteria'
17
+ require_relative 'redisant/records'
data/redisant.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'redisant/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "redisant"
8
+ spec.version = Redisant::VERSION
9
+ spec.authors = ["Emil Tin"]
10
+ spec.email = ["emil@tin.dk"]
11
+
12
+ spec.summary = %q{ORM-like Redis storage.}
13
+ spec.description = %q{Schema-less ORM-like Redis store.}
14
+ spec.homepage = "http://github.com/emiltin/redisant"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", '~> 2.2.26'#, '>= 2.2.26'
23
+ spec.add_development_dependency "rake", '~> 13.0.6', '>= 13.0.6'
24
+ spec.add_development_dependency "rspec", '~> 3.10.0', '>= 3.10.0'
25
+ spec.add_development_dependency "redis", '~> 4.4.0', '>= 4.4.0'
26
+
27
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redisant
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Emil Tin
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-08-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.2.26
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.2.26
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 13.0.6
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 13.0.6
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: 13.0.6
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 13.0.6
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 3.10.0
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 3.10.0
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 3.10.0
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 3.10.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: redis
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: 4.4.0
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 4.4.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 4.4.0
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 4.4.0
87
+ description: Schema-less ORM-like Redis store.
88
+ email:
89
+ - emil@tin.dk
90
+ executables: []
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - ".gitignore"
95
+ - ".rspec"
96
+ - ".travis.yml"
97
+ - CODE_OF_CONDUCT.md
98
+ - Gemfile
99
+ - LICENSE.txt
100
+ - README.md
101
+ - Rakefile
102
+ - bin/console
103
+ - bin/setup
104
+ - lib/redisant.rb
105
+ - lib/redisant/attribute_builder.rb
106
+ - lib/redisant/connection.rb
107
+ - lib/redisant/criteria.rb
108
+ - lib/redisant/errors.rb
109
+ - lib/redisant/index.rb
110
+ - lib/redisant/index_builder.rb
111
+ - lib/redisant/inflector.rb
112
+ - lib/redisant/orm.rb
113
+ - lib/redisant/query.rb
114
+ - lib/redisant/records.rb
115
+ - lib/redisant/relation_builder.rb
116
+ - lib/redisant/relations.rb
117
+ - lib/redisant/search.rb
118
+ - lib/redisant/search_builder.rb
119
+ - lib/redisant/set.rb
120
+ - lib/redisant/version.rb
121
+ - redisant.gemspec
122
+ homepage: http://github.com/emiltin/redisant
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubygems_version: 3.2.26
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: ORM-like Redis storage.
145
+ test_files: []