memorable 0.1.1

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: 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: []