redis_counters-dumpers 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0b52ecd91c02cece254c010ea63a1e67ed32b9e2
4
+ data.tar.gz: 24189eaed14d65f10cb264dc6cd01b973c715f68
5
+ SHA512:
6
+ metadata.gz: 46a78d977693f438c9a72b8d018ab3f5e3d36dc51afa0f792b444595c50076881a68351c9172d11a2d1a0fe1f3acfc5b883c190c5496d121b018923c4e497cef
7
+ data.tar.gz: 1e3c11304907178fbb9f6d3b6d4955a377b74f8cd9c19dc9bba04028f596c2053e7cc40115f28d9d3651eae4cef50c2ba1f4e92ab6cb4a88d02f2a8d7ea5702d
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in redis_counters-dumpers.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 bibendi
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,31 @@
1
+ # RedisCounters::Dumpers
2
+
3
+ Dump statistics from Redis to DB
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'redis_counters-dumpers'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install redis_counters-dumpers
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/abak-press/redis_counters-dumpers/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,9 @@
1
+ require 'redis_counters/dumpers/version'
2
+
3
+ module RedisCounters
4
+ module Dumpers
5
+ autoload :Engine, 'redis_counters/dumpers/engine'
6
+ autoload :Destination, 'redis_counters/dumpers/destination'
7
+ autoload :List, 'redis_counters/dumpers/list'
8
+ end
9
+ end
@@ -0,0 +1,128 @@
1
+ # coding: utf-8
2
+ require 'forwardable'
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+ require_relative 'dsl/destination'
5
+
6
+ module RedisCounters
7
+ module Dumpers
8
+ # Класс представляет конечную точку сохранения данных счетчика.
9
+ #
10
+ # Описывает в какую модель (таблицу), какие поля имеющиеся в распоряжении дампера,
11
+ # должны быть сохранены и каким образом.
12
+ #
13
+ # По сути, мерджит указанные поля из temp - таблицы, дампера
14
+ # в указанную таблицу.
15
+ #
16
+ # Может использоваться как напрямую так и с помощью DSL (см. модуль RedisCounters::Dumpers::Dsl::Destination).
17
+ class Destination
18
+ extend Forwardable
19
+ include ::RedisCounters::Dumpers::Dsl::Destination
20
+
21
+ # Ссылка на родительский движек - дампер.
22
+ attr_accessor :engine
23
+
24
+ # Модель, в таблицу, которой будет производится мердж данных, AR::Model.
25
+ attr_accessor :model
26
+
27
+ # Список полей, из доступных дамперу, которые необходимо сохранить, Array.
28
+ attr_accessor :fields
29
+
30
+ # Список полей, по комбинации которых, будет происходить определение существования записи,
31
+ # при мердже данных, Array.
32
+ attr_accessor :key_fields
33
+
34
+ # Список полей, которые будет инкрементированы при обновлении существующей записи, Array.
35
+ attr_accessor :increment_fields
36
+
37
+ # Карта полей - карта псевдонимов полей, Hash.
38
+ # Названия полей в целевой таблице, могут отличаться от названий полей дампера.
39
+ # Для сопоставления полей целевой таблицы и дампера, необходимо заполнить карту соответствия.
40
+ # Карта, заполняется только для тех полей, названия которых отличаются.
41
+ # Во всех свойствах, содержащий указания полей: fields, key_fields, increment_fields, conditions
42
+ # используются имена конечных полей целевой таблицы.
43
+ #
44
+ # Example:
45
+ # fields_map = {:pages => :value, :date => :start_month_date}
46
+ #
47
+ # Означает, что целевое поле :pages, указывает на поле :value, дампера,
48
+ # а целевое поле :date, указывает на поле :start_month_date, дампера.
49
+ attr_accessor :fields_map
50
+
51
+ # Список дополнительных условий, которые применяются при обновлении целевой таблицы, Array of String.
52
+ # Каждое условие представляет собой строку - часть SQL выражения, которое может включать именованные
53
+ # параметры из числа доступных в хеше оббщих параметров дампера: engine.common_params.
54
+ # Условия соеденяются через AND.
55
+ attr_accessor :conditions
56
+
57
+ def initialize(engine)
58
+ @engine = engine
59
+ @fields_map = HashWithIndifferentAccess.new
60
+ @conditions = []
61
+ end
62
+
63
+ def merge
64
+ target_fields = fields.join(', ')
65
+
66
+ sql = <<-SQL
67
+ WITH
68
+ source AS
69
+ (
70
+ SELECT #{selected_fields_expression}
71
+ FROM #{source_table}
72
+ ),
73
+ updated AS
74
+ (
75
+ UPDATE #{target_table} target
76
+ SET
77
+ #{updating_expression}
78
+ FROM source
79
+ WHERE #{matching_expression}
80
+ #{extra_conditions}
81
+ RETURNING target.*
82
+ )
83
+ INSERT INTO #{target_table} (#{target_fields})
84
+ SELECT #{target_fields}
85
+ FROM source
86
+ WHERE NOT EXISTS (
87
+ SELECT 1
88
+ FROM updated target
89
+ WHERE #{matching_expression}
90
+ #{extra_conditions}
91
+ )
92
+ SQL
93
+
94
+ sql = model.send(:sanitize_sql, [sql, engine.common_params])
95
+ connection.execute sql
96
+ end
97
+
98
+ def_delegator :model, :connection
99
+ def_delegator :model, :quoted_table_name, :target_table
100
+ def_delegator :engine, :temp_table_name, :source_table
101
+
102
+ protected
103
+
104
+ def selected_fields_expression
105
+ full_fields_map.map { |target_field, source_field| "#{source_field} as #{target_field}" }.join(', ')
106
+ end
107
+
108
+ def full_fields_map
109
+ fields_map.reverse_merge(Hash[fields.zip(fields)])
110
+ end
111
+
112
+ def updating_expression
113
+ increment_fields.map { |field| "#{field} = COALESCE(target.#{field}, 0) + source.#{field}" }.join(', ')
114
+ end
115
+
116
+ def matching_expression
117
+ source_key_fields = key_fields.map { |field| "source.#{field}" }.join(', ')
118
+ target_key_fields = key_fields.map { |field| "target.#{field}" }.join(', ')
119
+ "(#{source_key_fields}) = (#{target_key_fields})"
120
+ end
121
+
122
+ def extra_conditions
123
+ result = conditions.map { |condition| "(#{condition})" }.join(' AND ')
124
+ result.present? ? "AND #{result}" : result
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,46 @@
1
+ # coding: utf-8
2
+ module RedisCounters
3
+ module Dumpers
4
+ module Dsl
5
+ # Базовый класс для создания DSL к другим классам.
6
+ # Класс обертка, который имеет все свойства, включая callbacks,
7
+ # который просто настраивает целевой класс через его стандартные свойства.
8
+ # Профит в простоте реализации DSL, в его изоляции от основного класса,
9
+ # в разделении логики основного класса и DSL к нему.
10
+ class Base
11
+ attr_accessor :target
12
+
13
+ class << self
14
+ def setter(*method_names)
15
+ method_names.each do |name|
16
+ send :define_method, name do |data|
17
+ target.send "#{name}=".to_sym, data
18
+ end
19
+ end
20
+ end
21
+
22
+ def varags_setter(*method_names)
23
+ method_names.each do |name|
24
+ send :define_method, name do |*data|
25
+ target.send "#{name}=".to_sym, data.flatten
26
+ end
27
+ end
28
+ end
29
+
30
+ def callback_setter(*method_names)
31
+ method_names.each do |name|
32
+ send :define_method, name do |method = nil, &block|
33
+ target.send "#{name}=".to_sym, method, &block
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def initialize(target, &block)
40
+ @target = target
41
+ instance_eval(&block)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+ require 'active_support/concern'
3
+ require_relative 'base'
4
+
5
+ module RedisCounters
6
+ module Dumpers
7
+ module Dsl
8
+ # Модуль реализующий DSL для класса Destination
9
+ module Destination
10
+ extend ActiveSupport::Concern
11
+
12
+ class Configuration < ::RedisCounters::Dumpers::Dsl::Base
13
+ alias_method :destination, :target
14
+
15
+ setter :model
16
+
17
+ varags_setter :fields
18
+ varags_setter :key_fields
19
+ varags_setter :increment_fields
20
+
21
+ alias_method :take, :fields
22
+
23
+ def map(field, target_field)
24
+ destination.fields_map.merge!(field.to_sym => target_field[:to])
25
+ end
26
+
27
+ def condition(value)
28
+ destination.conditions << value
29
+ end
30
+ end
31
+
32
+ module ClassMethods
33
+ def build(engine, &block)
34
+ destination = new(engine)
35
+ Configuration.new(destination, &block)
36
+ destination
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ require 'active_support/concern'
3
+ require_relative 'base'
4
+
5
+ module RedisCounters
6
+ module Dumpers
7
+ module Dsl
8
+ # Модуль реализующий DSL для класса Engine::Base
9
+ module Engine
10
+ extend ActiveSupport::Concern
11
+
12
+ class Configuration < ::RedisCounters::Dumpers::Dsl::Base
13
+ alias_method :engine, :target
14
+
15
+ setter :name
16
+ setter :fields
17
+ setter :temp_table_name
18
+
19
+ callback_setter :on_before_merge
20
+ callback_setter :on_prepare_row
21
+ callback_setter :on_after_merge
22
+ callback_setter :on_after_delete
23
+
24
+ def destination(&block)
25
+ engine.destinations << ::RedisCounters::Dumpers::Destination.build(engine, &block)
26
+ end
27
+ end
28
+
29
+ module ClassMethods
30
+ def build(&block)
31
+ engine = new
32
+ Configuration.new(engine, &block)
33
+ engine
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ require 'active_support/concern'
3
+ require 'redis_counters/dumpers/engine'
4
+
5
+ module RedisCounters
6
+ module Dumpers
7
+ module Dsl
8
+ module List
9
+ extend ActiveSupport::Concern
10
+
11
+ module ClassMethods
12
+ def build(&block)
13
+ instance = new
14
+ instance.instance_eval(&block)
15
+ instance
16
+ end
17
+ end
18
+
19
+ def dumper(id, &block)
20
+ engine = ::RedisCounters::Dumpers::Engine.build(&block)
21
+ engine.name = id
22
+ @dumpers[id] = engine
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,284 @@
1
+ # coding: utf-8
2
+ require 'forwardable'
3
+ require 'callbacks_rb'
4
+ require 'active_support/core_ext/hash/indifferent_access'
5
+ require 'redis'
6
+ require 'redis/namespace'
7
+ require_relative 'dsl/engine'
8
+
9
+ module RedisCounters
10
+ module Dumpers
11
+ # Класс дампер (движек дампера) - класс осуществляющий перенос данных счетчика в БД.
12
+ #
13
+ # Может использоваться как напрямую так и с помощью DSL (см. модуль RedisCounters::Dumpers::Dsl::Engine).
14
+ #
15
+ # Общий алгоритм работы:
16
+ # - копируем данные счетчика в временную таблицу
17
+ # - мерджим данные во все целевые таблицы
18
+ # - удаляем перенесенные данные из счетчика
19
+ #
20
+ # Все destinations должны быть в рамках одной БД.
21
+ # Все действия происходят в рамках соединения БД, первой destination.
22
+ #
23
+ # Example:
24
+ # dumper = Dumper.build do
25
+ # name :hits_by_day
26
+ #
27
+ # fields => {
28
+ # :company_id => :integer,
29
+ # :value => :integer,
30
+ # :date => :date,
31
+ # :start_month_date => :date,
32
+ # }
33
+ #
34
+ # destination do
35
+ # model CompanyStatisticTotalByDay
36
+ # take :company_id, :pages, :date
37
+ # key_fields :company_id, :date
38
+ # increment_fields :pages
39
+ # map :pages, :to => :value
40
+ # condition 'target.date = :date'
41
+ # end
42
+ #
43
+ # destination do
44
+ # model CompanyStatisticTotalByMonth
45
+ # take :company_id, :pages, :date
46
+ # key_fields :company_id, :date
47
+ # increment_fields :pages
48
+ # map :pages, :to => :value
49
+ # map :date, :to => :start_month_date
50
+ # condition 'target.date = :start_month_date'
51
+ # end
52
+ #
53
+ # on_before_merge do |dumper, connection|
54
+ # dumper.common_params = {
55
+ # :date => dumper.date.strftime('%Y-%m-%d'),
56
+ # :start_month_date => dumper.date.beginning_of_month.strftime('%Y-%m-%d'),
57
+ # }
58
+ # end
59
+ # end
60
+ #
61
+ # dumper.process!(counter, Date.yesterday)
62
+ #
63
+ # В результате все данные счетчика за вчера, будут
64
+ # смерджены в целевые таблицы, по ключевым полям: company_id и date,
65
+ # причем все поля кроме pages, будут просто записаны в таблицы,
66
+ # а поле pages будет инкрементировано с текущим значением, при обновлении.
67
+ # Данные будут удалены из счетчика.
68
+ # Все действия производятся транзакционно, как в БД, так и в Redis.
69
+ class Engine
70
+ include CallbacksRb
71
+ include ::RedisCounters::Dumpers::Dsl::Engine
72
+ extend Forwardable
73
+
74
+ DATE_FORMAT = '%Y-%m-%d'.freeze
75
+
76
+ # properties/accessors
77
+
78
+ # Название дампера
79
+ attr_reader :name
80
+
81
+ # Список доступных для сохранение в целевые таблицы полей и их типов данных, в виде Hash.
82
+ # Доступны следующие типы данных: string, integer, date, timestamp, boolean.
83
+ # Преобразование типов производится непосредственно перед мерджем в целевые таблицы.
84
+ #
85
+ # Example:
86
+ # fields = {:company_id => :integer, :date => :timestamp}
87
+ attr_reader :fields
88
+
89
+ # Массив, целевых моделей для сохранение данных, Array.
90
+ # Каждый элемент массива это экземпляр класса Engine::Destination.
91
+ attr_accessor :destinations
92
+
93
+ # Название temp таблицы, используемой для переноса данных.
94
+ # По умолчанию: "tmp_#{dumper_name}"
95
+ attr_accessor :temp_table_name
96
+
97
+ # Хеш общий параметров.
98
+ # Данные хеш мерджится в каждую, поступающую от счетчика, строку данных.
99
+ attr_accessor :common_params
100
+
101
+ attr_reader :counter
102
+ attr_reader :date
103
+
104
+ # callbacks
105
+
106
+ # Вызывается, перед процессом мерджа данных, в рамках БД - транзакции.
107
+ # Параметры: dumper, db_connection.
108
+ callback :on_before_merge
109
+
110
+ # Вызывается для каждой строки, полученной от счетчика.
111
+ # Позволяет вычисляить дополнительные данные, которые необходимы для сохранения,
112
+ # или произвести предварительную обработку данных от счетчика перед сохранением.
113
+ # В in/out параметре row передается строка данных от счетчика, в виде хеша.
114
+ # Так же, row содержит все общие данные, заданные для дампера в свойстве common_params.
115
+ # Параметры: dumper, row.
116
+ callback :on_prepare_row
117
+
118
+ # Вызывается, по окончанию процесса мерджа данных, в рамках БД - транзакции.
119
+ # Параметры: dumper, db_connection.
120
+ callback :on_after_merge
121
+
122
+ # Вызывается, по окончанию процесса удаления данных из счетчика, в рамках redis - транзакции.
123
+ # Параметры: dumper, redis_connection.
124
+ callback :on_after_delete
125
+
126
+ # Public: Производит перенос данных счетчика.
127
+ #
128
+ # counter - экземпляр счетчика.
129
+ # date - Date - дата, за которую производится перенос данных.
130
+ #
131
+ # Returns Fixnum - кол-во обработанных строк.
132
+ #
133
+ def process!(counter, date)
134
+ @counter, @date = counter, date
135
+
136
+ db_transaction do
137
+ merge_data
138
+ start_redis_transaction
139
+ delete_from_redis
140
+ end
141
+
142
+ commit_redis_transaction
143
+
144
+ rows_processed
145
+ end
146
+
147
+ def initialize
148
+ @destinations = []
149
+ @common_params = {}
150
+ end
151
+
152
+ def fields=(value)
153
+ @fields = value.with_indifferent_access
154
+ end
155
+
156
+ def name=(value)
157
+ @name = value
158
+ @temp_table_name = "tmp_#{@name}"
159
+ end
160
+
161
+ protected
162
+
163
+ attr_accessor :rows_processed
164
+
165
+ def_delegator :redis_session, :multi, :start_redis_transaction
166
+ def_delegator :redis_session, :exec, :commit_redis_transaction
167
+ def_delegator :db_connection, :transaction, :db_transaction
168
+ def_delegator :db_connection, :quote
169
+
170
+ def merge_data
171
+ fire_callback(:on_before_merge, self, db_connection)
172
+
173
+ # копируем данные счетчика в временную таблицу
174
+ create_temp_table
175
+ fill_temp_table
176
+ analyze_table
177
+
178
+ # мерджим в целевые таблицы
179
+ destinations.each { |dest| dest.merge }
180
+
181
+ fire_callback(:on_after_merge, self, db_connection)
182
+ end
183
+
184
+ def fill_temp_table
185
+ @rows_processed = counter.data(:date => formatted_date) do |batch|
186
+ @current_batch = batch
187
+ prepare_batch
188
+ insert_batch
189
+ end
190
+ end
191
+
192
+ def prepare_batch
193
+ fields_keys = fields.keys
194
+
195
+ @current_batch.map! do |row|
196
+ row.merge!(common_params)
197
+ fire_callback(:on_prepare_row, self, row)
198
+
199
+ # выбираем из хеша только указанные поля
200
+ fields_keys.inject(HashWithIndifferentAccess.new) do |result, (field)|
201
+ result.merge!(field => row.fetch(field))
202
+ end
203
+ end
204
+ end
205
+
206
+ def insert_batch
207
+ db_connection.execute <<-SQL
208
+ INSERT INTO #{temp_table_name} VALUES #{batch_data}
209
+ SQL
210
+ end
211
+
212
+ def batch_data
213
+ @current_batch.map! do |row|
214
+ values = row.map do |field, value|
215
+ next 'null' if value.nil?
216
+ fields.fetch(field).eql?(:integer) ? value : quote(value)
217
+ end
218
+
219
+ "(#{values.join(',')})"
220
+ end.join(',')
221
+ end
222
+
223
+ def delete_from_redis
224
+ redis_session.pipelined do |redis|
225
+ counter.partitions(:date => formatted_date).each do |partition|
226
+ counter.delete_partition_direct!(partition, redis)
227
+ end
228
+ end
229
+
230
+ fire_callback(:on_after_delete, self, redis_session)
231
+ end
232
+
233
+ def redis_session
234
+ @redis_session ||= begin
235
+ redis = ::Redis.new(counter.redis.client.options)
236
+ ::Redis::Namespace.new(counter.redis.namespace, :redis => redis)
237
+ end
238
+ end
239
+
240
+ def create_temp_table
241
+ db_connection.execute <<-SQL
242
+ CREATE TEMP TABLE #{temp_table_name} (
243
+ #{columns_definition}
244
+ ) ON COMMIT DROP
245
+ SQL
246
+ end
247
+
248
+ def analyze_table
249
+ db_connection.execute <<-SQL
250
+ ANALYZE #{temp_table_name}
251
+ SQL
252
+ end
253
+
254
+ def columns_definition
255
+ @fields.map do |field, type|
256
+ pg_field_type = case type
257
+ when :string, :text
258
+ 'character varying(4000)'
259
+ when :integer, :serial, :number
260
+ 'integer'
261
+ when :date
262
+ 'date'
263
+ when :timestamp
264
+ 'timestamp'
265
+ when :boolean
266
+ 'boolean'
267
+ else
268
+ raise 'Unknown datatype %s for %s field' % [type, field]
269
+ end
270
+
271
+ "#{field} #{pg_field_type}"
272
+ end.join(',')
273
+ end
274
+
275
+ def formatted_date
276
+ date.strftime(DATE_FORMAT)
277
+ end
278
+
279
+ def db_connection
280
+ destinations.first.connection
281
+ end
282
+ end
283
+ end
284
+ end
@@ -0,0 +1,17 @@
1
+ # coding: utf-8
2
+ require 'active_support/core_ext/hash/indifferent_access'
3
+ require_relative 'dsl/list'
4
+
5
+ module RedisCounters
6
+ module Dumpers
7
+ class List
8
+ include ::RedisCounters::Dumpers::Dsl::List
9
+
10
+ attr_accessor :dumpers
11
+
12
+ def initialize
13
+ @dumpers = HashWithIndifferentAccess.new
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module RedisCounters
2
+ module Dumpers
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'redis_counters/dumpers/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'redis_counters-dumpers'
8
+ spec.version = RedisCounters::Dumpers::VERSION
9
+ spec.authors = ['Merkushin']
10
+ spec.email = ['bibendi@bk.ru']
11
+ spec.summary = 'Dump statistics from Redis to DB'
12
+ spec.homepage = 'https://github.com/abak-press/redis_counters-dumpers'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_dependency 'activesupport', '>= 3.0'
21
+ spec.add_dependency 'activerecord', '>= 3.0'
22
+ spec.add_dependency 'redis', '>= 3.0'
23
+ spec.add_dependency 'redis-namespace', '>= 1.3'
24
+ spec.add_dependency 'callbacks_rb', '>= 0.0.1'
25
+
26
+ spec.add_development_dependency 'bundler', '>= 1.7'
27
+ spec.add_development_dependency 'rake', '>= 10.0'
28
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis_counters-dumpers
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Merkushin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ prerelease: false
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ requirement: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - '>='
23
+ - !ruby/object:Gem::Version
24
+ version: '3.0'
25
+ type: :runtime
26
+ name: activesupport
27
+ - !ruby/object:Gem::Dependency
28
+ prerelease: false
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '3.0'
39
+ type: :runtime
40
+ name: activerecord
41
+ - !ruby/object:Gem::Dependency
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '3.0'
53
+ type: :runtime
54
+ name: redis
55
+ - !ruby/object:Gem::Dependency
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '1.3'
67
+ type: :runtime
68
+ name: redis-namespace
69
+ - !ruby/object:Gem::Dependency
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 0.0.1
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - '>='
79
+ - !ruby/object:Gem::Version
80
+ version: 0.0.1
81
+ type: :runtime
82
+ name: callbacks_rb
83
+ - !ruby/object:Gem::Dependency
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '1.7'
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '1.7'
95
+ type: :development
96
+ name: bundler
97
+ - !ruby/object:Gem::Dependency
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '10.0'
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '10.0'
109
+ type: :development
110
+ name: rake
111
+ description:
112
+ email:
113
+ - bibendi@bk.ru
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - .gitignore
119
+ - Gemfile
120
+ - LICENSE.txt
121
+ - README.md
122
+ - Rakefile
123
+ - lib/redis_counters/dumpers.rb
124
+ - lib/redis_counters/dumpers/destination.rb
125
+ - lib/redis_counters/dumpers/dsl/base.rb
126
+ - lib/redis_counters/dumpers/dsl/destination.rb
127
+ - lib/redis_counters/dumpers/dsl/engine.rb
128
+ - lib/redis_counters/dumpers/dsl/list.rb
129
+ - lib/redis_counters/dumpers/engine.rb
130
+ - lib/redis_counters/dumpers/list.rb
131
+ - lib/redis_counters/dumpers/version.rb
132
+ - redis_counters-dumpers.gemspec
133
+ homepage: https://github.com/abak-press/redis_counters-dumpers
134
+ licenses:
135
+ - MIT
136
+ metadata: {}
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubyforge_project:
153
+ rubygems_version: 2.4.2
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: Dump statistics from Redis to DB
157
+ test_files: []