memorable 0.1.1

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.
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in easy_ping.gemspec
4
+ gemspec
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,7 @@
1
+ module Memorable
2
+ class Error < StandardError; end
3
+ class InvalidOptionsError < Error; end
4
+ class InvalidYAMLData < Error; end
5
+ class InvalidLocals < Error; end
6
+ class TemplateNotFound < Error; end
7
+ 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
@@ -0,0 +1,9 @@
1
+ defaults:
2
+ create:
3
+ base: "%{resource_type} created."
4
+ update:
5
+ base: "%{resource_type} updated."
6
+ destroy:
7
+ base: "%{resource_type} deleted."
8
+ others:
9
+ base: "%{action} was executed on %{resource_type}."
@@ -0,0 +1,3 @@
1
+ module Memorable
2
+ VERSION = "0.1.1"
3
+ 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: []