memorable 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/lib/memorable.rb +22 -0
- data/lib/memorable/configuration.rb +47 -0
- data/lib/memorable/controller.rb +111 -0
- data/lib/memorable/error.rb +7 -0
- data/lib/memorable/model.rb +35 -0
- data/lib/memorable/template_engine.rb +99 -0
- data/lib/memorable/templates/en_US.yml +9 -0
- data/lib/memorable/version.rb +3 -0
- data/memorable.gemspec +27 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d7fb95cbd8bfc993b815802ff41abe3abf913f4f
|
4
|
+
data.tar.gz: 52fe4c06f792b75c26a461057ceb477b206ee30d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 746e7e131f7f1cd71c11e53a19ae00bc7e26eeb467af2026a59c400554555129d06c7a1fe765c5249264d2c688299d825eae0226e69ec809d02b52fac878f61e
|
7
|
+
data.tar.gz: 3a728d5a5021dee59581388cbd8493c554e95db98b86ce73c68906d1f222b3dc21b884f7f6d463537c71194f6ac66ffabd84d0e61ca8679487899190e5418267
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Xiaoguang Chen
|
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/lib/memorable.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
require 'memorable/version'
|
4
|
+
require 'memorable/error'
|
5
|
+
require 'memorable/configuration'
|
6
|
+
require 'memorable/template_engine'
|
7
|
+
require 'memorable/model'
|
8
|
+
require 'memorable/controller'
|
9
|
+
|
10
|
+
module Memorable
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def setup(&block)
|
14
|
+
Configuration.class_eval(&block) if block_given?
|
15
|
+
|
16
|
+
Configuration.journals_model.send :include, Memorable::Model
|
17
|
+
ActionController::Base.send :include, Memorable::Controller
|
18
|
+
|
19
|
+
TemplateEngine.load!
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Memorable
|
2
|
+
class Configuration
|
3
|
+
@register_actions = {}
|
4
|
+
@journals_model = nil
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_reader :register_actions, :journals_model
|
8
|
+
|
9
|
+
def journals_model=(model)
|
10
|
+
model_klass = Object.const_get model.capitalize
|
11
|
+
@journals_model = model_klass
|
12
|
+
end
|
13
|
+
|
14
|
+
def register(action_names, controller_name, options, condition_proc)
|
15
|
+
register_actions[controller_name] ||= {}
|
16
|
+
register_actions[controller_name][:actions] ||= []
|
17
|
+
register_actions[controller_name][:actions].concat([action_names].flatten)
|
18
|
+
register_actions[controller_name][:options] ||= {}
|
19
|
+
register_actions[controller_name][:options].merge! options
|
20
|
+
register_actions[controller_name][:condition_proc] ||= condition_proc
|
21
|
+
end
|
22
|
+
|
23
|
+
def registered?(key, name)
|
24
|
+
register_actions[key] && register_actions[key][:actions].include?(name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def condition_matched?(controller)
|
28
|
+
condition = condition_proc(controller.controller_name)
|
29
|
+
return true unless condition
|
30
|
+
return controller.instance_eval(&condition)
|
31
|
+
end
|
32
|
+
|
33
|
+
def controller_options(key)
|
34
|
+
register_actions[key][:options] rescue nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def condition_proc(key)
|
38
|
+
register_actions[key][:condition_proc] rescue nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# Controller Options Helpers
|
42
|
+
def resource_name(key)
|
43
|
+
register_actions[key][:options][:resource_name] rescue nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Memorable
|
2
|
+
module Controller
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
append_after_filter :memorize_callback
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def memorize_callback
|
12
|
+
return unless memorable?
|
13
|
+
|
14
|
+
# prepare locals for action
|
15
|
+
locals = extract_memorable_locals
|
16
|
+
|
17
|
+
# write to database
|
18
|
+
Configuration.journals_model.create_with_options!(locals)
|
19
|
+
|
20
|
+
rescue Exception => e
|
21
|
+
raise e if Rails.env.development? # for debug
|
22
|
+
Rails.logger.error e.message
|
23
|
+
end
|
24
|
+
|
25
|
+
def memorable?
|
26
|
+
Configuration.registered?(controller_name, action_name) &&
|
27
|
+
Configuration.condition_matched?(self) &&
|
28
|
+
response.successful?
|
29
|
+
end
|
30
|
+
|
31
|
+
def extract_memorable_locals
|
32
|
+
locals = ActiveSupport::HashWithIndifferentAccess.new ({
|
33
|
+
controller: controller_name,
|
34
|
+
action: action_name,
|
35
|
+
user_id: current_user.id
|
36
|
+
})
|
37
|
+
|
38
|
+
if memorable_resource
|
39
|
+
locals.merge! Hash[memorable_resource.previous_changes.map {|key, value| ["previous_#{key}", value[0]]}]
|
40
|
+
locals.merge! memorable_resource.attributes
|
41
|
+
locals.merge!({
|
42
|
+
resource_id: memorable_resource.id,
|
43
|
+
resource_type: memorable_resource.class.to_s
|
44
|
+
})
|
45
|
+
end
|
46
|
+
|
47
|
+
# # Journals Model is free to override the behavior of this method
|
48
|
+
# if journal.respond_to? :set_attributes_for_event
|
49
|
+
# journal.set_attributes_for_event(controller)
|
50
|
+
# end
|
51
|
+
|
52
|
+
custom_method_name = "memorable_#{action_name}"
|
53
|
+
|
54
|
+
if respond_to? custom_method_name
|
55
|
+
custom_locals = self.send custom_method_name
|
56
|
+
locals.merge!(custom_locals)
|
57
|
+
end
|
58
|
+
|
59
|
+
locals
|
60
|
+
end
|
61
|
+
|
62
|
+
def memorable_resource
|
63
|
+
@memorable_resource ||= self.instance_variable_get("@#{memorable_resource_name}") || self.send(:resource) rescue nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def memorable_resource_name
|
67
|
+
Configuration.resource_name(controller_name) || controller_name.singularize
|
68
|
+
end
|
69
|
+
|
70
|
+
module ClassMethods
|
71
|
+
def memorize(options = {}, &block)
|
72
|
+
raise InvalidOptionsError, "if and unless cannot appear at the sametime" if options[:if] && options[:unless]
|
73
|
+
|
74
|
+
if condition = (options[:if] || options[:unless])
|
75
|
+
if_condition = !!options[:if]
|
76
|
+
if condition.is_a?(Symbol)
|
77
|
+
condition_proc = proc { |c| if_condition ? c.send(condition) : !c.send(condition) }
|
78
|
+
elsif condition.is_a? Proc
|
79
|
+
condition_proc = proc { |c| if_condition ? condition.call(c) : !condition.call(c) }
|
80
|
+
else
|
81
|
+
raise InvalidOptionsError, "#{condition} is not a valid Proc or controller method."
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
raise InvalidOptionsError, "except and only cannot appear at the sametime" if options[:except] && options[:only]
|
86
|
+
|
87
|
+
specified_actions = [options[:except] || options[:only]].flatten.compact.map(&:to_s)
|
88
|
+
actions =
|
89
|
+
if options.delete(:only)
|
90
|
+
specified_actions
|
91
|
+
elsif options.delete(:except)
|
92
|
+
all_actions - specified_actions
|
93
|
+
else
|
94
|
+
all_actions
|
95
|
+
end
|
96
|
+
|
97
|
+
memorize_actions actions, options, condition_proc
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def memorize_actions(action_names, options, condition_proc)
|
103
|
+
Configuration.register action_names, controller_name, options, condition_proc
|
104
|
+
end
|
105
|
+
|
106
|
+
def all_actions
|
107
|
+
@all_actions ||= action_methods.map(&:to_s)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Memorable
|
2
|
+
|
3
|
+
module Model
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def write_content(options)
|
7
|
+
current_locale = I18n.locale
|
8
|
+
|
9
|
+
TemplateEngine.assemble(options).each do |template|
|
10
|
+
I18n.locale = template[0]
|
11
|
+
content = template[1]
|
12
|
+
end
|
13
|
+
|
14
|
+
I18n.locale = current_locale
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def create_with_options!(options={})
|
19
|
+
journal = self.build_with_options(options)
|
20
|
+
journal.save!
|
21
|
+
end
|
22
|
+
|
23
|
+
def build_with_options(options)
|
24
|
+
journal = self.new
|
25
|
+
|
26
|
+
journal.user_id = options.delete :user_id
|
27
|
+
journal.resource_id = options.delete :resource_id
|
28
|
+
journal.resource_type = options.delete :resource_type
|
29
|
+
|
30
|
+
journal.write_content(options)
|
31
|
+
journal
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Memorable
|
5
|
+
class TemplateEngine
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
attr_reader :load_path, :templates
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@load_path = []
|
12
|
+
@templates = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def store_templates
|
16
|
+
load_path.each do |filename|
|
17
|
+
locale = File.basename(filename, ".yml")
|
18
|
+
data = load_yml filename
|
19
|
+
templates[locale.to_sym] = data
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Loads a YAML template file. The data must have locales as
|
24
|
+
# toplevel keys.
|
25
|
+
def load_yml(filename)
|
26
|
+
begin
|
27
|
+
YAML.load_file(filename)
|
28
|
+
rescue TypeError, ScriptError, StandardError => e
|
29
|
+
raise InvalidYAMLData.new(filename, e.inspect)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def assemble(options)
|
34
|
+
action_templates(options[:controller], options[:action], options[:template_key]).map do |locale, template|
|
35
|
+
content = render_template(template, options)
|
36
|
+
[locale, content]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def render_template(template, locals)
|
43
|
+
if locals
|
44
|
+
I18n.interpolate(template, locals)
|
45
|
+
else
|
46
|
+
template
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def action_templates(controller, action, sub_key)
|
51
|
+
sub_key ||= 'base'
|
52
|
+
raw_templates = catch(:template_found) do
|
53
|
+
parse_entry(controller, action, sub_key)
|
54
|
+
parse_entry(controller, 'other', sub_key)
|
55
|
+
parse_entry('defaults', action, sub_key)
|
56
|
+
parse_entry('defaults', 'other', sub_key)
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
raise TemplateNotFound, "Template: #{controller} #{action} #{sub_key} not found" unless raw_templates
|
60
|
+
raw_templates
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_entry(controller, action, sub_key)
|
64
|
+
raw_templates = templates.map do |locale, template_data|
|
65
|
+
template = template_data[controller][action][sub_key] rescue nil
|
66
|
+
template ? [locale, template] : nil
|
67
|
+
end.compact
|
68
|
+
throw :template_found, raw_templates unless raw_templates.blank?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Class Methods
|
72
|
+
# ----------------------
|
73
|
+
#
|
74
|
+
def self.load!
|
75
|
+
pattern = self.pattern_from I18n.available_locales
|
76
|
+
|
77
|
+
add("templates/#{pattern}.yml")
|
78
|
+
add("app/views/memorable/#{pattern}.yml", Rails.root) if defined?(Rails)
|
79
|
+
|
80
|
+
instance.store_templates
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.assemble(*args)
|
84
|
+
instance.assemble(*args)
|
85
|
+
end
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
89
|
+
def self.add(pattern, base_dir=File.dirname(__FILE__))
|
90
|
+
files = Dir[File.join(base_dir, pattern)]
|
91
|
+
instance.load_path.concat(files)
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.pattern_from(args)
|
95
|
+
array = Array(args || [])
|
96
|
+
array.blank? ? '*' : "{#{array.join ','}}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/memorable.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 'memorable/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "memorable"
|
8
|
+
spec.version = Memorable::VERSION
|
9
|
+
spec.authors = ["Cxg"]
|
10
|
+
spec.email = ["xg.chen87@gmail.com"]
|
11
|
+
spec.summary = "A Rails logging system based on actions."
|
12
|
+
spec.description = %q{A Rails logging system based on actions, not model
|
13
|
+
callbacks. Customizable ready-to-run configurations
|
14
|
+
and built-in I18n support.}
|
15
|
+
spec.homepage = "https://github.com/serco-chen/memorable"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0") - %w[.gitignore]
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_dependency 'activesupport', '>= 3.2.8', "< 5.0"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: memorable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cxg
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.2.8
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.8
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5.0'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: bundler
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.7'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.7'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '10.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '10.0'
|
61
|
+
description: |-
|
62
|
+
A Rails logging system based on actions, not model
|
63
|
+
callbacks. Customizable ready-to-run configurations
|
64
|
+
and built-in I18n support.
|
65
|
+
email:
|
66
|
+
- xg.chen87@gmail.com
|
67
|
+
executables: []
|
68
|
+
extensions: []
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- Gemfile
|
72
|
+
- LICENSE
|
73
|
+
- lib/memorable.rb
|
74
|
+
- lib/memorable/configuration.rb
|
75
|
+
- lib/memorable/controller.rb
|
76
|
+
- lib/memorable/error.rb
|
77
|
+
- lib/memorable/model.rb
|
78
|
+
- lib/memorable/template_engine.rb
|
79
|
+
- lib/memorable/templates/en_US.yml
|
80
|
+
- lib/memorable/version.rb
|
81
|
+
- memorable.gemspec
|
82
|
+
homepage: https://github.com/serco-chen/memorable
|
83
|
+
licenses:
|
84
|
+
- MIT
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 2.4.2
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: A Rails logging system based on actions.
|
106
|
+
test_files: []
|