adalog 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +29 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE.md +27 -0
  5. data/README.md +144 -0
  6. data/Rakefile +7 -0
  7. data/adalog.gemspec +27 -0
  8. data/lib/adalog/active_record_repo.rb +62 -0
  9. data/lib/adalog/configuration.rb +46 -0
  10. data/lib/adalog/entry.rb +85 -0
  11. data/lib/adalog/in_memory_repo.rb +38 -0
  12. data/lib/adalog/pstore_repo.rb +62 -0
  13. data/lib/adalog/simple_logging_adapter.rb +21 -0
  14. data/lib/adalog/version.rb +3 -0
  15. data/lib/adalog/web/public/javascripts/adalog.js +2 -0
  16. data/lib/adalog/web/public/javascripts/entries-list.js +82 -0
  17. data/lib/adalog/web/public/javascripts/vanilla-js.js +103 -0
  18. data/lib/adalog/web/public/stylesheets/adalog.css +55 -0
  19. data/lib/adalog/web/public/stylesheets/entries-list.css +99 -0
  20. data/lib/adalog/web/public/stylesheets/pure/buttons-min.css +7 -0
  21. data/lib/adalog/web/public/stylesheets/pure/forms-min.css +7 -0
  22. data/lib/adalog/web/public/stylesheets/pure/menus-min.css +7 -0
  23. data/lib/adalog/web/public/stylesheets/pure/pure-base-min.css +11 -0
  24. data/lib/adalog/web/public/stylesheets/pure/pure-grids-responsive-min.css +7 -0
  25. data/lib/adalog/web/public/stylesheets/pure/tables-min.css +7 -0
  26. data/lib/adalog/web/views/adalog.html.erb +24 -0
  27. data/lib/adalog/web/views/index.html.erb +37 -0
  28. data/lib/adalog/web.rb +103 -0
  29. data/lib/adalog.rb +60 -0
  30. data/test/data/simple.yml +28 -0
  31. data/test/test_helper.rb +35 -0
  32. data/test/unit/active_record_repo.rb +9 -0
  33. data/test/unit/configuration_test.rb +62 -0
  34. data/test/unit/entry_test.rb +138 -0
  35. data/test/unit/in_memory_repo_test.rb +9 -0
  36. data/test/unit/pstore_repo_test.rb +9 -0
  37. metadata +175 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c15c023b9348c277ec76330788aa48ff50e97009
