aeternitas 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: 4d4b481331c2401685d1ff981be632b2d7696a60
4
+ data.tar.gz: 4b810bc3546cc5150d74c45a1538c64a71ce3442
5
+ SHA512:
6
+ metadata.gz: bfb492a28ccc5bc19153b96d8ae2b8c66bd9418d0e43320ff08c387d4ba07e8fc4840028c3b625dfb7e7f409cd8ece47130019a3bf39fd38dbe9692ad7df7994
7
+ data.tar.gz: abe100eb9b6c2ceabd31f713bc46a5d4ae552fb5ef0ab44871f4d1cdc564ebc36fe62b5c3e2c8012cf39882d180658a556afa5d39f511a58341d1320f90e6d52
@@ -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
+
11
+ .idea/
12
+
13
+ ## File-based project format:
14
+ *.iws
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,2 @@
1
+ Metrics/LineLength:
2
+ Max: 100
@@ -0,0 +1 @@
1
+ 2.4.0
@@ -0,0 +1,8 @@
1
+ sudo: false
2
+ services:
3
+ - redis-server
4
+ language: ruby
5
+ rvm:
6
+ - 2.3.3
7
+ - 2.4.0
8
+ before_install: gem install bundler -v 1.13.7
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at max@kopfueber.org. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aeternitas.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 DarthMax
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,289 @@
1
+ ![Aeternitas](https://github.com/FHG-IMW/aeternitas/blob/master/logo.png?raw=true)
2
+
3
+ [![Build Status](https://travis-ci.org/FHG-IMW/aeternitas.svg?branch=master)](https://travis-ci.org/FHG-IMW/aeternitas)
4
+
5
+ A ruby gem for continuous source retrieval and data integration.
6
+
7
+ Aeternitas provides means to regularly "poll" resources (i.e. a website, twitter feed or API) and to permanently store retrieved results.
8
+ By default it avoids putting too much load on external servers and stores raw results as compressed files on disk.
9
+ Aeternitas can be configured to a wide variety of polling strategies (e.g. frequencies, cooldown periods, ignoring exceptions, deactivating resources, ...).
10
+
11
+ Aeternitas is meant to be included in a rails application and expects a working sidekiq/redis setup and any kind of database backend.
12
+ All meta-data is stored in two database tables (aeternitas_pollable_meta_data and aeternitas_sources) while metrics are stored in Redis
13
+ and raw data as compressed files on disk.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'aeternitas'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle install
26
+ $ rails generate aeternitas:install
27
+ $ rake db:migrate
28
+
29
+ to install gem and generate tables and initializers needed for aeternitas.
30
+
31
+ ## Quickstart
32
+
33
+ Aeternitas expects you wanting to store single pollables as ActiveRecord Objects. For instance you might want to
34
+ monitor several Websites for the usage of the word æternitas and store the websites old states for later analysis.
35
+ Using Aeternitas you would create your website model and tables
36
+
37
+ $ rails generate model Website url:string aeternitas_word_count:integer
38
+
39
+ And then include Aeternitas
40
+
41
+
42
+ ```ruby
43
+ class Website < ApplicationRecord
44
+ include Aeternitas::Pollable
45
+
46
+ polling_options do
47
+ polling_frequency :weekly
48
+ end
49
+ end
50
+ ```
51
+
52
+ For now we are satisfied with aeternitas default setting [TODO:link] except for the polling_frequency. We only want to
53
+ check once a week.
54
+ Next up we have to implement the websites `poll` method.
55
+
56
+ ```ruby
57
+ def poll
58
+ page_content = Net::HTTP.get(URI.parse(self.url))
59
+ add_source(page_content) #store the retrieved page permanently
60
+ aeternitas_word_count = page_content.scan('aeternitas').size
61
+ update(aeternitas_word_count: aeternitas_word_count)
62
+ end
63
+ ```
64
+
65
+ The poll method is called every time a pollable is good to go. In our example this would be once a week. The time at which
66
+ aeternitas will execute the poll method is determined by the pollable metadata stored in a separate table and may be
67
+ checked using the `next_polling` method on a website (note: there are several advanced error states which may or may not
68
+ allow a pollable to be polled).
69
+
70
+ Assuming you have already setup sidekiq the only thing left is to regularly run `Aeternitas.enqueue_due_pollables`
71
+ and have a worker consuming the "polling" queue.
72
+
73
+ In most cases it makes sense to store polling results as sources to allow further work to be done in separate jobs.
74
+ In above example we already added the `page_content`as a source to the website. Aeternitas thereby only stores a new source
75
+ if the sources fingerprint does not yet exist (i.e. MD5 Hash of the page_content). If we wanted to process the word count in
76
+ a separate job the following implementation would allow to do so.
77
+
78
+ ```ruby
79
+ class Website < ApplicationRecord
80
+ include Aeternitas::Pollable
81
+
82
+ polling_options do
83
+ polling_frequency :weekly
84
+ end
85
+
86
+ def poll
87
+ page_content = Net::HTTP.get(URI.parse(self.url))
88
+ new_source = add_source(page_content) #returns nil if source already exists
89
+ CountWordJob.perform_async(new_source.id) if new_source
90
+
91
+
92
+ end
93
+ end
94
+
95
+ class CountWordJob
96
+ include Sidekiq::Worker
97
+
98
+ def perform(source_id)
99
+ source = Aeternitas::Source.find(source_id)
100
+ page_content = source.raw_content
101
+ aeternitas_word_count = page_content.scan('aeternitas').size
102
+ website = source.pollable
103
+ website.update(aeternitas_word_count: aeternitas_word_count)
104
+ end
105
+ end
106
+ ```
107
+
108
+ ## Configuration
109
+
110
+ ### Global Configuration
111
+
112
+ In this configuration you can specify the global settings for Æternitas. The configuration should be stored in
113
+ `config/initializers/aeternitas.rb`
114
+
115
+ #### redis
116
+
117
+ This option specifies the redis connection details. Æternitas uses redis to for resource locking and to store statistics.
118
+
119
+ ```ruby
120
+ Aeternitas.configure do |config|
121
+ config.redis = {host: localhost, port: 6379}
122
+ end
123
+ ```
124
+
125
+ For configuration options you can have a look here: [redis-rb](https://github.com/redis/redis-rb)
126
+
127
+ #### storage_adapter
128
+ _Default: Aeternitas::StorageAdapter::File_
129
+
130
+ Æternitas by default stores source file in compressed files on disk. If you however want to store them in another way you
131
+ can do so easily by implementing the `Aeternitas::StorageAdapter` interface. For an example you can have a look at
132
+ `Aeternitas::StorageAdapter::File`.
133
+ To specify which storage adapter Æternitas should use, just pass the class name to this option:
134
+
135
+ ```ruby
136
+ Aeternitas.configure do |config|
137
+ config.storage_adapter = Aeternitas::StorageAdapter::File
138
+ end
139
+ ```
140
+
141
+ #### storage_adapter_options
142
+
143
+ Some storage adapter need some extra configuration. The file adapter for example needs to now where to store the files:
144
+
145
+ ```ruby
146
+ Aeternitas.configure do |config|
147
+ config.storage_adapter_options = {
148
+ directory: File.join(Rails.root, 'public', 'sources')
149
+ }
150
+ end
151
+ ```
152
+
153
+ ### Pollable Configuration
154
+
155
+ Pollables can be configured on a per class base using the `polling_options` block.
156
+
157
+
158
+ #### polling_frequency
159
+ _Default: :daily_
160
+
161
+ This option controls how often a pollable is polled and can be configured in two different ways.
162
+ Either use one of the presets specified in `Aeternitas::PollingFrequency` by specifiey the presets name as a symbol
163
+
164
+ ```ruby
165
+ polling_options do
166
+ polling_frequency :weekly
167
+ end
168
+ ```
169
+
170
+ If want to specify a more complex polling schema you can also us a custom method which returns the time and date of the
171
+ next polling. For example if you want to increase the frequency depending on the pollables age you could use the
172
+ following method:
173
+
174
+ ```ruby
175
+ polling_options do
176
+ # set frequency depending elements age (+ 1 month for every 3 months)
177
+ polling_frequency ->(context) { 1.month + (Time.now - context.created_at).to_i / 3.month * 1.month }
178
+ end
179
+ ```
180
+
181
+ #### before_polling
182
+ _Default: []_
183
+
184
+ Specifies methods that are run before the polling is executed. You can either specify a method name or a lamba
185
+ ```ruby
186
+ polling_options do
187
+ # run the pollables `foo` and `bar` methods
188
+ before_polling :foo, :bar
189
+ # run a custom block
190
+ before_polling ->(pollable) { puts "About to poll #{pollable.id}"}
191
+ end
192
+ ```
193
+
194
+ #### after_polling
195
+
196
+ Specify methods run after polling was successful. See __before_polling__
197
+
198
+ #### deactivation_errors
199
+ _Default: []_
200
+
201
+ Specify error clases which, once they occur, will instantly deactivate the pollable. This can be useful for example if
202
+ the error implied that the resource does not exist any more.
203
+
204
+ ```ruby
205
+ polling_options do
206
+ # deactivate the pollable if the tweet was not found
207
+ deactivation_errors Twitter::Error::NotFound
208
+ end
209
+ ```
210
+
211
+ #### ignored_errors
212
+ _Default: []_
213
+
214
+ Errors specified as ignored errors are wrapped within `Aeternitas::Errors::Ignored`. which is then raised instead.
215
+ This is supposed to be used in combination with error tracking systems like Airbrake. Instead globally telling Airbrake which errors to
216
+ ignore, you can do this on a per pollable basis
217
+
218
+ ```ruby
219
+ polling_options do
220
+ # don't log an error if the twitter api is down
221
+ ignored_errors Twitter::Error::ServiceUnavailable
222
+ end
223
+ ```
224
+
225
+ #### sleep_on_guard_lock
226
+ _Default: false_
227
+
228
+ With this option set to true, if a pollable can't acquire the lock, it will sleep until the guard_timeout expires,
229
+ effectively blocking the Sidekiq queue from processing any other jobs.
230
+ This should *only* be used, if you know that all the jobs within this queue will try to access the same resource and you want to pause the entire queue.
231
+
232
+ #### queue
233
+ _Default: 'polling'_
234
+
235
+ This option specifies the Sidekiq queue into which the poll job will be enqueued.
236
+
237
+ #### guard_key
238
+ _Default: "#{obj.class.name}"_
239
+
240
+ This option specifies the guard key. This can be done by either specifying a method name or a custom block.
241
+ Default is to lock on pollable class level. Therefor only one job at a time per pollable class will be executed to
242
+ avoid DDOSing by accident.
243
+
244
+ ```ruby
245
+ polling_options do
246
+ # use the urls host as lock key
247
+ guard ->(website) { URI.parse(website.url).host }
248
+ end
249
+ ```
250
+
251
+ ## Web UI
252
+
253
+ Æternitas also has a monitoring interface which can be integrated with a rail application.
254
+
255
+ To use it add this line to your application's Gemfile:
256
+
257
+ ```ruby
258
+ gem 'aeternitas_web_ui'
259
+ ```
260
+
261
+ Then run
262
+
263
+ $ bundle install
264
+
265
+ And mount the engine
266
+
267
+ ```ruby
268
+ # config/routes.rb
269
+
270
+ mount Aeternitas::WebUi::Engine => '/aeternitas'
271
+ ```
272
+
273
+ For more information head over to the project page: [https://github.com/FHG-IMW/aeternitas_web_ui](https://github.com/FHG-IMW/aeternitas_web_ui)
274
+
275
+ ## Development
276
+
277
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
278
+
279
+ 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`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
280
+
281
+ ## Contributing
282
+
283
+ Bug reports and spec backed pull requests are welcome on GitHub at https://github.com/FHG-IMW/aeternitas. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
284
+
285
+
286
+ ## License
287
+
288
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
289
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,44 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'aeternitas/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'aeternitas'
8
+ spec.version = Aeternitas::VERSION
9
+ spec.authors = ['Michael Prilop', 'Max Kießling', 'Robert Terbach']
10
+ spec.email = ['max@kopfueber.org', 'michael.prilop@imw.fraunhofer.de']
11
+
12
+ spec.summary = "æternitas - A ruby gem for continuous source retrieval and data integration"
13
+ spec.description = <<-EOF
14
+ Æternitas provides means to regularly 'poll' resources (i.e. a website, twitter feed or API) and to permanently
15
+ store retrieved results. By default æternitas avoids putting too much load on external servers and stores raw
16
+ results as compressed files on disk. It can be configured to a wide variety of polling strategies (e.g. frequencies,
17
+ cooldown periods, ignoring exceptions, deactivating resources, ...)."
18
+ EOF
19
+ spec.homepage = "https://github.com/FHG-IMW/aeternitas"
20
+ spec.license = "MIT"
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
+ f.match(%r{^(test|spec|features)/})
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_dependency 'activerecord', '~> 5'
30
+ spec.add_dependency 'redis'
31
+ spec.add_dependency 'connection_pool'
32
+ spec.add_dependency 'aasm'
33
+ spec.add_dependency 'sidekiq', '> 4'
34
+ spec.add_dependency 'sidekiq-unique-jobs', '~> 5.0'
35
+ spec.add_dependency 'tabstabs'
36
+
37
+ spec.add_development_dependency 'bundler', '~> 1.13'
38
+ spec.add_development_dependency 'rake', '~> 10.0'
39
+ spec.add_development_dependency 'rspec', '~> 3.0'
40
+ spec.add_development_dependency 'sqlite3'
41
+ spec.add_development_dependency 'database_cleaner', '~> 1.5'
42
+ spec.add_development_dependency 'rspec-sidekiq'
43
+ spec.add_development_dependency 'mock_redis'
44
+ end