string-direction 0.0.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/README.md +76 -19
- data/Rakefile +1 -0
- data/bin/console +11 -0
- data/bin/setup +7 -0
- data/lib/string-direction.rb +38 -2
- data/lib/string-direction/configuration.rb +20 -0
- data/lib/string-direction/detector.rb +72 -0
- data/lib/string-direction/strategies/characters_strategy.rb +40 -0
- data/lib/string-direction/strategies/marks_strategy.rb +35 -0
- data/lib/string-direction/strategy.rb +25 -0
- data/lib/string-direction/string_methods.rb +31 -0
- data/lib/string-direction/version.rb +4 -0
- data/spec/spec_helper.rb +6 -3
- data/spec/string-direction/configuration_spec.rb +15 -0
- data/spec/string-direction/detector_spec.rb +146 -0
- data/spec/string-direction/strategies/characters_strategy_spec.rb +107 -0
- data/spec/string-direction/strategies/marks_strategy_spec.rb +55 -0
- data/spec/string-direction/strategy_spec.rb +5 -0
- data/spec/string-direction/string_methods_spec.rb +33 -0
- data/spec/string-direction_spec.rb +49 -0
- data/string-direction.gemspec +24 -0
- metadata +70 -12
- data/lib/string-direction/string-direction.rb +0 -102
- data/spec/string/string_spec.rb +0 -96
- data/string-direction.spec +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ef6e54736486fccff8d5e5c004d73c27a5ca7b4
|
4
|
+
data.tar.gz: d16b4d813ef25aa3cbd032af62a2861cf5fe0fe6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72c1db5ba8b6d1f3cfffe46315a8956a34d41f957a4ebad2e09daad798dcf590c90a06947115caa696e9c32f2eaaf61114e101d621ed5d8a27b53267ad3f50c0
|
7
|
+
data.tar.gz: 4424cab8cab4d1b9e016169867a1cf0c8b050ce6f498727c234a89414ebbad36b24a704abf6c84c15ef55b251064379918c42f0ffbd436b006dd08fa0f7d1bb9
|
data/.gitignore
CHANGED
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -1,32 +1,53 @@
|
|
1
1
|
# string-direction
|
2
2
|
|
3
|
-
`string-direction` is a ruby
|
3
|
+
`string-direction` is a ruby library for automatic detection of the direction (left-to-right, right-to-left or bi-directional) in which a text should be displayed.
|
4
4
|
|
5
|
-
|
5
|
+
## Overview
|
6
6
|
|
7
7
|
```ruby
|
8
|
-
#encoding: utf-8
|
9
8
|
require 'string-direction'
|
10
9
|
|
11
|
-
|
12
|
-
p 'العربية'.direction #=> "rtl"
|
13
|
-
p 'english العربية'.direction #=> "bidi"
|
10
|
+
detector = StringDirection::Detector.new
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
detector.direction('english') #=> 'ltr'
|
13
|
+
detector.direction('العربية') #=> 'rtl'
|
14
|
+
detector.direction("english العربية") #=> 'bidi'
|
15
|
+
|
16
|
+
detector.ltr?('english') #=> true
|
17
|
+
detector.rtl?('العربية') #=> true
|
18
|
+
detector.bidi?('english') #=> false
|
19
19
|
```
|
20
20
|
|
21
|
-
|
22
|
-
If Unicode marks [left-to-right](http://en.wikipedia.org/wiki/Left-to-right_mark) (\u200e) or [right-to-left](http://en.wikipedia.org/wiki/Right-to-left_mark) (\u200f) are present, then `string-direction` rely on them instead of trying to guess from the characters used.
|
21
|
+
But, if you preffer, you can monkey patch `String`:
|
23
22
|
|
24
23
|
```ruby
|
25
|
-
|
26
|
-
|
24
|
+
String.send(:include, StringDirection::StringMethods)
|
25
|
+
|
26
|
+
'english'.direction #=> 'ltr'
|
27
|
+
'العربية'.rtl? #=> true
|
27
28
|
```
|
28
29
|
|
29
|
-
##
|
30
|
+
## Strategies
|
31
|
+
|
32
|
+
`string-direction` uses different strategies in order to try to detect the direction of a string. The detector uses them once at a time and returns the result once one of them succeeds, aborting any further analysis.
|
33
|
+
|
34
|
+
Right now, two strategies are natively integrated: `marks` and `characters`. They are used, in that order, as default strategies if no strategies are given.
|
35
|
+
|
36
|
+
### marks
|
37
|
+
|
38
|
+
Looks for the presence of direction Unicode marks: [left-to-right](http://en.wikipedia.org/wiki/Left-to-right_mark) (\u200e) or [right-to-left](http://en.wikipedia.org/wiki/Right-to-left_mark) (\u200f).
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
detector = StringDirection::Detector.new(:marks)
|
42
|
+
|
43
|
+
detector.direction("\u200eالعربية") #=> "ltr"
|
44
|
+
detector.direction("\u200fEnglish") #=> "rtl"
|
45
|
+
```
|
46
|
+
|
47
|
+
### characters
|
48
|
+
|
49
|
+
Looks for the presence of right-to-left characters in the scripts used in the string.
|
50
|
+
|
30
51
|
By default, `string-direction` consider following scripts to have a right-to-left writing:
|
31
52
|
|
32
53
|
* Arabic
|
@@ -38,12 +59,23 @@ By default, `string-direction` consider following scripts to have a right-to-lef
|
|
38
59
|
* Thaana
|
39
60
|
* Tifinagh
|
40
61
|
|
41
|
-
|
62
|
+
```ruby
|
63
|
+
detector = StringDirection::Detector.new(:characters)
|
64
|
+
|
65
|
+
detector.direction('english') #=> 'ltr'
|
66
|
+
detector.direction('العربية') #=> 'rtl'
|
67
|
+
```
|
68
|
+
|
69
|
+
You can change these defaults:
|
42
70
|
|
43
71
|
```ruby
|
44
|
-
|
45
|
-
|
46
|
-
|
72
|
+
detector.direction('ᚪᚫᚬᚭᚮᚯ') #=> 'ltr'
|
73
|
+
|
74
|
+
StringDirection.configuration do |config|
|
75
|
+
config.rtl_scripts << 'Runic'
|
76
|
+
end
|
77
|
+
|
78
|
+
detector.direction('ᚪᚫᚬᚭᚮᚯ') #=> 'rtl'
|
47
79
|
```
|
48
80
|
|
49
81
|
This can be useful, mainly, for scripts that have both left-to-right and right-to-left representations:
|
@@ -58,6 +90,31 @@ This can be useful, mainly, for scripts that have both left-to-right and right-t
|
|
58
90
|
|
59
91
|
Keep in mind than only [scripts recognized by Ruby regular expressions](http://www.ruby-doc.org/core-1.9.3/Regexp.html#label-Character+Properties) are allowed.
|
60
92
|
|
93
|
+
### Custom Strategies
|
94
|
+
|
95
|
+
You can define your custom strategies. To do so, you just have to define a class inside `StringDirection` module with a name ending with `Strategy`. This class has to respond to an instance method `run` which takes the string as argument. You can inherit from `StringDirection::Strategy` to have convenient methods `ltr`, `rtl` and `bidi`.
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
class StringDirection::AlwaysLtrStrategy
|
99
|
+
def run(string)
|
100
|
+
ltr
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
detector = StringDirection::Detector.new(:always_ltr)
|
105
|
+
detector.direction('العربية') #=> 'ltr'
|
106
|
+
```
|
107
|
+
|
108
|
+
### Changing default strategies
|
109
|
+
|
110
|
+
`marks` and `characters` are default strategies, but you can change them:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
StringDirection.configuration do |config|
|
114
|
+
config.default_strategies = [:custom, :marks, :always_ltr]
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
61
118
|
## Release Policy
|
62
119
|
|
63
120
|
`string-direction` follows the principles of [semantic versioning](http://semver.org/).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'string-direction'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require 'pry'
|
11
|
+
Pry.start
|
data/bin/setup
ADDED
data/lib/string-direction.rb
CHANGED
@@ -1,3 +1,39 @@
|
|
1
|
-
|
1
|
+
require 'string-direction/version'
|
2
|
+
require 'string-direction/configuration'
|
3
|
+
require 'string-direction/detector'
|
4
|
+
require 'string-direction/strategy'
|
5
|
+
require 'string-direction/strategies/marks_strategy'
|
6
|
+
require 'string-direction/strategies/characters_strategy'
|
7
|
+
require 'string-direction/string_methods'
|
2
8
|
|
3
|
-
|
9
|
+
# Constants & configuration common in the whole library
|
10
|
+
module StringDirection
|
11
|
+
# left-to-right identifier
|
12
|
+
LTR = 'ltr'.freeze
|
13
|
+
|
14
|
+
# right-to-left identifier
|
15
|
+
RTL = 'rtl'.freeze
|
16
|
+
|
17
|
+
# bidi identifier
|
18
|
+
BIDI = 'bidi'.freeze
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# {Configuration} object
|
22
|
+
attr_accessor :configuration
|
23
|
+
|
24
|
+
def configuration
|
25
|
+
@configuration ||= Configuration.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# Yields current {Configuration}
|
29
|
+
def configure
|
30
|
+
self.configuration ||= Configuration.new
|
31
|
+
yield(configuration)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Reset {Configuration}
|
35
|
+
def reset_configuration
|
36
|
+
self.configuration = Configuration.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module StringDirection
|
2
|
+
# {StringDirection} configuration
|
3
|
+
class Configuration
|
4
|
+
# Scripts for which characters are treated as right-to-left. Defaults to Arabic, Hebrew, Nko, Kharoshthi, Phoenician, Syriac, Thaana and Tifinagh. Notice than only {http://ruby-doc.org/core-2.2.3/Regexp.html#class-Regexp-label-Character+Properties recognized Ruby regular expression scripts} are accepted.
|
5
|
+
#
|
6
|
+
# @return [Array]
|
7
|
+
attr_accessor :rtl_scripts
|
8
|
+
|
9
|
+
# Default strategies, in order, that {Detector} uses if they are not given explicetly. Values are symbols with a matching class expected. For example, `:marks` expects a class `StringDirection::MarksStrategy` to exist. Defaults to `:marks` and `:characters`.
|
10
|
+
#
|
11
|
+
# @return [Array]
|
12
|
+
attr_accessor :default_strategies
|
13
|
+
|
14
|
+
# Initialize defaults
|
15
|
+
def initialize
|
16
|
+
self.rtl_scripts = %w(Arabic Hebrew Nko Kharoshthi Phoenician Syriac Thaana Tifinagh)
|
17
|
+
self.default_strategies = [:marks, :characters]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module StringDirection
|
2
|
+
# String direction detector
|
3
|
+
class Detector
|
4
|
+
# Array of initialized strategies used, in order, to try to detect the direction of a string
|
5
|
+
#
|
6
|
+
# @return [Array]
|
7
|
+
attr_accessor :strategies
|
8
|
+
|
9
|
+
# Initialize strategies from given arguments. If no strategies are given, they are taken from the value of {StringDirection::Configuration#default_strategies}
|
10
|
+
#
|
11
|
+
# @raise [ArgumentError] if strategy class is not found. For example, for an strategy `:marks` a class `StringDirection::MarksStrategy` is expected
|
12
|
+
def initialize(*strategies)
|
13
|
+
strategies = StringDirection.configuration.default_strategies if strategies.empty?
|
14
|
+
initialize_strategies(strategies)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Tries to detect and return the direction of a string. It returns `ltr` if the string is left-to-right, `rtl` if it is right-to-left, `bidi` if it is bidirectional or `nil` if it can't detect the direction. It iterates through {#strategies} until one of them successes.
|
18
|
+
#
|
19
|
+
# @param string [String] The string to inspect
|
20
|
+
# @return [String, nil]
|
21
|
+
def direction(string)
|
22
|
+
direction = nil
|
23
|
+
strategies.each do |strategy|
|
24
|
+
direction = strategy.run(string)
|
25
|
+
break if direction
|
26
|
+
end
|
27
|
+
direction
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns whether string is left-to-right or not
|
31
|
+
#
|
32
|
+
# @param string [String] The string to inspect
|
33
|
+
# @return [Boolean]
|
34
|
+
def ltr?(string)
|
35
|
+
direction(string) == StringDirection::LTR
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns whether string is right-to-left or not
|
39
|
+
#
|
40
|
+
# @param string [String] The string to inspect
|
41
|
+
# @return [Boolean]
|
42
|
+
def rtl?(string)
|
43
|
+
direction(string) == StringDirection::RTL
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns whether string is bidirectional or not
|
47
|
+
#
|
48
|
+
# @param string [String] The string to inspect
|
49
|
+
# @return [Boolean]
|
50
|
+
def bidi?(string)
|
51
|
+
direction(string) == StringDirection::BIDI
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def initialize_strategies(strategies)
|
57
|
+
self.strategies = strategies.map do |strategy|
|
58
|
+
begin
|
59
|
+
name = infer_strategy_class_name(strategy)
|
60
|
+
Kernel.const_get(name).new
|
61
|
+
rescue NameError
|
62
|
+
raise ArgumentError, "Can't find '#{name}' strategy"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def infer_strategy_class_name(strategy)
|
68
|
+
base_name = strategy.to_s.split('_').map(&:capitalize).join
|
69
|
+
"StringDirection::#{base_name}Strategy"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module StringDirection
|
2
|
+
# Strategy to detect direction from the scripts to which string characters belong
|
3
|
+
class CharactersStrategy < Strategy
|
4
|
+
# Ignored characters: unicode marks, punctuations, symbols, separator and other general categories
|
5
|
+
CHAR_IGNORE_REGEX = /[\p{M}\p{P}\p{S}\p{Z}\p{C}]/.freeze
|
6
|
+
|
7
|
+
# Inspect to wich scripts characters belongs and infer from them the string direction. right-to-left scripts are those in {Configuration#rtl_scripts}
|
8
|
+
#
|
9
|
+
# params [String] The string to inspect
|
10
|
+
# @return [String, nil]
|
11
|
+
def run(string)
|
12
|
+
string = string.to_s
|
13
|
+
if ltr_characters?(string) && rtl_characters?(string)
|
14
|
+
bidi
|
15
|
+
elsif ltr_characters?(string)
|
16
|
+
ltr
|
17
|
+
elsif rtl_characters?(string)
|
18
|
+
rtl
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def rtl_characters?(string)
|
25
|
+
string.match(/[#{join_scripts_for_regexp(rtl_scripts)}]/) ? true : false
|
26
|
+
end
|
27
|
+
|
28
|
+
def ltr_characters?(string)
|
29
|
+
string.gsub(CHAR_IGNORE_REGEX, '').match(/[^#{join_scripts_for_regexp(rtl_scripts)}]/) ? true : false
|
30
|
+
end
|
31
|
+
|
32
|
+
def join_scripts_for_regexp(scripts)
|
33
|
+
scripts.map { |script| '\p{' + script + '}' }.join
|
34
|
+
end
|
35
|
+
|
36
|
+
def rtl_scripts
|
37
|
+
StringDirection.configuration.rtl_scripts
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module StringDirection
|
2
|
+
# Strategy to detect direction looking for the presence of unicode marks
|
3
|
+
class MarksStrategy < Strategy
|
4
|
+
# left-to-right unicode mark
|
5
|
+
LTR_MARK = "\u200e".freeze
|
6
|
+
|
7
|
+
# right-to-right unicode mark
|
8
|
+
RTL_MARK = "\u200f".freeze
|
9
|
+
|
10
|
+
# Look for the presence of unicode marks in given string and infers from them its direction
|
11
|
+
#
|
12
|
+
# params [String] The string to inspect
|
13
|
+
# @return [String, nil]
|
14
|
+
def run(string)
|
15
|
+
string = string.to_s
|
16
|
+
if ltr_mark?(string) && rtl_mark?(string)
|
17
|
+
bidi
|
18
|
+
elsif ltr_mark?(string)
|
19
|
+
ltr
|
20
|
+
elsif rtl_mark?(string)
|
21
|
+
rtl
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def ltr_mark?(string)
|
28
|
+
string.include?(LTR_MARK) ? true : false
|
29
|
+
end
|
30
|
+
|
31
|
+
def rtl_mark?(string)
|
32
|
+
string.include?(RTL_MARK) ? true : false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module StringDirection
|
2
|
+
# @abstract Subclass and override {#run} to implement
|
3
|
+
class Strategy
|
4
|
+
# Each strategy must implement this method, accepting an string as its argument. It must return {StringDirection::LTR}, {StringDirection::RTL}, {StringDirection::BIDI} depending on direction detected, or nil on detection failure
|
5
|
+
# @abstract
|
6
|
+
# @raise [NotImplementedError]
|
7
|
+
def run(string)
|
8
|
+
fail NotImplementedError, "`run` method must be implemented"
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def ltr
|
14
|
+
StringDirection::LTR
|
15
|
+
end
|
16
|
+
|
17
|
+
def rtl
|
18
|
+
StringDirection::RTL
|
19
|
+
end
|
20
|
+
|
21
|
+
def bidi
|
22
|
+
StringDirection::BIDI
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module StringDirection
|
2
|
+
# Methods intended to be monkey patched to String through `String.include(StringDirection::StringMethods)`. This will allow stuff like `'English'.direction #=> 'ltr'`. All methods are delegated to {Detector} with `self` as string argument.
|
3
|
+
module StringMethods
|
4
|
+
# @see Detector#direction
|
5
|
+
# @return [String]
|
6
|
+
def direction
|
7
|
+
string_direction_detector.direction(self)
|
8
|
+
end
|
9
|
+
|
10
|
+
# @see Detector#ltr?
|
11
|
+
def ltr?
|
12
|
+
string_direction_detector.ltr?(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @see Detector#rtl?
|
16
|
+
def rtl?
|
17
|
+
string_direction_detector.rtl?(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @see Detector#bidi?
|
21
|
+
def bidi?
|
22
|
+
string_direction_detector.bidi?(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def string_direction_detector
|
28
|
+
@string_direction_detector ||= StringDirection::Detector.new
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'string-direction'
|
3
|
+
require 'pry'
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
+
Dir[File.join(__dir__, 'support/**/*.rb')].each { |f| require f }
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.color = true
|
5
8
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StringDirection::Configuration do
|
4
|
+
describe '#rtl_scripts' do
|
5
|
+
it 'defaults to an array with Arabic, Hebrew, Nko, Kharoshthi, Phoenician, Syriac, Thaana and Tifinagh' do
|
6
|
+
expect(subject.rtl_scripts).to match_array(%w(Arabic Hebrew Nko Kharoshthi Phoenician Syriac Thaana Tifinagh))
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#default_strategies' do
|
11
|
+
it 'defaults to an array with :marks and :characters' do
|
12
|
+
expect(subject.default_strategies).to eq([:marks, :characters])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StringDirection::Detector do
|
4
|
+
module StringDirection
|
5
|
+
class LtrStrategy < Strategy
|
6
|
+
def run(string)
|
7
|
+
ltr
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class RtlStrategy < Strategy
|
12
|
+
def run(string)
|
13
|
+
rtl
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class BidiStrategy < Strategy
|
18
|
+
def run(string)
|
19
|
+
bidi
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class NilStrategy < Strategy
|
24
|
+
def run(string)
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class CamelizeCamelizeStrategy < Strategy
|
30
|
+
def run(string)
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context '#initialize(*strategies)' do
|
37
|
+
it 'initializes the strategies inflected from the arguments and adds them, in the same order, as strategies instance var array' do
|
38
|
+
detector = described_class.new(:ltr, :nil)
|
39
|
+
|
40
|
+
expect(detector.strategies.first).to be_an_instance_of(StringDirection::LtrStrategy)
|
41
|
+
expect(detector.strategies.last).to be_an_instance_of(StringDirection::NilStrategy)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'infers strategy name camelizing, ending with "Strategy" and looking inside StringDirection module' do
|
45
|
+
detector = described_class.new(:camelize_camelize)
|
46
|
+
|
47
|
+
expect(detector.strategies.first).to be_an_instance_of(StringDirection::CamelizeCamelizeStrategy)
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when it can't infer the strategy class from given symbol" do
|
51
|
+
it 'raises an ArgumentError' do
|
52
|
+
expect { described_class.new(:something) }.to raise_error(ArgumentError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when stragies are not given' do
|
57
|
+
it 'takes defaults set in default_strategies configuration option' do
|
58
|
+
allow(StringDirection.configuration).to receive(:default_strategies).and_return([:ltr])
|
59
|
+
|
60
|
+
detector = described_class.new
|
61
|
+
|
62
|
+
expect(detector.strategies.first).to be_an_instance_of(StringDirection::LtrStrategy)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context '#direction(string)' do
|
68
|
+
context 'when first strategy detects direction' do
|
69
|
+
it 'returns it' do
|
70
|
+
detector = described_class.new(:ltr, :rtl)
|
71
|
+
|
72
|
+
expect(detector.direction('abc')).to eq(StringDirection::LTR)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when first strategy does not detect direction' do
|
77
|
+
it 'it tries with the second' do
|
78
|
+
detector = described_class.new(:nil, :rtl)
|
79
|
+
|
80
|
+
expect(detector.direction('abc')).to eq(StringDirection::RTL)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when no strategy detects direction' do
|
85
|
+
it 'returns nil' do
|
86
|
+
detector = described_class.new(:nil)
|
87
|
+
|
88
|
+
expect(detector.direction('abc')).to be_nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#ltr?(string)' do
|
94
|
+
context 'when string has left-to-right direction' do
|
95
|
+
it 'returns true' do
|
96
|
+
detector = described_class.new(:ltr)
|
97
|
+
|
98
|
+
expect(detector.ltr?('abc')).to eq(true)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'when string has not left-to-right direction' do
|
103
|
+
it 'returns false' do
|
104
|
+
detector = described_class.new(:rtl)
|
105
|
+
|
106
|
+
expect(detector.ltr?('abc')).to eq(false)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe '#rtl?(string)' do
|
112
|
+
context 'when string has right-to-left direction' do
|
113
|
+
it 'returns true' do
|
114
|
+
detector = described_class.new(:rtl)
|
115
|
+
|
116
|
+
expect(detector.rtl?('abc')).to eq(true)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'when string has not right-to-left direction' do
|
121
|
+
it 'returns false' do
|
122
|
+
detector = described_class.new(:bidi)
|
123
|
+
|
124
|
+
expect(detector.rtl?('abc')).to eq(false)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe '#bidi?(string)' do
|
130
|
+
context 'when string has bidirectional direction' do
|
131
|
+
it 'returns true' do
|
132
|
+
detector = described_class.new(:bidi)
|
133
|
+
|
134
|
+
expect(detector.bidi?('abc')).to eq(true)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'when string has not bidirectional direction' do
|
139
|
+
it 'returns false' do
|
140
|
+
detector = described_class.new(:ltr)
|
141
|
+
|
142
|
+
expect(detector.bidi?('abc')).to eq(false)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StringDirection::CharactersStrategy do
|
4
|
+
describe '#run' do
|
5
|
+
let(:english) { 'English' }
|
6
|
+
let(:arabic) { 'العربية' }
|
7
|
+
|
8
|
+
subject { described_class.new.run(string) }
|
9
|
+
|
10
|
+
context 'when both left-to-right and right-to-left characters are present' do
|
11
|
+
let(:string) { arabic + english }
|
12
|
+
|
13
|
+
it "returns 'bidi'" do
|
14
|
+
expect(subject).to eq 'bidi'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when right-to-left character are present but none of left-to-right' do
|
19
|
+
let(:string) { arabic }
|
20
|
+
|
21
|
+
it "returns 'rtl'" do
|
22
|
+
expect(subject).to eq 'rtl'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when left-to-right character are present but none of right-to-left' do
|
27
|
+
let(:string) { english }
|
28
|
+
|
29
|
+
it "returns 'ltr'" do
|
30
|
+
expect(subject).to eq 'ltr'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when neither left-to-right nor right-to-left characters are present' do
|
35
|
+
let(:string) { ' ' }
|
36
|
+
|
37
|
+
it 'returns nil' do
|
38
|
+
expect(subject).to be_nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when default right-to-left scripts are changed' do
|
43
|
+
let(:new_rtl_script) { 'Latin' }
|
44
|
+
let(:old_rtl_script) { 'Arabic' }
|
45
|
+
|
46
|
+
context 'when there are characters from an added right-to-left script' do
|
47
|
+
let(:string) { english }
|
48
|
+
|
49
|
+
it 'treats them as right-to-left chracters' do
|
50
|
+
StringDirection.configure do |config|
|
51
|
+
config.rtl_scripts << new_rtl_script
|
52
|
+
end
|
53
|
+
|
54
|
+
expect(subject).to eq 'rtl'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when there are characters from a deleted right-to-left script ' do
|
59
|
+
let(:string) { arabic }
|
60
|
+
|
61
|
+
it 'treats them as left-to-right characters' do
|
62
|
+
StringDirection.configure do |config|
|
63
|
+
config.rtl_scripts.delete(old_rtl_script)
|
64
|
+
end
|
65
|
+
|
66
|
+
expect(subject).to eq 'ltr'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
after :each do
|
71
|
+
StringDirection.reset_configuration
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'when special characters are present' do
|
76
|
+
let(:string) do
|
77
|
+
mark = "\u0903"
|
78
|
+
punctuation = '_'
|
79
|
+
symbol = '€'
|
80
|
+
separator = ' '
|
81
|
+
other = "\u0005"
|
82
|
+
|
83
|
+
arabic + mark + punctuation + symbol + separator + other
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'ignores them' do
|
87
|
+
expect(subject).to eq 'rtl'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when an object responding to #to_s is given' do
|
92
|
+
let(:string) do
|
93
|
+
class StringDirection::TestObject
|
94
|
+
def to_s
|
95
|
+
'English'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
StringDirection::TestObject.new
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'takes as string the result of #to_s method' do
|
103
|
+
expect(subject).to eq('ltr')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StringDirection::MarksStrategy do
|
4
|
+
describe '#run' do
|
5
|
+
subject { described_class.new.run(string) }
|
6
|
+
|
7
|
+
context 'when string contains the left-to-right mark but not the right-to-left mark' do
|
8
|
+
let(:string) { described_class::LTR_MARK + 'abc' }
|
9
|
+
|
10
|
+
it "returns 'ltr'" do
|
11
|
+
expect(subject).to eql 'ltr'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'when string contains the right-to-left mark but not the left-to-right mark' do
|
16
|
+
let(:string) { described_class::RTL_MARK + 'abc' }
|
17
|
+
|
18
|
+
it "returns 'rtl'" do
|
19
|
+
expect(subject).to eql 'rtl'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when string contains both the left-to-right mark and the right-to-left mark' do
|
24
|
+
let(:string) { described_class::LTR_MARK + described_class::RTL_MARK + 'abc' }
|
25
|
+
|
26
|
+
it "returns 'bidi'" do
|
27
|
+
expect(subject).to eql 'bidi'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when string neither contains the left-to-right mark nor the right-to-left mark' do
|
32
|
+
let(:string) { 'abc' }
|
33
|
+
|
34
|
+
it "returns nil" do
|
35
|
+
expect(subject).to be_nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when an object responding to #to_s is given' do
|
40
|
+
let(:string) do
|
41
|
+
class StringDirection::TestObject
|
42
|
+
def to_s
|
43
|
+
StringDirection::MarksStrategy::LTR_MARK
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
StringDirection::TestObject.new
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'takes as string the result of #to_s method' do
|
51
|
+
expect(subject).to eq('ltr')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StringDirection::StringMethods do
|
4
|
+
subject { 'abc' }
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
String.send(:include, StringDirection::StringMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#direction' do
|
11
|
+
it 'returns string direction' do
|
12
|
+
expect(subject.direction).to eq(StringDirection::LTR)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#ltr?' do
|
17
|
+
it 'returns whether string direction is left-to-right' do
|
18
|
+
expect(subject.ltr?).to eq(true)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#rtl?' do
|
23
|
+
it 'returns whether string direction is right-to-left' do
|
24
|
+
expect(subject.rtl?).to eq(false)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#bidi?' do
|
29
|
+
it 'returns whether string direction is bidirectional' do
|
30
|
+
expect(subject.bidi?).to eq(false)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StringDirection do
|
4
|
+
it 'has a version number' do
|
5
|
+
expect(described_class::VERSION).not_to be nil
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '::LTR' do
|
9
|
+
it "is 'ltr'" do
|
10
|
+
expect(described_class::LTR).to eq('ltr')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '::RTL' do
|
15
|
+
it "is 'rtl'" do
|
16
|
+
expect(described_class::RTL).to eq('rtl')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '::BIDI' do
|
21
|
+
it "is 'bidi'" do
|
22
|
+
expect(described_class::BIDI).to eq('bidi')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '::configure' do
|
27
|
+
it 'initializes configuration instance var with an instance of StringDirection::Configuration' do
|
28
|
+
described_class.configure {}
|
29
|
+
|
30
|
+
expect(described_class.configuration).to be_an_instance_of(StringDirection::Configuration)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'yields the Configuration instance' do
|
34
|
+
expect { |b| described_class.configure(&b) }.to yield_with_args(StringDirection.configuration)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#reset_configuration' do
|
39
|
+
it 'sets its configuration instance var as a new instance of Configuration' do
|
40
|
+
StringDirection.configure {}
|
41
|
+
configuration = StringDirection.configuration
|
42
|
+
|
43
|
+
StringDirection.reset_configuration
|
44
|
+
|
45
|
+
expect(StringDirection.configuration).to be_an_instance_of(StringDirection::Configuration)
|
46
|
+
expect(StringDirection.configuration).not_to eq(configuration)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'string-direction/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'string-direction'
|
8
|
+
s.version = StringDirection::VERSION
|
9
|
+
s.summary = 'Automatic detection of text direction (ltr, rtl or bidi) for strings'
|
10
|
+
s.description = 'https://github.com/waiting-for-dev/string-direction/'
|
11
|
+
s.license = 'GPL3'
|
12
|
+
s.homepage = 'https://github.com/waiting-for-dev/string-direction/'
|
13
|
+
s.authors = ['Marc Busqué']
|
14
|
+
s.email = 'marc@lamarciana.com'
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
|
17
|
+
s.add_runtime_dependency 'yard', '~>0.8'
|
18
|
+
|
19
|
+
s.add_development_dependency 'bundler', '~> 1.10'
|
20
|
+
s.add_development_dependency 'rake', '~> 10.4'
|
21
|
+
s.add_development_dependency 'rspec', '~> 3.3'
|
22
|
+
s.add_development_dependency 'pry', '~> 0.10'
|
23
|
+
s.add_development_dependency 'pry-byebug', '~> 3.2'
|
24
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: string-direction
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc Busqué
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yard
|
@@ -25,33 +25,75 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.8'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
-
type: :
|
33
|
+
version: '1.10'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.10'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.4'
|
48
|
+
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
54
|
+
version: '10.4'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rspec
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '3.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.10'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.10'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry-byebug
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.2'
|
48
90
|
type: :development
|
49
91
|
prerelease: false
|
50
92
|
version_requirements: !ruby/object:Gem::Requirement
|
51
93
|
requirements:
|
52
94
|
- - "~>"
|
53
95
|
- !ruby/object:Gem::Version
|
54
|
-
version: '2
|
96
|
+
version: '3.2'
|
55
97
|
description: https://github.com/waiting-for-dev/string-direction/
|
56
98
|
email: marc@lamarciana.com
|
57
99
|
executables: []
|
@@ -61,12 +103,28 @@ files:
|
|
61
103
|
- ".gitignore"
|
62
104
|
- ".yardopts"
|
63
105
|
- COPYING.txt
|
106
|
+
- Gemfile
|
64
107
|
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- bin/console
|
110
|
+
- bin/setup
|
65
111
|
- lib/string-direction.rb
|
66
|
-
- lib/string-direction/
|
112
|
+
- lib/string-direction/configuration.rb
|
113
|
+
- lib/string-direction/detector.rb
|
114
|
+
- lib/string-direction/strategies/characters_strategy.rb
|
115
|
+
- lib/string-direction/strategies/marks_strategy.rb
|
116
|
+
- lib/string-direction/strategy.rb
|
117
|
+
- lib/string-direction/string_methods.rb
|
118
|
+
- lib/string-direction/version.rb
|
67
119
|
- spec/spec_helper.rb
|
68
|
-
- spec/string/
|
69
|
-
- string-direction.
|
120
|
+
- spec/string-direction/configuration_spec.rb
|
121
|
+
- spec/string-direction/detector_spec.rb
|
122
|
+
- spec/string-direction/strategies/characters_strategy_spec.rb
|
123
|
+
- spec/string-direction/strategies/marks_strategy_spec.rb
|
124
|
+
- spec/string-direction/strategy_spec.rb
|
125
|
+
- spec/string-direction/string_methods_spec.rb
|
126
|
+
- spec/string-direction_spec.rb
|
127
|
+
- string-direction.gemspec
|
70
128
|
homepage: https://github.com/waiting-for-dev/string-direction/
|
71
129
|
licenses:
|
72
130
|
- GPL3
|
@@ -87,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
145
|
version: '0'
|
88
146
|
requirements: []
|
89
147
|
rubyforge_project:
|
90
|
-
rubygems_version: 2.
|
148
|
+
rubygems_version: 2.4.5
|
91
149
|
signing_key:
|
92
150
|
specification_version: 4
|
93
151
|
summary: Automatic detection of text direction (ltr, rtl or bidi) for strings
|
@@ -1,102 +0,0 @@
|
|
1
|
-
#encoding: UTF-8
|
2
|
-
|
3
|
-
# Module with all the logic for automatic detection of text direction. It will be included in String class.
|
4
|
-
module StringDirection
|
5
|
-
# left-to-right unicode mark
|
6
|
-
LTR_MARK = "\u200e"
|
7
|
-
|
8
|
-
# right-to-left unicode mark
|
9
|
-
RTL_MARK = "\u200f"
|
10
|
-
|
11
|
-
# returns the direction in which a string is written
|
12
|
-
#
|
13
|
-
# @return ["lft"] if it's a left-to-right string
|
14
|
-
# @return ["rtl"] if it's a right-to-left string
|
15
|
-
# @return ["bidi"] if it's a bi-directinal string
|
16
|
-
def direction
|
17
|
-
if has_ltr_mark? and has_rtl_mark?
|
18
|
-
'bidi'
|
19
|
-
elsif has_ltr_mark?
|
20
|
-
'ltr'
|
21
|
-
elsif has_rtl_mark?
|
22
|
-
'rtl'
|
23
|
-
elsif not has_rtl_characters?
|
24
|
-
'ltr'
|
25
|
-
elsif has_ltr_characters?
|
26
|
-
'bidi'
|
27
|
-
else
|
28
|
-
'rtl'
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# whether string is a left-to-right one
|
33
|
-
#
|
34
|
-
# @return [Boolean] true if it is a left-to-right string, false otherwise
|
35
|
-
def is_ltr?
|
36
|
-
(direction == 'ltr') ? true : false
|
37
|
-
end
|
38
|
-
|
39
|
-
# whether string is a right-to-left one
|
40
|
-
#
|
41
|
-
# @return [Boolean] true if it is a right-to-left string, false otherwise
|
42
|
-
def is_rtl?
|
43
|
-
(direction == 'rtl') ? true : false
|
44
|
-
end
|
45
|
-
|
46
|
-
# whether string is a bi-directional one
|
47
|
-
#
|
48
|
-
# @return [Boolean] true if it is a bi-directional string, false otherwise
|
49
|
-
def is_bidi?
|
50
|
-
(direction == 'bidi') ? true : false
|
51
|
-
end
|
52
|
-
|
53
|
-
# returns whether string contains the unicode left-to-right mark
|
54
|
-
#
|
55
|
-
# @return [Boolean] true if it containts ltr mark, false otherwise
|
56
|
-
def has_ltr_mark?
|
57
|
-
match(/^(.*)#{LTR_MARK}(.*)$/) ? true : false
|
58
|
-
end
|
59
|
-
|
60
|
-
# returns whether string contains the unicode right-to-left mark
|
61
|
-
#
|
62
|
-
# @return [Boolean] true if it containts rtl mark, false otherwise
|
63
|
-
def has_rtl_mark?
|
64
|
-
match(/^(.*)#{RTL_MARK}(.*)$/) ? true : false
|
65
|
-
end
|
66
|
-
|
67
|
-
# returns whether string contains some right-to-left character
|
68
|
-
#
|
69
|
-
# @return [Boolean] true if it containts rtl characters, false otherwise
|
70
|
-
def has_rtl_characters?
|
71
|
-
match(/[#{StringDirection::join_scripts_for_regexp(StringDirection.rtl_scripts)}]/) ? true : false
|
72
|
-
end
|
73
|
-
|
74
|
-
# returns whether string contains some left-to-right character
|
75
|
-
#
|
76
|
-
# @return [Boolean] true if it containts ltr characters, false otherwise
|
77
|
-
def has_ltr_characters?
|
78
|
-
# ignore unicode marks, punctuations, symbols, separator and other general categories
|
79
|
-
gsub(/[\p{M}\p{P}\p{S}\p{Z}\p{C}]/, '').match(/[^#{StringDirection::join_scripts_for_regexp(StringDirection.rtl_scripts)}]/) ? true : false
|
80
|
-
end
|
81
|
-
|
82
|
-
class << self
|
83
|
-
attr_accessor :rtl_scripts
|
84
|
-
|
85
|
-
# hook that is called when the module is included and that initializes rtl_scripts
|
86
|
-
#
|
87
|
-
# @param [Module] base The base module from within current module is included
|
88
|
-
def included(base)
|
89
|
-
@rtl_scripts = %w[Arabic Hebrew Nko Kharoshthi Phoenician Syriac Thaana Tifinagh]
|
90
|
-
end
|
91
|
-
|
92
|
-
# given an array of script names, which should be supported by Ruby {http://www.ruby-doc.org/core-1.9.3/Regexp.html#label-Character+Properties regular expression properties}, returns a string where all of them are concatenaded inside a "\\p{}" construction
|
93
|
-
#
|
94
|
-
# @param [Array] scripts the array of script names
|
95
|
-
# @return [String] the script names joined ready to be used in the construction of a regular expression
|
96
|
-
# @example
|
97
|
-
# StringDirection.join_scripts_for_regexp(%w[Arabic Hebrew]) #=> "\p{Arabic}\p{Hebrew}"
|
98
|
-
def join_scripts_for_regexp(scripts)
|
99
|
-
scripts.map { |script| '\p{'+script+'}' }.join
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
data/spec/string/string_spec.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
#encoding: UTF-8
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe String do
|
5
|
-
let(:english) { 'English' }
|
6
|
-
let(:arabic) { 'العربية' }
|
7
|
-
describe "#direction" do
|
8
|
-
context "when marks are present" do
|
9
|
-
it "should return 'ltr' if it contains the left-to-right mark and no right-to-left mark" do
|
10
|
-
string = String::LTR_MARK+english
|
11
|
-
string.direction.should eql 'ltr'
|
12
|
-
end
|
13
|
-
it "should return 'rtl' if it contains the right-to-left mark and no left-to-right mark" do
|
14
|
-
string = String::RTL_MARK+arabic
|
15
|
-
string.direction.should eql 'rtl'
|
16
|
-
end
|
17
|
-
it "should return 'bidi' if it contains both the left-to-right mark and the right-to-left mark" do
|
18
|
-
string = String::LTR_MARK+english+String::RTL_MARK+arabic
|
19
|
-
string.direction.should eql 'bidi'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
context "when marks are not present" do
|
23
|
-
it "should return 'ltr' if no right-to-left character is present" do
|
24
|
-
string = english
|
25
|
-
string.direction.should eql 'ltr'
|
26
|
-
end
|
27
|
-
it "should return 'rtl' if only right-to-left character are present" do
|
28
|
-
string = arabic
|
29
|
-
string.direction.should eql 'rtl'
|
30
|
-
end
|
31
|
-
it "should return 'bidi' if both left-to-right and right-to-left characters are present" do
|
32
|
-
string = arabic+' '+english
|
33
|
-
string.direction.should eql 'bidi'
|
34
|
-
end
|
35
|
-
end
|
36
|
-
context "when default rtl scripts are changed" do
|
37
|
-
let(:new_rtl_script) { 'Latin' }
|
38
|
-
let(:old_rtl_script) { 'Arabic' }
|
39
|
-
it "should return 'rtl' if there are characters for an added right-to-left script and no marks characters are present" do
|
40
|
-
StringDirection.rtl_scripts << new_rtl_script
|
41
|
-
string = english
|
42
|
-
string.direction.should eql 'rtl'
|
43
|
-
end
|
44
|
-
it "should return 'ltr' if there are characters for a deleted right-to-left script (so now ltr) and no mark characters are present" do
|
45
|
-
StringDirection.rtl_scripts.delete old_rtl_script
|
46
|
-
string = arabic
|
47
|
-
string.direction.should eql 'ltr'
|
48
|
-
end
|
49
|
-
after :each do
|
50
|
-
StringDirection.rtl_scripts.delete new_rtl_script if StringDirection.rtl_scripts.include? new_rtl_script
|
51
|
-
StringDirection.rtl_scripts << old_rtl_script unless StringDirection.rtl_scripts.include? old_rtl_script
|
52
|
-
end
|
53
|
-
context "when special characters are present" do
|
54
|
-
it "should ignore special characters for the direction detection" do
|
55
|
-
mark = "\u0903"
|
56
|
-
punctuation = "_"
|
57
|
-
symbol = "€"
|
58
|
-
separator = " "
|
59
|
-
other = "\u0005"
|
60
|
-
string = arabic+mark+punctuation+symbol+separator+other
|
61
|
-
string.direction.should eql 'rtl'
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
describe "#is_ltr?" do
|
67
|
-
it "should return true if it is a left-to-right string" do
|
68
|
-
string = english
|
69
|
-
string.is_ltr?.should be_true
|
70
|
-
end
|
71
|
-
it "should return false if it is not a left-to-right string" do
|
72
|
-
string = arabic
|
73
|
-
string.is_ltr?.should be_false
|
74
|
-
end
|
75
|
-
end
|
76
|
-
describe "#is_rtl?" do
|
77
|
-
it "should return true if it is a right-to-left string" do
|
78
|
-
string = arabic
|
79
|
-
string.is_rtl?.should be_true
|
80
|
-
end
|
81
|
-
it "should return false if it is not a right-to-left string" do
|
82
|
-
string = english
|
83
|
-
string.is_rtl?.should be_false
|
84
|
-
end
|
85
|
-
end
|
86
|
-
describe "#is_bidi?" do
|
87
|
-
it "should return true if it is a bi-directional string" do
|
88
|
-
string = english+' '+arabic
|
89
|
-
string.is_bidi?.should be_true
|
90
|
-
end
|
91
|
-
it "should return false if it is not a bi-directional string" do
|
92
|
-
string = english
|
93
|
-
string.is_bidi?.should be_false
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
data/string-direction.spec
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
Gem::Specification.new do |s|
|
2
|
-
s.name = 'string-direction'
|
3
|
-
s.version = '0.0.4'
|
4
|
-
s.summary = 'Automatic detection of text direction (ltr, rtl or bidi) for strings'
|
5
|
-
s.description = 'https://github.com/waiting-for-dev/string-direction/'
|
6
|
-
s.license = 'GPL3'
|
7
|
-
s.homepage = 'https://github.com/waiting-for-dev/string-direction/'
|
8
|
-
s.authors = ['Marc Busqué']
|
9
|
-
s.email = 'marc@lamarciana.com'
|
10
|
-
s.files = `git ls-files`.split("\n")
|
11
|
-
|
12
|
-
s.add_runtime_dependency "yard", "~>0.8"
|
13
|
-
s.add_runtime_dependency "redcarpet", "~>2.2"
|
14
|
-
|
15
|
-
s.add_development_dependency "rspec", "~>2.13"
|
16
|
-
end
|