active_kv 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []