string-direction 0.0.4 → 1.0.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.
- 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
|