nexaas-async-collector 1.0.0

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: 171dcf42099cb98d18b2492a2a3cb499e95d99cc
4
+ data.tar.gz: f361911c8d5751a9df609c457c9c4c44758c16e6
5
+ SHA512:
6
+ metadata.gz: 4a12a3ddd8ce4ea14d1669990a09976bae771eb45fd0106adf6308eccb80836bde722be0c3feea978d81c63d7e0680d9ef6565f802b1488c96e4dbf1dbfb305a
7
+ data.tar.gz: dfea2bb81b8ada2ef1c290b85b46261aecc955a4d1dfab2ce8f18876274831a0d7f2dbed1f156d36d0be937c788bf5aa299ae0d6e917cc158b1bdb1ab8ff1bf3
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Eduardo Hertz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # Nexaas::Async::Collector
2
+ Agnostic collector and generator of async content for Rails apps. Used in production in a few [Nexaas](www.nexaas.com) systems.
3
+
4
+ This gems is compatible with Ruby 2.1+ and Rails 3.2+.
5
+
6
+ ## Prerequisites
7
+ The prerequisites of this project are:
8
+ - Sidekiq 4+
9
+ - Redis
10
+ - JQuery 1.0+
11
+
12
+ ## Installation
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'nexaas-async-collector'
17
+ ```
18
+
19
+ And then run:
20
+
21
+ ```bash
22
+ $ bundle install
23
+ ```
24
+
25
+ Or install it yourself as:
26
+
27
+ ```bash
28
+ $ gem install nexaas-async-collector
29
+ ```
30
+
31
+ ## Usage
32
+ 1) Create the file `config/initializers/nexaas-async-collector.rb` with the following content (edit as you need):
33
+
34
+ ```ruby
35
+ Nexaas::Async::Collector.configure do |config|
36
+ # Your redis URL. The default value will be the value of yout REDIS_URL env var
37
+ config.redis_url = "redis://test.local"
38
+
39
+ # The namespace where you want to store you data within Redis
40
+ config.redis_namespace = 'nexaas_async'
41
+
42
+ # The method that returns the use robject (or anyother object you want. It must respond to id method)
43
+ config.scope = :current_user
44
+
45
+ # The parent class of all nexaas-async-collector controller
46
+ config.parent_controller = "::ActionController::Base"
47
+ end
48
+ ```
49
+
50
+ 2) Use the view helper to do all the process:
51
+
52
+ ```ruby
53
+ <%= nexaas_async_collector(user.id, ModelService, :model_method, [arg1, arg2]) %>
54
+ ```
55
+
56
+ ## Contributing
57
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/myfreecomm/nexaas-async-collector](https://github.com/myfreecomm/nexaas-async-collector). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
58
+
59
+ After downloading the project and install all gems (through `bundler`) don't forget to install appraisal gems dependencies:
60
+
61
+ ```
62
+ $ appraisal install
63
+ ```
64
+
65
+ This is necessary to test the code in different versions of Ruby and Rails. To run the full suite of tests:
66
+
67
+ ```
68
+ $ bundle exec rake spec
69
+ ```
70
+
71
+ ## License
72
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ begin
5
+ require 'bundler/setup'
6
+ rescue LoadError
7
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
8
+ end
9
+
10
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
11
+ load 'rails/tasks/engine.rake'
12
+ load 'rails/tasks/statistics.rake'
13
+
14
+ begin
15
+ require 'rspec/core/rake_task'
16
+ RSpec::Core::RakeTask.new(:spec)
17
+ rescue LoadError
18
+ end
19
+
20
+ namespace :spec do
21
+ task :all do
22
+ system('appraisal rake spec')
23
+ end
24
+ end
25
+
@@ -0,0 +1,9 @@
1
+ module Nexaas
2
+ module Async
3
+ module Collector
4
+ class ApplicationController < Nexaas::Async::Collector.configuration.parent_controller.constantize
5
+ protect_from_forgery with: :exception
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ module Nexaas
2
+ module Async
3
+ module Collector
4
+ class AsyncResourceController < ApplicationController
5
+ def show
6
+ @result = Result.new(collector_scope.id, params[:id])
7
+ respond_to { |f| f.js }
8
+ end
9
+
10
+ private
11
+
12
+ def nexaas_async_collector_scope
13
+ Nexaas::Async::Collector.configuration.scope
14
+ end
15
+
16
+ def collector_scope
17
+ send(nexaas_async_collector_scope)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ module Nexaas
2
+ module Async
3
+ module Collector
4
+ module ApplicationHelper
5
+ #
6
+ # Helper to enqueue AsyncResourceJob and include the JavaScript code to request the result
7
+ # - user_id: an ID that is unique for each logged user (maybe user.id, account.id, organization.id, etc)
8
+ # - klass_name: The name of the class responsible for generate the content to be stored in the memory
9
+ # - klass_method: The name of the class method to be called
10
+ # - args: The arguments to be passed in the call of the class method
11
+ #
12
+ # Example:
13
+ # <%= nexaas_async_collect(current_user.id, ReportGenerator, :generate, []) %>
14
+ #
15
+ def nexaas_async_collect(user_id, klass_name, klass_method, args=[])
16
+ AsyncResourceJob.perform_async(collector_user_id, user_id, klass_name, klass_method, args)
17
+ render(partial: 'nexaas/async/collector/async_resource/show')
18
+ end
19
+
20
+ private
21
+
22
+ def collector_user_id
23
+ @collector_user_id ||= SecureRandom.hex(15)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ <style type="text/css">
2
+ .nexaas-async-collector-loading img {
3
+ vertical-align: middle;
4
+ }
5
+
6
+ .nexaas-async-collector-loading {
7
+ text-align: center;
8
+ }
9
+ </style>
10
+
11
+ <div class="nexaas-async-collector-loading">
12
+ <%= image_tag 'nexaas/async/collector/spin.svg' %> <%= I18n.t('nexaas.async.collector.loading') %>
13
+ </div>
@@ -0,0 +1,12 @@
1
+ <script type="text/javascript" id="js-nexaas-async-collector">
2
+ var requestContent = function() {
3
+ $.ajax({
4
+ url: '<%= Rails.application.routes.url_helpers.nexaas_async_collect_path(@collector_user_id) %>',
5
+ dataType: 'script'
6
+ })
7
+ }
8
+
9
+ window.nexaas_async_interval = setInterval(requestContent, 3000);
10
+ </script>
11
+
12
+ <%= render partial: "nexaas/async/collector/async_resource/loading" %>
@@ -0,0 +1,5 @@
1
+ <% if @result.content_is_ready? %>
2
+ $('#js-nexaas-async-collector').parent().append("<%= escape_javascript(@result.content.html_safe) %>");
3
+ $('.nexaas-async-collector-loading').remove();
4
+ clearInterval(window.nexaas_async_interval);
5
+ <% end %>
@@ -0,0 +1,26 @@
1
+ module Nexaas
2
+ module Async
3
+ module Collector
4
+ class AsyncResourceJob
5
+
6
+ include Sidekiq::Worker
7
+ sidekiq_options queue: :high_fast
8
+
9
+ def perform(collector_id, user_id, klass_name, klass_method, args=[])
10
+ content = call_for_method(klass_name, klass_method, args)
11
+ Persist.save(user_id, collector_id, content)
12
+ end
13
+
14
+ private
15
+
16
+ def call_for_method(klass_name, klass_method, args=[])
17
+ if args.any?
18
+ klass_name.to_s.constantize.send(klass_method, *args)
19
+ else
20
+ klass_name.to_s.constantize.send(klass_method)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ en:
2
+ nexaas:
3
+ async:
4
+ collector:
5
+ loading: 'Loading...'
@@ -0,0 +1,5 @@
1
+ pt:
2
+ nexaas:
3
+ async:
4
+ collector:
5
+ loading: 'Carregando...'
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ get 'nexaas/async/collect/:id' => 'nexaas/async/collector/async_resource#show', as: :nexaas_async_collect
3
+ end
@@ -0,0 +1,23 @@
1
+ require 'redis-namespace'
2
+ require 'sidekiq'
3
+ require "nexaas/async/collector/engine"
4
+ require "nexaas/async/collector/configuration"
5
+ require "nexaas/async/collector/in_memory_storage"
6
+ require "nexaas/async/collector/result"
7
+ require "nexaas/async/collector/persist"
8
+
9
+ module Nexaas
10
+ module Async
11
+ module Collector
12
+ class << self
13
+ def configure
14
+ yield(configuration)
15
+ end
16
+
17
+ def configuration
18
+ @configuration ||= Configuration.new
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module Nexaas
2
+ module Async
3
+ module Collector
4
+ class Configuration
5
+ attr_accessor :redis_url, :redis_namespace, :scope, :parent_controller
6
+
7
+ def initialize
8
+ @redis_url = ENV.fetch('REDIS_URL') { nil }
9
+ @redis_namespace = 'nexaas_async'
10
+ @scope = :current_user
11
+ @parent_controller = '::ActionController::Base'
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module Nexaas
2
+ module Async
3
+ module Collector
4
+ class Engine < ::Rails::Engine
5
+ initializer 'Nexaas::Async::Collector precompile hook', group: :all do |app|
6
+ app.config.assets.precompile += %w(
7
+ nexaas/async/collector/spin.svg
8
+ )
9
+ end
10
+
11
+ config.generators do |g|
12
+ g.test_framework :rspec
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ module Nexaas
2
+ module Async
3
+ module Collector
4
+ class InMemoryStorage
5
+
6
+ attr_reader :connection, :namespace
7
+
8
+ def initialize
9
+ @connection ||= Redis.new(url: redis_url)
10
+ @namespace ||= Redis::Namespace.new(redis_namespace, redis: connection) if redis_namespace
11
+ end
12
+
13
+ def get(key)
14
+ storage.get(key)
15
+ end
16
+
17
+ def set(key, value, expiration=nil)
18
+ storage.set(key, value)
19
+ storage.expire(key, expiration) if expiration
20
+ end
21
+
22
+ private
23
+
24
+ def storage
25
+ @namespace || @connection
26
+ end
27
+
28
+ def redis_url
29
+ Nexaas::Async::Collector.configuration.redis_url
30
+ end
31
+
32
+ def redis_namespace
33
+ Nexaas::Async::Collector.configuration.redis_namespace
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ module Nexaas
2
+ module Async
3
+ module Collector
4
+ class Persist
5
+ class << self
6
+ def save(user_id, key, value)
7
+ storage.set(key, content(user_id, value))
8
+ end
9
+
10
+ def content(user_id, value)
11
+ {
12
+ 'user_id' => user_id,
13
+ 'content' => value
14
+ }.to_json
15
+ end
16
+
17
+ private
18
+
19
+ def storage
20
+ InMemoryStorage.new
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,52 @@
1
+ # Content is stored in the database as the following JSON:
2
+ # {
3
+ # 'user_id' => 'user_id',
4
+ # 'content' => 'generated content'
5
+ # }
6
+ # The content is scoped by user, so any other user who discover the key (id) of the content, will not be able
7
+ # to access only. Only the user who created it.
8
+ # Maybe leave this more generic?
9
+
10
+ module Nexaas
11
+ module Async
12
+ module Collector
13
+ class Result
14
+
15
+ attr_reader :user_id, :id, :content
16
+
17
+ def initialize(user_id, id)
18
+ @user_id = user_id
19
+ @id = id
20
+ end
21
+
22
+ def content
23
+ _content['content'] if content_is_ready?
24
+ end
25
+
26
+ def content_is_ready?
27
+ _content && _content['user_id'] == user_id && _content['content']
28
+ end
29
+
30
+ private
31
+
32
+ def storage
33
+ @storage ||= InMemoryStorage.new
34
+ end
35
+
36
+ def _content
37
+ @_content ||= storage.get(id)
38
+ @content ||= JSON.parse(@_content) if @_content
39
+ end
40
+
41
+ def url
42
+ Nexaas::Async::Collector.configuration.redis_url
43
+ end
44
+
45
+ def namespace
46
+ Nexaas::Async::Collector.configuration.redis_namespace
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,7 @@
1
+ module Nexaas
2
+ module Async
3
+ module Collector
4
+ VERSION = '1.0.0'
5
+ end
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nexaas-async-collector
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Eduardo Hertz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sidekiq
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis-namespace
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.5.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.5.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 3.6.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 3.6.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 3.6.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 3.6.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: appraisal
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 2.2.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 2.2.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: fakeredis
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 0.6.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 0.6.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: 9.0.6
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '='
123
+ - !ruby/object:Gem::Version
124
+ version: 9.0.6
125
+ description: Agnostic collector and generator of async content for Rails apps.
126
+ email:
127
+ - eduardohertz@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - MIT-LICENSE
133
+ - README.md
134
+ - Rakefile
135
+ - app/controllers/nexaas/async/collector/application_controller.rb
136
+ - app/controllers/nexaas/async/collector/async_resource_controller.rb
137
+ - app/helpers/nexaas/async/collector/application_helper.rb
138
+ - app/views/nexaas/async/collector/async_resource/_loading.html.erb
139
+ - app/views/nexaas/async/collector/async_resource/_show.html.erb
140
+ - app/views/nexaas/async/collector/async_resource/show.js.erb
141
+ - app/workers/nexaas/async/collector/async_resource_job.rb
142
+ - config/locales/en.yml
143
+ - config/locales/pt.yml
144
+ - config/routes.rb
145
+ - lib/nexaas/async/collector.rb
146
+ - lib/nexaas/async/collector/configuration.rb
147
+ - lib/nexaas/async/collector/engine.rb
148
+ - lib/nexaas/async/collector/in_memory_storage.rb
149
+ - lib/nexaas/async/collector/persist.rb
150
+ - lib/nexaas/async/collector/result.rb
151
+ - lib/nexaas/async/collector/version.rb
152
+ homepage: https://github.com/myfreecomm/nexaas-async-collector
153
+ licenses:
154
+ - MIT
155
+ metadata: {}
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubyforge_project:
172
+ rubygems_version: 2.5.2
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: Collect and generate async content.
176
+ test_files: []