redisant 0.1.1

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.
@@ -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: []