git-commit-notifier 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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +22 -0
- data/README.textile +54 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/bin/git-commit-notifier +14 -0
- data/config/git-notifier-configl.yml.sample +15 -0
- data/git-commit-notifier.gemspec +79 -0
- data/lib/commit_hook.rb +53 -0
- data/lib/diff_to_html.rb +321 -0
- data/lib/emailer.rb +102 -0
- data/lib/git.rb +35 -0
- data/lib/result_processor.rb +122 -0
- data/template/email.html.erb +9 -0
- data/template/styles.css +10 -0
- data/test/fixtures/git_log +34 -0
- data/test/fixtures/git_show_51b986619d88f7ba98be7d271188785cbbb541a0 +83 -0
- data/test/fixtures/git_show_a4629e707d80a5769f7a71ca6ed9471015e14dc9 +49 -0
- data/test/fixtures/git_show_dce6ade4cdc2833b53bd600ef10f9bce83c7102d +83 -0
- data/test/fixtures/git_show_e28ad77bba0574241e6eb64dfd0c1291b221effe +76 -0
- data/test/test_helper.rb +21 -0
- data/test/unit/test_commit_hook.rb +22 -0
- data/test/unit/test_diff_to_html.rb +97 -0
- data/test/unit/test_result_processor.rb +95 -0
- metadata +125 -0
data/lib/emailer.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
class Emailer
|
5
|
+
|
6
|
+
def initialize(config, project_path, recipient, from_address, from_alias, subject, text_message, html_diff, old_rev, new_rev, ref_name)
|
7
|
+
@config = config
|
8
|
+
@config ||= {}
|
9
|
+
@project_path = project_path
|
10
|
+
@recipient = recipient
|
11
|
+
@from_address = from_address
|
12
|
+
@from_alias = from_alias
|
13
|
+
@subject = subject
|
14
|
+
@text_message = text_message
|
15
|
+
@ref_name = ref_name
|
16
|
+
@old_rev = old_rev
|
17
|
+
@new_rev = new_rev
|
18
|
+
|
19
|
+
template = File.join(File.dirname(__FILE__), '/../template/email.html.erb')
|
20
|
+
@html_message = ERB.new(File.read(template)).result(binding)
|
21
|
+
end
|
22
|
+
|
23
|
+
def boundary
|
24
|
+
return @boundary if @boundary
|
25
|
+
srand
|
26
|
+
seed = "#{rand(10000)}#{Time.now}"
|
27
|
+
@boundary = Digest::SHA1.hexdigest(seed)
|
28
|
+
end
|
29
|
+
|
30
|
+
def stylesheet_string
|
31
|
+
stylesheet = File.join(File.dirname(__FILE__), '/../template/styles.css')
|
32
|
+
File.read(stylesheet)
|
33
|
+
end
|
34
|
+
|
35
|
+
def perform_delivery_smtp(content, smtp_settings)
|
36
|
+
settings = { }
|
37
|
+
%w(address port domain user_name password authentication enable_tls).each do |key|
|
38
|
+
val = smtp_settings[key].to_s.empty? ? nil : smtp_settings[key]
|
39
|
+
settings.merge!({ key => val})
|
40
|
+
end
|
41
|
+
|
42
|
+
Net::SMTP.start(settings['address'], settings['port'], settings['domain'],
|
43
|
+
settings['user_name'], settings['password'], settings['authentication']) do |smtp|
|
44
|
+
|
45
|
+
smtp.enable_tls if settings['enable_tls']
|
46
|
+
|
47
|
+
smtp.open_message_stream(@from_address, [@recipient]) do |f|
|
48
|
+
content.each do |line|
|
49
|
+
f.puts line
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def perform_delivery_sendmail(content, options = nil)
|
56
|
+
sendmail_settings = {
|
57
|
+
'location' => "/usr/sbin/sendmail",
|
58
|
+
'arguments' => "-i -t"
|
59
|
+
}.merge(options || {})
|
60
|
+
command = "#{sendmail_settings['location']} #{sendmail_settings['arguments']}"
|
61
|
+
IO.popen(command, "w+") do |f|
|
62
|
+
f.write(content.join("\n"))
|
63
|
+
f.flush
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def send
|
68
|
+
from = @from_alias.empty? ? @from_address : "#{@from_alias} <#{@from_address}>"
|
69
|
+
content = ["From: #{from}",
|
70
|
+
"Reply-To: #{from}",
|
71
|
+
"To: #{@recipient}",
|
72
|
+
"Subject: #{@subject}",
|
73
|
+
"X-Git-Refname: #{@ref_name}",
|
74
|
+
"X-Git-Oldrev: #{@old_rev}",
|
75
|
+
"X-Git-Newrev: #{@new_rev}",
|
76
|
+
"Mime-Version: 1.0",
|
77
|
+
"Content-Type: multipart/alternative; boundary=#{boundary}\n\n\n",
|
78
|
+
"--#{boundary}",
|
79
|
+
"Content-Type: text/plain; charset=utf-8",
|
80
|
+
"Content-Transfer-Encoding: 8bit",
|
81
|
+
"Content-Disposition: inline\n\n\n",
|
82
|
+
@text_message,
|
83
|
+
"--#{boundary}",
|
84
|
+
"Content-Type: text/html; charset=utf-8",
|
85
|
+
"Content-Transfer-Encoding: 8bit",
|
86
|
+
"Content-Disposition: inline\n\n\n",
|
87
|
+
@html_message,
|
88
|
+
"--#{boundary}--"]
|
89
|
+
|
90
|
+
if @recipient.empty?
|
91
|
+
puts content.join("\n")
|
92
|
+
return
|
93
|
+
end
|
94
|
+
|
95
|
+
if @config['delivery_method'] == 'smtp'
|
96
|
+
perform_delivery_smtp(content, @config['smtp_server'])
|
97
|
+
else
|
98
|
+
perform_delivery_sendmail(content, @config['sendmail_options'])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
data/lib/git.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class Git
|
2
|
+
def self.show(rev)
|
3
|
+
`git show #{rev.strip} -w`
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.log(rev1, rev2)
|
7
|
+
`git log #{rev1}..#{rev2}`.strip
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.branch_commits(treeish)
|
11
|
+
args = Git.branch_heads - [Git.branch_head(treeish)]
|
12
|
+
args.map! {|tree| "^#{tree}"}
|
13
|
+
args << treeish
|
14
|
+
`git rev-list #{args.join(' ')}`.to_a.map{|commit| commit.chomp}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.branch_heads
|
18
|
+
`git rev-parse --branches`.to_a.map{|head| head.chomp}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.branch_head(treeish)
|
22
|
+
`git rev-parse #{treeish}`.strip
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.repo_name
|
26
|
+
git_prefix = `git config hooks.emailprefix`.strip
|
27
|
+
return git_prefix unless git_prefix.empty?
|
28
|
+
dir_name = `pwd`.chomp.split("/").last.gsub(/\.git$/, '')
|
29
|
+
return "#{dir_name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.mailing_list_address
|
33
|
+
`git config hooks.mailinglist`.strip
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
class ResultProcessor
|
4
|
+
# input (loaded in @diff) is an array having Hash elements:
|
5
|
+
# { :action => action, :token => string }
|
6
|
+
# action can be :discard_a, :discard_b or :match
|
7
|
+
|
8
|
+
# output: two formatted html strings, one for the removals and one for the additions
|
9
|
+
|
10
|
+
def results
|
11
|
+
close_tags # close last tag
|
12
|
+
[array_of_lines(@result[:removal]), array_of_lines(@result[:addition])]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def initialize(diff)
|
18
|
+
@diff = diff
|
19
|
+
init
|
20
|
+
filter_replaced_lines
|
21
|
+
process
|
22
|
+
end
|
23
|
+
|
24
|
+
def init
|
25
|
+
@result = { :addition => [], :removal => [] }
|
26
|
+
@tag_open = { :addition => false, :removal => false}
|
27
|
+
end
|
28
|
+
|
29
|
+
def process
|
30
|
+
@highlight = !@diff.select { |d| d[:action] == :match}.empty? # highlight only if block contains both matches and differences
|
31
|
+
@diff.each do |diff|
|
32
|
+
case diff[:action]
|
33
|
+
when :match
|
34
|
+
match(diff)
|
35
|
+
when :discard_a
|
36
|
+
discard_a(diff)
|
37
|
+
when :discard_b
|
38
|
+
discard_b(diff)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def discard_match(position, token)
|
44
|
+
# replace it with 2 changes
|
45
|
+
@diff[position][:action] = :discard_a
|
46
|
+
@diff.insert(position, { :action => :discard_b, :token => token} )
|
47
|
+
end
|
48
|
+
|
49
|
+
def length_in_chars(diff)
|
50
|
+
diff.inject(0) { |length, s| length + s[:token].size}
|
51
|
+
end
|
52
|
+
|
53
|
+
def filter_replaced_lines
|
54
|
+
# if a block is replaced by an other one, lcs-diff will find even the single common word between the old and the new content
|
55
|
+
# no need for intelligent diff in this case, simply show the removed and the added block with no highlighting
|
56
|
+
# rule: if less than 33% of a block is not a match, we don't need intelligent diff for that block
|
57
|
+
match_length = length_in_chars(@diff.select { |d| d[:action] == :match})
|
58
|
+
total_length = length_in_chars(@diff)
|
59
|
+
|
60
|
+
if total_length.to_f / match_length > 3.3
|
61
|
+
@diff.each_with_index do |d, i|
|
62
|
+
next if d[:action] != :match
|
63
|
+
discard_match(i, d[:token])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def match(diff)
|
69
|
+
close_tags
|
70
|
+
all_actions do |action|
|
71
|
+
close_last_tag(diff)
|
72
|
+
@result[action] << escape_content(diff[:token])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def discard_a(diff)
|
77
|
+
open_tag(:removal, diff[:token])
|
78
|
+
close_last_tag(diff)
|
79
|
+
@result[:removal] << escape_content(diff[:token])
|
80
|
+
end
|
81
|
+
|
82
|
+
def discard_b(diff)
|
83
|
+
open_tag(:addition, diff[:token])
|
84
|
+
close_last_tag(diff)
|
85
|
+
@result[:addition] << escape_content(diff[:token])
|
86
|
+
end
|
87
|
+
|
88
|
+
def all_actions
|
89
|
+
[:addition, :removal].each do |action|
|
90
|
+
yield(action)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def open_tag(action, next_token)
|
95
|
+
return if !@highlight || next_token.strip.empty? # don't open span tag if no highlighting is needed or the first token is empty
|
96
|
+
unless @tag_open[action]
|
97
|
+
klass = action == :addition ? 'aa' : 'rr'
|
98
|
+
@result[action] << "<span class=\"#{klass}\">"
|
99
|
+
@tag_open[action] = true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def close_tags
|
104
|
+
return unless @highlight
|
105
|
+
all_actions do |action|
|
106
|
+
if @tag_open[action]
|
107
|
+
@result[action] << "</span>"
|
108
|
+
@tag_open[action] = false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def close_last_tag(diff)
|
114
|
+
return unless @highlight
|
115
|
+
close_tags if diff[:token] == "\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
def array_of_lines(tokens)
|
119
|
+
tokens.join('').split("\n")
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
data/template/styles.css
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
* {font-size:11px;}
|
2
|
+
h2 {font-family:Verdana;font-size:12px;background-color: #bbb; font-weight: bold; line-height:25px; margin-bottom:2px; padding-left:5px}
|
3
|
+
table {width:100%;border-collapse:collapse}
|
4
|
+
.r {background-color: #fdd;}
|
5
|
+
.rr {background-color: #faa;}
|
6
|
+
.a {background-color: #dfd;}
|
7
|
+
.aa {background-color: #afa;}
|
8
|
+
.title {background-color: #ddd; padding: 10px;font-family:Verdana;font-size:12px;}
|
9
|
+
tr {color:#000;font-family: "Bitstream Vera Sans Mono","Monaco","Courier",monospace}
|
10
|
+
.ln {background-color: #ccc; width:23px; text-align:right}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
commit e28ad77bba0574241e6eb64dfd0c1291b221effe
|
2
|
+
Author: Tom Stuart <tom@experthuman.com>
|
3
|
+
Date: Wed Oct 8 09:31:00 2008 +0100
|
4
|
+
|
5
|
+
Allow use of :path_prefix and :name_prefix outside of namespaced routes. [#1188 state:resolved]
|
6
|
+
|
7
|
+
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
|
8
|
+
|
9
|
+
commit a4629e707d80a5769f7a71ca6ed9471015e14dc9
|
10
|
+
Author: Michael Koziarski <michael@koziarski.com>
|
11
|
+
Date: Mon Sep 29 17:36:09 2008 +0200
|
12
|
+
|
13
|
+
Extract transliteration code to a seperate method.
|
14
|
+
|
15
|
+
Use iconv by default, but only when the transliteration is well behaved. When it isn't, fallback to mb_chars
|
16
|
+
|
17
|
+
commit dce6ade4cdc2833b53bd600ef10f9bce83c7102d
|
18
|
+
Author: Andrew Kaspick <andrew@redlinesoftware.com>
|
19
|
+
Date: Tue Sep 30 14:15:36 2008 -0500
|
20
|
+
|
21
|
+
Ensure select_tag#name attribute uses [] when :multiple is true. [#1146 state:resolved]
|
22
|
+
|
23
|
+
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
|
24
|
+
|
25
|
+
commit 51b986619d88f7ba98be7d271188785cbbb541a0
|
26
|
+
Author: Tarmo Tänav <tarmo@itech.ee>
|
27
|
+
Date: Mon Oct 6 17:35:21 2008 +0300
|
28
|
+
|
29
|
+
Implement submit_to_remote as a wrapper around a more generic button_to_remote
|
30
|
+
|
31
|
+
Removed the "return false" from submit_to_remote onclick end as
|
32
|
+
button input elements have no default behavior to cancel.
|
33
|
+
|
34
|
+
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
|
@@ -0,0 +1,83 @@
|
|
1
|
+
commit 51b986619d88f7ba98be7d271188785cbbb541a0
|
2
|
+
Author: Tarmo Tänav <tarmo@itech.ee>
|
3
|
+
Date: Mon Oct 6 17:35:21 2008 +0300
|
4
|
+
|
5
|
+
Implement submit_to_remote as a wrapper around a more generic button_to_remote
|
6
|
+
|
7
|
+
Removed the "return false" from submit_to_remote onclick end as
|
8
|
+
button input elements have no default behavior to cancel.
|
9
|
+
|
10
|
+
Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
|
11
|
+
|
12
|
+
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
|
13
|
+
index 54ea93f..4de93a1 100644
|
14
|
+
--- a/actionpack/CHANGELOG
|
15
|
+
+++ b/actionpack/CHANGELOG
|
16
|
+
@@ -1,5 +1,7 @@
|
17
|
+
*Edge*
|
18
|
+
|
19
|
+
+* Make PrototypeHelper#submit_to_remote a wrapper around PrototypeHelper#button_to_remote. [Tarmo Tänav]
|
20
|
+
+
|
21
|
+
* Set HttpOnly for the cookie session store's cookie. #1046
|
22
|
+
|
23
|
+
* Added FormTagHelper#image_submit_tag confirm option #784 [Alastair Brunton]
|
24
|
+
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
|
25
|
+
index ff41a6d..a3eccc7 100644
|
26
|
+
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
|
27
|
+
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
|
28
|
+
@@ -405,7 +405,7 @@ module ActionView
|
29
|
+
# # Generates: <input name="create_btn" onclick="new Ajax.Request('/testing/create',
|
30
|
+
# # {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
|
31
|
+
# # return false;" type="button" value="Create" />
|
32
|
+
- # <%= button_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
|
33
|
+
+ # <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>
|
34
|
+
#
|
35
|
+
# # Submit to the remote action update and update the DIV succeed or fail based
|
36
|
+
# # on the success or failure of the request
|
37
|
+
@@ -413,24 +413,18 @@ module ActionView
|
38
|
+
# # Generates: <input name="update_btn" onclick="new Ajax.Updater({success:'succeed',failure:'fail'},
|
39
|
+
# # '/testing/update', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});
|
40
|
+
# # return false;" type="button" value="Update" />
|
41
|
+
- # <%= button_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
|
42
|
+
+ # <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
|
43
|
+
# :update => { :success => "succeed", :failure => "fail" }
|
44
|
+
#
|
45
|
+
# <tt>options</tt> argument is the same as in form_remote_tag.
|
46
|
+
- #
|
47
|
+
- # Note: This method used to be called submit_to_remote, but that's now just an alias for button_to_remote
|
48
|
+
- def button_to_remote(name, value, options = {})
|
49
|
+
+ def submit_to_remote(name, value, options = {})
|
50
|
+
options[:with] ||= 'Form.serialize(this.form)'
|
51
|
+
|
52
|
+
- options[:html] ||= {}
|
53
|
+
- options[:html][:type] = 'button'
|
54
|
+
- options[:html][:onclick] = "#{remote_function(options)}; return false;"
|
55
|
+
- options[:html][:name] = name
|
56
|
+
- options[:html][:value] = value
|
57
|
+
+ html_options = options.delete(:html) || {}
|
58
|
+
+ html_options[:name] = name
|
59
|
+
|
60
|
+
- tag("input", options[:html], false)
|
61
|
+
+ button_to_remote(value, options, html_options)
|
62
|
+
end
|
63
|
+
- alias_method :submit_to_remote, :button_to_remote
|
64
|
+
|
65
|
+
# Returns '<tt>eval(request.responseText)</tt>' which is the JavaScript function
|
66
|
+
# that +form_remote_tag+ can call in <tt>:complete</tt> to evaluate a multiple
|
67
|
+
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
|
68
|
+
index a1f541f..d6b86a3 100644
|
69
|
+
--- a/actionpack/test/template/prototype_helper_test.rb
|
70
|
+
+++ b/actionpack/test/template/prototype_helper_test.rb
|
71
|
+
@@ -218,9 +218,9 @@ class PrototypeHelperTest < PrototypeHelperBaseTest
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
- def test_button_to_remote
|
76
|
+
- assert_dom_equal %(<input name=\"More beer!\" onclick=\"new Ajax.Updater('empty_bottle', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)}); return false;\" type=\"button\" value=\"1000000\" />),
|
77
|
+
- button_to_remote("More beer!", 1_000_000, :update => "empty_bottle")
|
78
|
+
+ def test_submit_to_remote
|
79
|
+
+ assert_dom_equal %(<input name=\"More beer!\" onclick=\"new Ajax.Updater('empty_bottle', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});\" type=\"button\" value=\"1000000\" />),
|
80
|
+
+ submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle")
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_observe_field
|
@@ -0,0 +1,49 @@
|
|
1
|
+
commit a4629e707d80a5769f7a71ca6ed9471015e14dc9
|
2
|
+
Author: Michael Koziarski <michael@koziarski.com>
|
3
|
+
Date: Mon Sep 29 17:36:09 2008 +0200
|
4
|
+
|
5
|
+
Extract transliteration code to a seperate method.
|
6
|
+
|
7
|
+
Use iconv by default, but only when the transliteration is well behaved. When it isn't, fallback to mb_chars
|
8
|
+
|
9
|
+
diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb
|
10
|
+
index 89a93f4..f1e7abf 100644
|
11
|
+
--- a/activesupport/lib/active_support/inflector.rb
|
12
|
+
+++ b/activesupport/lib/active_support/inflector.rb
|
13
|
+
@@ -1,4 +1,5 @@
|
14
|
+
require 'singleton'
|
15
|
+
+require 'iconv'
|
16
|
+
|
17
|
+
module ActiveSupport
|
18
|
+
# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
|
19
|
+
@@ -258,14 +259,28 @@ module ActiveSupport
|
20
|
+
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
|
21
|
+
def parameterize(string, sep = '-')
|
22
|
+
re_sep = Regexp.escape(sep)
|
23
|
+
- string.mb_chars.normalize(:kd). # Decompose accented characters
|
24
|
+
- gsub(/[^\x00-\x7F]+/, ''). # Remove anything non-ASCII entirely (e.g. diacritics).
|
25
|
+
+ transliterate(string).
|
26
|
+
gsub(/[^a-z0-9\-_\+]+/i, sep). # Turn unwanted chars into the separator.
|
27
|
+
squeeze(sep). # No more than one of the separator in a row.
|
28
|
+
gsub(/^#{re_sep}|#{re_sep}$/i, ''). # Remove leading/trailing separator.
|
29
|
+
downcase
|
30
|
+
end
|
31
|
+
|
32
|
+
+
|
33
|
+
+ # Replaces accented characters with their ascii equivalents.
|
34
|
+
+ def transliterate(string)
|
35
|
+
+ Iconv.iconv('ascii//translit//IGNORE', 'utf-8', string).to_s
|
36
|
+
+ end
|
37
|
+
+
|
38
|
+
+ # The iconv transliteration code doesn't function correctly
|
39
|
+
+ # on some platforms, but it's very fast where it does function.
|
40
|
+
+ if "foo" != Inflector.transliterate("föö")
|
41
|
+
+ def transliterate(string)
|
42
|
+
+ string.mb_chars.normalize(:kd). # Decompose accented characters
|
43
|
+
+ gsub(/[^\x00-\x7F]+/, '') # Remove anything non-ASCII entirely (e.g. diacritics).
|
44
|
+
+ end
|
45
|
+
+ end
|
46
|
+
+
|
47
|
+
# Create the name of a table like Rails does for models to table names. This method
|
48
|
+
# uses the +pluralize+ method on the last word in the string.
|
49
|
+
#
|