4
+ data.tar.gz: 1a67e84184ec901ae73f58253decf22e0196e8b5
5
+ SHA512:
6
+ metadata.gz: c80d618c9f584f39167f2ef97b42ff1f05eddb0c6b8f0dab7664d21bd8183e05bdb471baeff58627db214f48e38e3a94c81b12eea0f15c3e664d2c6e15677e38
7
+ data.tar.gz: 583ba7212ab7caf2fda0bd7772bbee8af0f7a9b7f59c9ea31c21305593fab93e5f46585e34dd3df75ff9b11fd1b30fa65510d2b3fe567ba78c96af800829c573
data/.gitignore ADDED
@@ -0,0 +1,29 @@
1
+ .DS_Store
2
+ lob/*.log
3
+ log/*.log
4
+ tmp/**/*
5
+ *.swp
6
+ *.tmproj
7
+ tmtags
8
+ .project
9
+ .bundle
10
+ *.sublime-project
11
+ *.sublime-workspace
12
+ .vagrant
13
+ .ruby-version
14
+ .ruby-gemset
15
+ *.gem
16
+ *.rbc
17
+ .config
18
+ .yardoc
19
+ InstalledFiles
20
+ Gemfile.lock
21
+ _yardoc
22
+ coverage
23
+ doc/
24
+ lib/bundler/man
25
+ pkg
26
+ rdoc
27
+ test/tmp
28
+ test/version_tmp
29
+ config.ru
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2015, Paul Kwiatkowski
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of adalog nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # Adalog
2
+
3
+ It pairs some Log-like Repository implementation with a Sinatra app. What an achievement, right? No? I agree, but read on.
4
+
5
+
6
+ ## Motivation
7
+
8
+ Far, far too many third party services do not bother to implement competent sandbox environments. If you're doing "the right thing" and wrapping their client libraries in your own adapter layer (of some variety), you can easily swap out for a stub implementation in development, test or staging environments.
9
+
10
+ Great! But now you have to write a second set test of adapaters yourself. So... not great! And sometimes all you want to do is check they were even sending (or receiving) the right message/data anyway?
11
+
12
+ In the way that Matz "is not a concurrency guy", I "am not a logging guy". If I'm poking around in a UI while developing, I don't want to cram even more into my logs just to make sure an API call with big chunk of JSON got sent off properly, amidst 30 render statements, 3 or 4 SQL queries, and probably some stuff going in to/out of Redis.
13
+
14
+ In fact, since I'm already building a webpage, why can't I see it in my browser?
15
+
16
+ Enter <strong>Log</strong>ging for Stub <strong>Ada</strong>pters: Adalog
17
+
18
+
19
+ ## We _Can_ Have Nice Things
20
+
21
+ Adalog really only needs one thing: A repository to put entries into and take them out of. For this purpose, a repository (repo for short) is anything that responds to four messages: `fetch`, `insert`, `clear!`, and `all`.
22
+
23
+ Adalog even comes with three available repos: `InMemoryRepo`, `PStoreRepo` and `ActiveRecordRepo` which all do exactly what their names suggest.
24
+
25
+ The Sinatra app has (for now) a single page that lists the output of `Adalog.configuration.repo.all` in reverse chronological order. Ideal for running as its own little service or mounting in a Rails app during in the development environment!
26
+
27
+ ## Example Configuration and Usage
28
+
29
+ After the usual addition of `gem 'adalog'` to your `Gemfile` and `bundle install` usage can be achieved by the following:
30
+
31
+ ```ruby
32
+ Adalog.configure do |config|
33
+ # No action needed, however the default repo is a non-threadsafe InMemoryRepo
34
+ end
35
+
36
+ # Insert something that has a 'title', optionally a 'message' and 'details'
37
+ Adalog.configuration.repo.insert(
38
+ title: "StubSendGridAdapter",
39
+ message: "Email Sent")
40
+ ```
41
+
42
+ With only one repo in use, Adalog will forward the messages of `insert`, `fetch`, `clear!` and `all` to that one repo. Like so:
43
+
44
+ ```ruby
45
+ Adalog.insert(
46
+ title: "StubSendGridAdapter",
47
+ message: "Email Sent"
48
+ details: {
49
+ to: "foo@example.com",
50
+ from: "bar@example.com",
51
+ template: "welcome-email"
52
+ })
53
+ ```
54
+
55
+ The default repos also accept entries with a `timestamp`, which defaults to `Time.now` and a `format` of the `details` attribute to assist in displaying in the Sinatra app's view. The `format` defaults to `'json'`.
56
+
57
+ Speaking of the app, it can be executed in the usual manner of creating a `config.ru` file:
58
+
59
+ ```ruby
60
+ require 'adalog'
61
+ run Adalog::Web
62
+ ```
63
+
64
+ followed by `rackup` in a terminal. Or, within a Rails app, it can be mounted via `routes.rb` at a particular path:
65
+
66
+ ```ruby
67
+ YourApp::Application.routes.draw do
68
+
69
+ unless Rails.env.production?
70
+ mount Adalog::Web => '/adalog'
71
+ end
72
+
73
+ end
74
+ ```
75
+
76
+ Visit the particular URL for the Sinatra standalone or Rails route, and _voilà!_
77
+
78
+
79
+ ## Configuration Options
80
+
81
+ Actually, that _"voilà!"_ might have been a little underwhelming. The default `InMemoryRepo` is blank upon each boot (it is in-memory, after all) so the default configuration won't show anything initially. Specifying a different repository will fix that. It is one configuration option among the following:
82
+
83
+ - **repo:** The repository to store and retreive from. Defaults to `Adalog::InMemoryRepo`.
84
+ - **singleton:** Whether or not to add class methods to the root `Adalog` module to access a singular repo. Defaults to `true`.
85
+ - **time_format:** the `strftime` format string to use when displaying dates in the app's views. Defaults to `"%H:%M:%S - %d %b %Y"`.
86
+ - **web_heading:** the title of the heading in the app's views.
87
+ - **erb_layout:** _(not yet implemented)_ set the layout for the app's views.
88
+ - **views_folder:** _(not yet implemented)_ specify a custom views folder for the Sinatra app if you feel like rolling your own entirely new version of the front-end.
89
+
90
+ As an example, here is a configuration that uses a threadsafe PStore to persist the log entries, changes the time formatting, and sets a custom heading:
91
+
92
+ ```ruby
93
+ Adalog.configure do |config|
94
+ pstore_file = "log/adalog-#{ENV['RACK_ENV']}.pstore"
95
+ config.repo = Adalog::PStoreAdapter.new(pstore_file)
96
+ config.time_format = "%b %d %H:%M:%S"
97
+ config.web_heading = "Poor Richard's Third Party API Calls"
98
+ end
99
+ ```
100
+
101
+ And now if, in the course of building out your application, you can make an adapter which simply calls out to `Adalog.insert` with its data rather than a non-sandboxed third-party service, and you can validate "success" (insofar as your local code succeeded) by visiting a web page instead of checking a logfile!
102
+
103
+
104
+ ## Included Adapters
105
+
106
+ Much like how useful repositories come built-in, there are also basic adapters included in the project. Presently the only included adapter that saves you from writing your own, is `SimpleLoggingAdapter`, which uses `method_missing` to write entries for every method call made on it. For example:
107
+
108
+ ```ruby
109
+ # After configuring Adalog
110
+ adapter = Adalog::SimpleLoggingAdapter.new("StubMoosendAdapter", Adalog.repo)
111
+ adapter.unsubscribe_email('baz@example.com')
112
+ ```
113
+
114
+ And now `Adalog.repo` contains the entry:
115
+ ```ruby
116
+ { title: "StubMoosendAdapter",
117
+ timestamp: "...",
118
+ message: "unsubscribe_email",
119
+ details: "['baz@example.com']",
120
+ format: "json"
121
+ }
122
+ ```
123
+
124
+ Although one would obviously want to use a more dependency-injection-esque style when choosing `Adalog::SimpleLoggingAdapter` over whatever the production-mode default adapter is for the service in question.
125
+
126
+ ## Project Status and Goals
127
+
128
+ This project adheres to [Semantic Versioning](http://semver.org/), including the part whereby being pre-1.0 means that all bets are off, code can and will change radically, and that it should not be considered "production-ready" until said time as version `1.0.0` is declared.
129
+
130
+ This library is being test-driven by its inclusion within a functioning production application, so rest assured it will get there soon enough. In the mean time, feedback is welcome. Bug reports about functionality that the README claims works but doesn't is welcome. But so as not to distract from the original goals, contributions in the form of pull requests will languish until the project's initial functionality is completed.
131
+
132
+
133
+ ### Remaining Work Prior to 1.0.0
134
+
135
+ - Customization of ERB Layout.
136
+ - Customization of Sinatra Layout.
137
+ - Time formats that allow for strictly numeric time or even string-based logical time.
138
+ - A quick script to color-code entries with the same title.
139
+ - Implementations for `InMemoryRepo#fetch` and `PStoreRepo#fetch`.
140
+ - Tests for the three built-in repository classes.
141
+ - A `StubLoggingAdapter` which can be pre-programmed with responses to certain messages.
142
+ - A `MockLoggingAdapter` which can be pre-programmed with responses to certain messages and asked to enforce that some messages were received.
143
+ - A method (working name `MultiWeb`) to allow multiple Sinatra apps to be generated, in case one wants to have a separate route-and-repo combination for every adapter.
144
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = Dir[File.join(File.dirname(__FILE__), 'test/**/*test.rb')]
7
+ end
data/adalog.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'adalog/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "adalog"
8
+ spec.version = Adalog::VERSION
9
+ spec.platform = Gem::Platform::RUBY
10
+ spec.authors = ["Paul Kwiatkowski"]
11
+ spec.email = ["paul@groupraise.com"]
12
+ spec.summary = %q{A not-quite logger, with several storage methods. Records occurrences, provides a web app for viewing entries.}
13
+ spec.description = %q{A not-quite logger, with several storage methods. Records occurrences, provides a web app for viewing entries. Motivated by the need to record the actions of stubbed-out versions of adapters to third-party services, and for that record to be something other than a traditional logger. Web viewer is built in Sinatra and so can be run standalone via the usual methods or mounted at some route within a Rails app. Comes with three repository implementations for storing/retrieving entries and is trivial to write your own.}
14
+ spec.homepage = "https://github.com/swifthand/adalog"
15
+ spec.license = "Revised BSD, see LICENSE.md"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "sinatra", "~> 1.4", ">= 1.4.4"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest-reporters", "~> 1.1"
26
+ spec.add_development_dependency "turn-again-reporter", "~> 1.1", ">= 1.1.0"
27
+ end
@@ -0,0 +1,62 @@
1
+ module Adalog
2
+ class ActiveRecordRepo
3
+
4
+ attr_reader :record_class, :base_relation
5
+
6
+ def initialize(record_class, **repo_options)
7
+ @record_class = record_class
8
+ @base_relation = determine_base_relation(repo_options)
9
+ end
10
+
11
+
12
+ def fetch(**options)
13
+ where_options = options.fetch(:where, {})
14
+ order_options = options.fetch(:order, :none)
15
+ relation = relation_from_options(where_options, order_options)
16
+ if options[:first]
17
+ relation.first(options[:first])
18
+ elsif options[:last]
19
+ relation.last(options[:last])
20
+ else
21
+ relation.to_a
22
+ end
23
+ end
24
+
25
+
26
+ def insert(attr_hash = {}, **attr_args)
27
+ attrs = attr_hash.merge(attr_args)
28
+ record = record_class.new(**attrs)
29
+ if record.valid?
30
+ if record.save
31
+ [:ok, record]
32
+ else
33
+ wtf = "Unknown Non-validation error in call to #{record_class}#save"
34
+ [:error, [wtf]]
35
+ end
36
+ else
37
+ [:error, record.errors.full_messages]
38
+ end
39
+ end
40
+
41
+
42
+ def all
43
+ record_class.all.to_a
44
+ end
45
+
46
+
47
+ private ########################################################################
48
+
49
+
50
+ def relation_from_options(where, order)
51
+ relation = base_relation.dup
52
+ relation = relation.where(where) if where.any?
53
+ relation = relation.order(order) if order != :none
54
+ end
55
+
56
+
57
+ def determine_base_relation(options)
58
+ options.fetch(:base_relation, record_class.unscoped)
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,46 @@
1
+ module Adalog
2
+
3
+ class Configuration
4
+
5
+ RequiredSettings = [:repo, :singleton, :html_erb, :time_format, :web_heading].freeze
6
+ UntouchedValue = Object.new.freeze
7
+
8
+ attr_accessor *RequiredSettings
9
+
10
+ def initialize
11
+ defaults.each_pair do |attr, value|
12
+ self.send("#{attr}=", value)
13
+ end
14
+ end
15
+
16
+
17
+ def defaults
18
+ { repo: Adalog::InMemoryRepo.new,
19
+ singleton: true,
20
+ html_erb: true,
21
+ time_format: "%H:%M:%S - %d %b %Y",
22
+ web_heading: "Stub Adapter Logs",
23
+ }
24
+ end
25
+
26
+
27
+ def web_defaults
28
+ { repo: self.repo,
29
+ time_format: self.time_format,
30
+ heading: self.web_heading,
31
+ }
32
+ end
33
+
34
+
35
+ def validate!
36
+ RequiredSettings.each do |required_attr|
37
+ if UntouchedValue == self.send(required_attr)
38
+ raise "Setting '#{required_attr}' for Adalog left unconfigured."
39
+ end
40
+ end
41
+ end
42
+
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,85 @@
1
+ require 'time'
2
+ require 'date'
3
+
4
+ module Adalog
5
+ class Entry
6
+
7
+ def self.build(obj = nil, **options)
8
+ arguments =
9
+ if nil == obj
10
+ options
11
+ elsif obj.is_a?(Hash) || obj.respond_to?(:[])
12
+ obj
13
+ elsif obj.respond_to?(:to_h)
14
+ obj.to_h.merge(options)
15
+ else
16
+ { title: obj.respond_to?(:title) && obj.title,
17
+ timestamp: obj.respond_to?(:timestamp) && obj.timestamp,
18
+ message: obj.respond_to?(:message) && obj.message,
19
+ details: obj.respond_to?(:details) && obj.details,
20
+ format: obj.respond_to?(:format) && obj.format,
21
+ }.merge(options)
22
+ end
23
+
24
+ self.new(
25
+ title: arguments[:title] || arguments['title'],
26
+ timestamp: arguments[:timestamp] || arguments['timestamp'],
27
+ message: arguments[:message] || arguments['message'],
28
+ details: arguments[:details] || arguments['details'],
29
+ format: arguments[:format] || arguments['format'],
30
+ )
31
+ end
32
+
33
+ attr_reader :title, :timestamp, :message, :details, :errors
34
+
35
+ def initialize(title: nil, timestamp: nil, message: nil, details: nil, format: nil)
36
+ @title = title || ''
37
+ @timestamp = timestamp || Time.now
38
+ @message = message || ''
39
+ @details = details || ''
40
+ @format = format || 'json'
41
+ validate!
42
+ end
43
+
44
+
45
+ def valid?
46
+ @errors.none?
47
+ end
48
+
49
+ ##
50
+ # TODO: Make this something we store and/or can override.
51
+ def format
52
+ :json
53
+ end
54
+
55
+
56
+ def details_blank?
57
+ blank?(details)
58
+ end
59
+
60
+
61
+ private ######################################################################
62
+
63
+
64
+ def validate!
65
+ @errors = []
66
+ errors << content_error if no_content?
67
+ end
68
+
69
+
70
+ def content_error
71
+ "Must have at least one of: 'title', 'message', 'details'."
72
+ end
73
+
74
+
75
+ def no_content?
76
+ blank?(title) && blank?(message) && blank?(details)
77
+ end
78
+
79
+
80
+ def blank?(val)
81
+ nil == val || '' == val
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,38 @@
1
+ module Adalog
2
+ class InMemoryRepo
3
+
4
+ attr_reader :storage
5
+
6
+ def initialize(**repo_options)
7
+ @storage = Array.new
8
+ end
9
+
10
+
11
+ def fetch(**options)
12
+ all
13
+ end
14
+
15
+
16
+ def insert(entry = nil, **options)
17
+ converted_entry = Adalog::Entry.build(entry = nil, **options)
18
+ if converted_entry.valid?
19
+ storage.unshift(converted_entry)
20
+ [:ok, converted_entry]
21
+ else
22
+ [:error, converted_entry.errors]
23
+ end
24
+ end
25
+
26
+
27
+ def clear!
28
+ @storage = Array.new
29
+ :ok
30
+ end
31
+
32
+
33
+ def all
34
+ storage.dup
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,62 @@
1
+ require 'pstore'
2
+
3
+ module Adalog
4
+ class PStoreRepo
5
+
6
+ attr_reader :storage
7
+
8
+ def initialize(path, **repo_options)
9
+ @storage = PStore.new(path, true)
10
+ end
11
+
12
+
13
+ def fetch(**options)
14
+ all
15
+ end
16
+
17
+
18
+ def all
19
+ storage.transaction do
20
+ storage.roots.flat_map do |key|
21
+ storage.fetch(key, [])
22
+ end
23
+ end.reverse
24
+ end
25
+
26
+
27
+ def insert(entry = nil, **options)
28
+ converted_entry = Adalog::Entry.build(entry, **options)
29
+ if converted_entry.valid?
30
+ insert_into_storage(converted_entry)
31
+ [:ok, converted_entry]
32
+ else
33
+ [:error, converted_entry.errors]
34
+ end
35
+ end
36
+
37
+
38
+ def clear!
39
+ storage.transaction do
40
+ all_keys = storage.roots
41
+ all_keys.each do |key|
42
+ storage.delete(key)
43
+ end
44
+ end
45
+ :ok
46
+ end
47
+
48
+
49
+ private ######################################################################
50
+
51
+
52
+ def insert_into_storage(entry)
53
+ storage.transaction do
54
+ key = Time.now.to_i
55
+ list = storage.fetch(key, [])
56
+ list.unshift(entry)
57
+ storage[key] = list
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,21 @@
1
+ module Adalog
2
+ class SimpleLoggingAdapter
3
+
4
+ attr_reader :service_name, :repo
5
+
6
+ def initialize(service_name, repo)
7
+ @service_name = service_name
8
+ @repo = repo
9
+ end
10
+
11
+ ##
12
+ # TODO: Record something w.r.t. whether or not a block is given?
13
+ def method_missing(msg, *args, &block)
14
+ repo.insert(
15
+ title: service_name,
16
+ message: msg,
17
+ details: args)
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Adalog
2
+ VERSION = "0.4.0"
3
+ end
@@ -0,0 +1,2 @@
1
+ // A namespace all our own, to guard jealously and puts javascript within.
2
+ var adalog = Object.create(null);
@@ -0,0 +1,82 @@
1
+ adalog.entriesList = (function(js) {
2
+
3
+ var showButtonClass = '-show-details';
4
+ var hideButtonClass = '-hide-details';
5
+ var detailsContentClass = '-details-content';
6
+ var toggleDetailsClass = '-toggle-details';
7
+
8
+
9
+ var init = function init() {
10
+ js.docReady(function() {
11
+ formatDetailsJSON();
12
+ // initVisibilityButtons();
13
+ initDetailsToggle();
14
+ });
15
+ };
16
+
17
+
18
+ var formatDetailsJSON = function formatDetailsJSON() {
19
+ var detailsElements = document.getElementsByClassName('-format-as-json');
20
+ js.convertArrayLike(detailsElements).forEach(function(elt) {
21
+ try {
22
+ var contentsAsObj = JSON.parse(elt.textContent);
23
+ elt.textContent = JSON.stringify(contentsAsObj, null, 2);
24
+ } catch(exc) {
25
+ console.log("Failed to parse details of element that was expicitly flagged with '-format-as-json'.");
26
+ }
27
+ });
28
+ };
29
+
30
+
31
+ var initDetailsToggle = function initDetailsToggle() {
32
+ var detailsToggles = document.getElementsByClassName(toggleDetailsClass);
33
+ var detailsRegions = document.getElementsByClassName(detailsContentClass);
34
+
35
+ js.convertArrayLike(detailsToggles).forEach(function(elt) {
36
+ elt.addEventListener('click', function(evt) {
37
+ var detailsContent = evt.currentTarget.getElementsByClassName(detailsContentClass)[0];
38
+ js.eltDisplayToggle(detailsContent, 'block');
39
+ })
40
+ });
41
+
42
+ js.convertArrayLike(detailsRegions).forEach(js.eltHide);
43
+ };
44
+
45
+
46
+ var initVisibilityButtons = function initVisibilityButtons() {
47
+ var showButtons = document.getElementsByClassName(showClass);
48
+ var hideButtons = document.getElementsByClassName(hideClass);
49
+ var detailsRegions = document.getElementsByClassName(detailsContentClass);
50
+ var initiallyHidden = js.convertArrayLike(detailsRegions).concat(js.convertArrayLike(hideButtons));
51
+
52
+ // Click events for show buttons show content, show a hide button and hide themselves.
53
+ js.convertArrayLike(showButtons).forEach(function(elt) {
54
+ elt.addEventListener('click', function(evt) {
55
+ var hideSibling = js.findSiblingByClassName(evt.currentTarget, hideClass);
56
+ var details = js.findSiblingByClassName(evt.currentTarget, detailsContentClass);
57
+ js.eltShowBlock(details);
58
+ js.eltShowILBlock(hideSibling);
59
+ js.eltHide(evt.currentTarget);
60
+ });
61
+ });
62
+
63
+ // Click events for hide buttons hide content, show a show button and hide themselves.
64
+ js.convertArrayLike(hideButtons).forEach(function(elt) {
65
+ elt.addEventListener('click', function(evt) {
66
+ var showSibling = js.findSiblingByClassName(evt.currentTarget, showClass);
67
+ var details = js.findSiblingByClassName(evt.currentTarget, detailsContentClass);
68
+ js.eltHide(details);
69
+ js.eltShowBlock(showSibling);
70
+ js.eltHide(evt.currentTarget);
71
+ });
72
+ });
73
+
74
+ initiallyHidden.forEach(js.eltHide);
75
+ };
76
+
77
+
78
+ return {
79
+ 'init': init
80
+ };
81
+
82
+ })(adalog.vanillaJS);