active_kv 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d2f673f0b0a6150b3b57d6d983dea905450e2bb4
4
+ data.tar.gz: 36710d432228d89aa41bfabd972756ff1d5f4abf
5
+ SHA512:
6
+ metadata.gz: 0895e2ac0c2ad161c42ab2ae7711a14de238dfcd43f66cbcdba43ee9e5dd59429cee9b798f4d33a2e09125bd8d8a4046e33330b4e5331273b1159ab337f3ce5e
7
+ data.tar.gz: 8a524df8b4d89067c02e7215ff55c3430b9ff5d16e02c83cac92e14d4a7858a5226d5c2a065479d2a1839f29d66ed1f7a66a090e6e5971b02fae9fde8a47c675
@@ -0,0 +1,39 @@
1
+ # ActiveRedis
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/active_redis`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'active_redis'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install active_redis
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ 1. Fork it ( https://github.com/[my-github-username]/active_redis/fork )
36
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
37
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
38
+ 4. Push to the branch (`git push origin my-new-feature`)
39
+ 5. Create a new Pull Request
@@ -0,0 +1,40 @@
1
+ require "active_model"
2
+ require "redis"
3
+
4
+ require "active_redis/version"
5
+ require "active_redis/attributes"
6
+ require "active_redis/relation"
7
+ require "active_redis/boolean"
8
+
9
+ module ActiveRedis
10
+
11
+ @redis ||= Redis.new
12
+
13
+ attr_reader :redis
14
+
15
+ class Base
16
+
17
+ include ActiveModel::Validations
18
+ include ActiveModel::Serialization
19
+ include ActiveModel::Serializers::JSON
20
+ include ActiveModel::Serializers::Xml
21
+ include ActiveRedis::Attributes::InstanceMethods
22
+ include ActiveRedis::Relation::InstanceMethods
23
+ extend ActiveRedis::Attributes::ClassMethods
24
+ extend ActiveRedis::Relation::ClassMethods
25
+
26
+ def initialize args={}, settings={reload: false}
27
+ raise TypeError, 'Hash expected.' unless args.is_a? Hash
28
+ keys.merge!({id: Integer, updated_at: Time})
29
+ args = Hash[args.map{|(k,v)| [k.to_sym,v]}]
30
+ @h = args
31
+ create_relation_methods
32
+ create_attribute_methods
33
+ get_attributes reload: settings[:reload]
34
+ end
35
+
36
+ end
37
+
38
+ extend self
39
+
40
+ end
@@ -0,0 +1,297 @@
1
+ module ActiveRedis
2
+ module Attributes
3
+
4
+ module InstanceMethods
5
+
6
+ ##
7
+ # Save the current record to redis store.
8
+ #
9
+ # @return [true,false]
10
+ #
11
+ def save
12
+ valid? ? (set_attributes(attributes) && true) : false
13
+ end
14
+
15
+ ##
16
+ # Reformat object.
17
+ #
18
+ # @return [String]
19
+ #
20
+ def inspect
21
+ a = keys.reject{|k,v| k == :id }.map do |attribute,type|
22
+ %Q{#{attribute}=#{send(attribute).inspect}}
23
+ end
24
+ %Q{#<#{self.class.name} id=#{self.id} #{a.join(' ')}>}
25
+ end
26
+
27
+ ##
28
+ # Remove the instance from redis store.
29
+ # Remove all relations to another objects.
30
+ #
31
+ # @return [void]
32
+ #
33
+ def remove
34
+ blto = self.class.instance_variable_get(:@belongs_to)
35
+ (blto || []).each do |rel|
36
+ handle_relation relation_key: "#{rel.to_s.singularize}_id",
37
+ action: :del
38
+ end
39
+ connection.del *attribute_paths
40
+ end
41
+
42
+ alias :delete :remove
43
+ alias :destroy :remove
44
+
45
+ ##
46
+ # Get the current attributes.
47
+ #
48
+ # @return [Hash]
49
+ #
50
+ def attributes
51
+ get_attributes
52
+ end
53
+
54
+ ##
55
+ # @see ActiveRedis#keys
56
+ #
57
+ def keys
58
+ self.class.keys
59
+ end
60
+
61
+ ##
62
+ # @see ActiveRedis#connection
63
+ #
64
+ def connection
65
+ self.class.connection
66
+ end
67
+
68
+ ##
69
+ # Implement ActiveModel validation support.
70
+ #
71
+ def read_attribute_for_validation key
72
+ attributes[key]
73
+ end
74
+
75
+
76
+ module_function
77
+
78
+ ##
79
+ # Create getter/setter/bool methods by attributes.
80
+ # This method should be called in a constructor.
81
+ #
82
+ # @return [void]
83
+ #
84
+ def create_attribute_methods
85
+ self.class.instance_eval do
86
+ keys.each do |attribute,type|
87
+ define_method(attribute) do
88
+ attributes[attribute]
89
+ end
90
+
91
+ define_method("#{attribute}=") do |arg|
92
+ attributes[attribute] = format_attribute(attribute,arg.to_s)
93
+ end
94
+
95
+ define_method("#{attribute}?") do
96
+ !attributes[attribute].to_s.empty?
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ ##
103
+ # Get attributes from redis store.
104
+ #
105
+ # @param reload [true,false] Force reload from redis.
106
+ #
107
+ # @return [Hash]
108
+ #
109
+ def get_attributes reload: false
110
+ if reload or not @h
111
+ h = (@h and @h[:id]) ? connection.hgetall(basename) : {}
112
+ attribute_hash = {}
113
+ unless h.empty?
114
+ keys.each do |k,v|
115
+ attribute_hash[k.to_sym] = format_attribute(k.to_sym, h[k.to_s])
116
+ end
117
+ end
118
+ @h = attribute_hash
119
+ end
120
+ @h
121
+ end
122
+
123
+ ##
124
+ # Casting objects into original values.
125
+ # The type whould be defined at class method "keys"
126
+ #
127
+ # @see ActiveRedis.keys
128
+ #
129
+ # @param key [String,Symbol]
130
+ # @param value [Object]
131
+ #
132
+ # @return [Object] Casted value.
133
+ #
134
+ def format_attribute key, value
135
+ case keys[key.to_sym].to_s
136
+ when /Integer/
137
+ value.to_i
138
+ when /Array/
139
+ connection.lrange("#{basename}:#{key}",0,-1)
140
+ when /Hash/
141
+ connection.hgetall("#{basename}:#{key}")
142
+ when /Time/
143
+ Time.parse(value) rescue nil
144
+ else value
145
+ end
146
+ end
147
+
148
+ ##
149
+ # Save given hash to redis server.
150
+ # Refresh updated_at timestamp.
151
+ # Unless id isn't set, get the next primary key.
152
+ #
153
+ # @param arg [Hash]
154
+ #
155
+ # @return [void]
156
+ #
157
+ def set_attributes arg
158
+ remove
159
+ arg[:updated_at] = Time.now.to_s
160
+ arg[:id] ||= connection.incr("#{self.class.name}_id")
161
+ arg.each do |k,v|
162
+ case v
163
+ when Array
164
+ v.each do |v|
165
+ connection.lpush "#{basename}:#{k}", v
166
+ end
167
+ when Hash
168
+ connection.mapped_hmset "#{basename}:#{k}", v
169
+ else
170
+ handle_relation relation_key: k, value: v, action: :add
171
+ connection.hset basename, k, v
172
+ end
173
+ end
174
+ get_attributes(reload: true)
175
+ end
176
+
177
+ ##
178
+ # Redis basename.
179
+ #
180
+ # @return [String]
181
+ #
182
+ def basename
183
+ "#{self.class.name.downcase.pluralize}:#{self.id}"
184
+ end
185
+
186
+ ##
187
+ # Get attribute paths by keys.
188
+ # Complex types needs to be stored at another path.
189
+ #
190
+ # @return [Array]
191
+ #
192
+ def attribute_paths
193
+ keys.map do |k,v|
194
+ case v.to_s
195
+ when /Hash/, /Array/ then "#{basename}:#{k}"
196
+ else basename
197
+ end
198
+ end.uniq
199
+ end
200
+
201
+ end
202
+
203
+ module ClassMethods
204
+
205
+ ##
206
+ # Fromatted class name.
207
+ #
208
+ # @return [String]
209
+ #
210
+ def cname
211
+ self.name.downcase.pluralize
212
+ end
213
+
214
+ ##
215
+ # Create a new instance and save it to redis store.
216
+ #
217
+ # @return [ActiveRedis::Base]
218
+ #
219
+ def create attributes
220
+ a = self.new(attributes)
221
+ a.save
222
+ a
223
+ end
224
+
225
+ ##
226
+ # Get all objects.
227
+ #
228
+ # @return [Array<ActiveRedis>]
229
+ #
230
+ def all
231
+ connection.keys("#{cname}:*").map do |key|
232
+ self.new({id: key[/#{cname}:(\d+)/,1].to_i},reload: true)
233
+ end
234
+ end
235
+
236
+ ##
237
+ # Redis base connection.
238
+ #
239
+ # @return [Redis]
240
+ #
241
+ def connection
242
+ ActiveRedis.redis
243
+ end
244
+
245
+ ##
246
+ # @param id [Integer]
247
+ #
248
+ # @return [ActiveRedis::Base]
249
+ #
250
+ def find id
251
+ a = connection.hgetall("#{cname}:#{id}")
252
+ self.new({id: id},reload: true) unless a.empty?
253
+ end
254
+
255
+ ##
256
+ # @see where
257
+ #
258
+ # @return [ActiveRedis::Base]
259
+ #
260
+ def find_by basename: nil
261
+ if basename
262
+ where(basename: basename).first
263
+ end
264
+ end
265
+
266
+ ##
267
+ # @params basename [String]
268
+ #
269
+ # @return [Array]
270
+ #
271
+ def where basename: nil
272
+ if basename
273
+ a = connection.smembers(basename)
274
+ a.map{ |entry| self.find(entry) }
275
+ else
276
+ []
277
+ end
278
+ end
279
+
280
+ ##
281
+ # Set the keys/attributs for an ActiveRedis::Base child.
282
+ #
283
+ # Example:
284
+ #
285
+ # keys title: String,
286
+ # number: Integer
287
+ #
288
+ def keys hash=nil
289
+ hash ? (@keys ||= hash) : @keys
290
+ end
291
+
292
+
293
+ extend self
294
+
295
+ end
296
+ end
297
+ end
@@ -0,0 +1,4 @@
1
+ module Boolean; end
2
+ TrueClass.include Boolean
3
+ FalseClass.include Boolean
4
+
@@ -0,0 +1,92 @@
1
+ module ActiveRedis
2
+ module Relation
3
+
4
+ module InstanceMethods
5
+
6
+ module_function
7
+
8
+ ##
9
+ # Generate methods for relations.
10
+ #
11
+ # @return [void]
12
+ #
13
+ def create_relation_methods
14
+ self.class.instance_eval do
15
+ (@has_many || []).each do |x|
16
+ define_method(x.to_s.pluralize) do
17
+ klazz = Object.const_get(x.to_s.capitalize.singularize)
18
+ klazz.where(
19
+ basename: "#{basename}:#{x.to_s.downcase.pluralize}")
20
+ end
21
+ end
22
+ (@belongs_to || []).each do |x|
23
+ keys.merge!({"#{x.to_s.downcase.singularize}_id".to_sym => Integer})
24
+ define_method(x.to_s.singularize) do
25
+ klazz = Object.const_get(x.to_s.capitalize.singularize)
26
+ klazz.find send("#{x.to_s.downcase.singularize}_id")
27
+ end
28
+ end
29
+ (@has_one || []).each do |x|
30
+ define_method(x.to_s) do
31
+ klazz = Object.const_get(x.to_s.capitalize.singularize)
32
+ klazz.find_by(basename: "#{basename}:"+
33
+ "#{x.to_s.downcase.singularize}")
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ ##
40
+ # Create a set for each relation. This is needed to
41
+ # fetch directly the objects by id from redis store.
42
+ # Nobody wan't to do it only at application level.
43
+ #
44
+ # @param relation_key [String] Like Author - Post.author_id
45
+ # @param value [String]
46
+ # @param action [:add,:del]
47
+ #
48
+ # @return [void]
49
+ #
50
+ def handle_relation relation_key: nil, value: nil, action: :add
51
+ blto = self.class.instance_variable_get(:@belongs_to)
52
+ relation = relation_key.to_s[/^([a-z_]+)_id$/,1].to_s
53
+ if blto.is_a?(Array) and
54
+ blto.include?(relation.to_sym)
55
+ if action == :add
56
+ connection.sadd(
57
+ "#{relation.pluralize}:#{value}:#{self.class.cname}",
58
+ id)
59
+ elsif action == :del
60
+ connection.srem(
61
+ "#{relation.pluralize}:"+
62
+ "#{value || send(relation_key)}:"+
63
+ "#{self.class.cname}",
64
+ id)
65
+ end
66
+ end
67
+ end
68
+
69
+
70
+ end
71
+
72
+ module ClassMethods
73
+
74
+ ##
75
+ # Create class level methods.
76
+ #
77
+ %w{has_many has_one belongs_to}.each do |m|
78
+ define_method(m) do |symbol|
79
+ unless instance_variable_get("@#{m}").is_a?(Array)
80
+ instance_variable_set("@#{m}",[])
81
+ end
82
+ instance_variable_get("@#{m}") << symbol
83
+ end
84
+ end
85
+
86
+
87
+ extend self
88
+
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveRedis
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_kv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tim Foerster
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-06 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: '1.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 4.7.3
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 4.7.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: redis
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: active_model
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - timhormersdorf@googlemail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - README.md
91
+ - lib/active_redis.rb
92
+ - lib/active_redis/attributes.rb
93
+ - lib/active_redis/boolean.rb
94
+ - lib/active_redis/relation.rb
95
+ - lib/active_redis/version.rb
96
+ homepage:
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.2.1
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Get redis relational
120
+ test_files: []