elch_scan 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +112 -0
- data/Rakefile +1 -0
- data/VERSION +1 -0
- data/bin/elch_scan +14 -0
- data/doc/config.yml +44 -0
- data/doc/filter.rb +70 -0
- data/elch_scan.gemspec +29 -0
- data/elch_scan.rb +2 -0
- data/lib/active_support/number_helper/number_converter.rb +182 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +21 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
- data/lib/banana/logger.rb +258 -0
- data/lib/elch_scan/application/dispatch.rb +190 -0
- data/lib/elch_scan/application/filter.rb +34 -0
- data/lib/elch_scan/application.rb +125 -0
- data/lib/elch_scan/formatter/base.rb +17 -0
- data/lib/elch_scan/formatter/html.rb +9 -0
- data/lib/elch_scan/formatter/plain.rb +76 -0
- data/lib/elch_scan/movie.rb +92 -0
- data/lib/elch_scan/version.rb +4 -0
- data/lib/elch_scan.rb +31 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 131c629fe880ea13e446b157bc7586d7d0ebb780
|
4
|
+
data.tar.gz: 986cd909e6034a135cd68a523860139daa6689c3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7b002be74cadb78eb3c2dc5758d09f863b24b57b652dd8e470e6875a5dc01b70c989938066210444380cd6a498d98406a2cf27efd5356deab943a4c5eea88926
|
7
|
+
data.tar.gz: c6b3ec97796266ff9eb171307972eaec59cb124fd6151edd7a1bc117accd4e587d70e1e82475302beb914abf3b0630975308250c05387dcd340d38a35bf933f6
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Sven Pachnit
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# ElchScan
|
2
|
+
|
3
|
+
Query your MediaElch/XBMC library with Ruby! Easy and powerful search for your media chaos.
|
4
|
+
|
5
|
+
* Query on NFO, source data or file contents
|
6
|
+
* e.g.: List movies with actor X but not actor Y and release date was between 2003 and 2010
|
7
|
+
* e.g.: List movies with or without local trailers
|
8
|
+
* e.g.: List movies with more than one or exactly one audio stream
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
$ gem install elch_scan
|
13
|
+
|
14
|
+
## Usage
|
15
|
+
|
16
|
+
First generate a sample configuration by running
|
17
|
+
|
18
|
+
$ elch_scan --generate-config
|
19
|
+
|
20
|
+
You will need to specify at least one directory with movies in it. You might want to change other settings as well.
|
21
|
+
|
22
|
+
|
23
|
+
To get a basic list of what you've got run
|
24
|
+
|
25
|
+
$ elch_scan -q
|
26
|
+
|
27
|
+
![example](http://files.sven.bmonkeys.net/images/_master_Volumescodebinelch_scan__bash_20140408_072644_20140408_072649.png)
|
28
|
+
|
29
|
+
To get a list of available options run
|
30
|
+
|
31
|
+
$ elch_scan --help
|
32
|
+
|
33
|
+
Usage: elch_scan [options]
|
34
|
+
--generate-config Generate sample configuration file in ~/.elch_scan.yml
|
35
|
+
-h, --help Shows this help
|
36
|
+
-v, --version Shows version and other info
|
37
|
+
-f, --formatter HTML Use formatter
|
38
|
+
-o, --output FILENAME Write formatted results to file
|
39
|
+
-e, --edit SELECT_SCRIPT Edit selector script
|
40
|
+
-s, --select [WITH_SUBS,NO_NFO] Filter movies with saved selector scripts
|
41
|
+
-p, --permute Open editor to write permutation code for collection
|
42
|
+
-q, --quiet Don't ask to filter or save results
|
43
|
+
-c, --console Start console to play around with the collection
|
44
|
+
|
45
|
+
If you choose to create a script or filter on the fly you will find some more examples on this topic.
|
46
|
+
|
47
|
+
### Filters
|
48
|
+
|
49
|
+
You can easily filter your movies with Ruby. It's not hard, just look at these examples.
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
# Filter by name, for regex see http://rubular.com
|
53
|
+
collection.select! {|name, movie| movie.name =~ /simpsons/i }
|
54
|
+
|
55
|
+
# Rating > 5
|
56
|
+
collection.select! {|name, movie| movie.nfo!{|n| n["rating"].first.to_f > 5 } }
|
57
|
+
|
58
|
+
# 720p or higher
|
59
|
+
collection.select! { |name, movie| movie.source.width >= 1280 }
|
60
|
+
|
61
|
+
# With actor
|
62
|
+
collection.select! do |name, movie|
|
63
|
+
movie.nfo! {|n| n["actor"].map {|actor| actor["name"].first }.include?("Jim Carrey") }
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
Note: Use `binding.pry` to start an interactive console so you can easily check out which attributes are available.
|
68
|
+
|
69
|
+
### Custom formatters
|
70
|
+
|
71
|
+
To add a custom formatter just add the ruby file to your config (you can omit the .rb):
|
72
|
+
|
73
|
+
```yml
|
74
|
+
formatters:
|
75
|
+
- "~/.elch_scan/my_custom_formatter.rb"
|
76
|
+
```
|
77
|
+
|
78
|
+
To write a custom formatter look at the existing ones. You can take this as template:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
module ElchScan
|
82
|
+
module Formatter
|
83
|
+
class MyFormatter < Base
|
84
|
+
|
85
|
+
def format(results)
|
86
|
+
[].tap do |lines|
|
87
|
+
# render your output and append it to lines
|
88
|
+
lines << "I have #{results.count} results here..."
|
89
|
+
binding.pry # Interactive console will start here!
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
You can then use your formatter by passing `-f MyFormatter`, it's case sensitive!
|
99
|
+
|
100
|
+
|
101
|
+
## ToDo
|
102
|
+
|
103
|
+
- [ ] Add HTML formatter
|
104
|
+
- [ ] Add support for TV shows
|
105
|
+
|
106
|
+
## Contributing
|
107
|
+
|
108
|
+
1. Fork it ( http://github.com/2called-chaos/elch_scan/fork )
|
109
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
110
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
111
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
112
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/elch_scan
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# That's __FILE__ in BASH :)
|
4
|
+
# From: http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
|
5
|
+
SOURCE="${BASH_SOURCE[0]}"
|
6
|
+
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
7
|
+
MCLDIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
8
|
+
SOURCE="$(readlink "$SOURCE")"
|
9
|
+
[[ $SOURCE != /* ]] && SOURCE="$MCLDIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
10
|
+
done
|
11
|
+
PROJECT_ROOT="$( cd -P "$( dirname "$SOURCE" )"/.. && pwd )"
|
12
|
+
|
13
|
+
# Actually run Heatmon
|
14
|
+
cd $PROJECT_ROOT && bundle exec ruby elch_scan.rb "$@"
|
data/doc/config.yml
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# This file has to be valid YAML!
|
2
|
+
# Small howto: http://ess.khhq.net/wiki/YAML_Tutorial
|
3
|
+
# Validate: http://yamllint.com/
|
4
|
+
|
5
|
+
# add your movie directories here. Note that we only support
|
6
|
+
# "one folder per movie" structures. TV show support may come soon.
|
7
|
+
movies:
|
8
|
+
- "/mnt/media/my_movies"
|
9
|
+
|
10
|
+
# if you want to add custom formatters you can pass a list of ruby files.
|
11
|
+
# take a look at the existing formatters to build your own.
|
12
|
+
formatters: []
|
13
|
+
|
14
|
+
# Application settings
|
15
|
+
application:
|
16
|
+
# Checks for new version when calling "--version" or "-v"
|
17
|
+
check_version: true
|
18
|
+
|
19
|
+
# The command of the editor to use for defining filters
|
20
|
+
# - vim
|
21
|
+
# - nano
|
22
|
+
# - mate -w
|
23
|
+
# - subl -w
|
24
|
+
editor: vim
|
25
|
+
|
26
|
+
# Change these to match your file naming
|
27
|
+
naming:
|
28
|
+
nfo: <baseFileName>.nfo
|
29
|
+
poster: <baseFileName>-poster.jpg
|
30
|
+
fanart: <baseFileName>-fanart.jpg
|
31
|
+
logo: logo.png
|
32
|
+
clear_art: clearart.png
|
33
|
+
cd_art: disc.png
|
34
|
+
banner: <baseFileName>-banner.jpg
|
35
|
+
thumb: <baseFileName>-landscape.jpg
|
36
|
+
trailer: <baseFileName>-trailer
|
37
|
+
|
38
|
+
# used to determine whether a file is a video file or not
|
39
|
+
video_extensions: 3gp avi flv m1v m2v m4v mkv mov mpeg mpg mpe ogg rm wmv
|
40
|
+
|
41
|
+
# This controls the console output and logging
|
42
|
+
logger:
|
43
|
+
# disable colorized output
|
44
|
+
colorize: true
|
data/doc/filter.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# TIP: Using vim and want to get rid of this example shit?
|
2
|
+
# In nav-mode type: 100dd
|
3
|
+
|
4
|
+
# Hey there,
|
5
|
+
# to filter your records you will use Ruby
|
6
|
+
# but don't be afraid, it's fairly simple.
|
7
|
+
# Just look at the examples and referenced links.
|
8
|
+
|
9
|
+
# You have access to a variable `collection` and
|
10
|
+
# whatever you do with it, we will take the same
|
11
|
+
# variable `collection` as result.
|
12
|
+
# This means you can reassign or permute it.
|
13
|
+
# YOU SHOULD NOT MODIFY the movie objects! Do this
|
14
|
+
# with "-p" or "--permute"!
|
15
|
+
|
16
|
+
# Use ruby methods to narrow down you result set.
|
17
|
+
# * http://www.ruby-doc.org/core-2.1.1/File.html
|
18
|
+
# * http://www.ruby-doc.org/core-2.1.1/Enumerable.html
|
19
|
+
|
20
|
+
# ====================================================
|
21
|
+
# = Doc (remove, reuse or comment out the examples!) =
|
22
|
+
# ====================================================
|
23
|
+
collection.select! do |name, movie|
|
24
|
+
# name is the same as movie.dirname
|
25
|
+
# movie has the following methods
|
26
|
+
# * dir => source movie directory (e.g. C:/Movies)
|
27
|
+
# * path => folder path of movie (e.g. C:/Movies/BestFilmEver)
|
28
|
+
# * dirname => source movie directory name (e.g. BestFilmEver)
|
29
|
+
# * files => array of all files in movie folder
|
30
|
+
# * name => base name of movie file (without extension)
|
31
|
+
# * movie => movie file
|
32
|
+
# * nfo! => Hash/Array representation of NFO-XML (see http://xml-simple.rubyforge.org/)
|
33
|
+
# Almost all shit is an array so [0] or .first is your friend.
|
34
|
+
# Pass a block (yields the nfo representation) to catch nil[] errors.
|
35
|
+
# * source => StreamIO object (see https://github.com/streamio/streamio-ffmpeg)
|
36
|
+
# * all naming key names from the configuration file (nfo, poster, etc.)
|
37
|
+
|
38
|
+
# Set break point to interactively call methods from here.
|
39
|
+
# See http://pryrepl.org ory type "help" when you are in the REPL.
|
40
|
+
# Use exit or exit! to break out of REPL.
|
41
|
+
# binding.pry
|
42
|
+
end
|
43
|
+
|
44
|
+
# Filter by name, for regex see http://rubular.com
|
45
|
+
collection.select! {|name, movie| movie.name =~ /simpsons/i }
|
46
|
+
|
47
|
+
# Rating > 5
|
48
|
+
collection.select! {|name, movie| movie.nfo!{|n| n["rating"].first.to_f > 5 } }
|
49
|
+
|
50
|
+
# 720p or higher
|
51
|
+
collection.select! { |name, movie| movie.source.width >= 1280 }
|
52
|
+
|
53
|
+
# With actor
|
54
|
+
collection.select! do |name, movie|
|
55
|
+
movie.nfo! {|n| n["actor"].map {|actor| actor["name"].first }.include?("Jim Carrey") }
|
56
|
+
end
|
57
|
+
|
58
|
+
# No thriller movies
|
59
|
+
collection.select! do |name, movie|
|
60
|
+
!movie.nfo!{|n| n["genre"][0].split("/").map(&:strip).include?("Thriller") }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Only with english sound stream
|
64
|
+
collection.select! do |name, movie|
|
65
|
+
movie.nfo! do |nfo|
|
66
|
+
nfo["fileinfo"][0]["streamdetails"][0]["audio"].any? do |stream|
|
67
|
+
stream["language"][0] == "eng"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/elch_scan.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'elch_scan/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "elch_scan"
|
8
|
+
spec.version = ElchScan::VERSION
|
9
|
+
spec.authors = ["Sven Pachnit"]
|
10
|
+
spec.email = ["sven@bmonkeys.net"]
|
11
|
+
spec.summary = %q{ODO: Write a short summary. Required.}
|
12
|
+
spec.description = %q{ODO: Write a longer description. Optional.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "active_support"
|
22
|
+
spec.add_runtime_dependency "i18n"
|
23
|
+
spec.add_runtime_dependency "pry"
|
24
|
+
spec.add_runtime_dependency "xml-simple"
|
25
|
+
spec.add_runtime_dependency "streamio-ffmpeg"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
end
|
data/elch_scan.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'active_support/core_ext/big_decimal/conversions'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
3
|
+
require 'active_support/core_ext/hash/keys'
|
4
|
+
require 'active_support/i18n'
|
5
|
+
require 'active_support/core_ext/class/attribute'
|
6
|
+
|
7
|
+
module ActiveSupport
|
8
|
+
module NumberHelper
|
9
|
+
class NumberConverter # :nodoc:
|
10
|
+
# Default and i18n option namespace per class
|
11
|
+
class_attribute :namespace
|
12
|
+
|
13
|
+
# Does the object need a number that is a valid float?
|
14
|
+
class_attribute :validate_float
|
15
|
+
|
16
|
+
attr_reader :number, :opts
|
17
|
+
|
18
|
+
DEFAULTS = {
|
19
|
+
# Used in number_to_delimited
|
20
|
+
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
|
21
|
+
format: {
|
22
|
+
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
|
23
|
+
separator: ".",
|
24
|
+
# Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
|
25
|
+
delimiter: ",",
|
26
|
+
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
|
27
|
+
precision: 3,
|
28
|
+
# If set to true, precision will mean the number of significant digits instead
|
29
|
+
# of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
|
30
|
+
significant: false,
|
31
|
+
# If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
|
32
|
+
strip_insignificant_zeros: false
|
33
|
+
},
|
34
|
+
|
35
|
+
# Used in number_to_currency
|
36
|
+
currency: {
|
37
|
+
format: {
|
38
|
+
format: "%u%n",
|
39
|
+
negative_format: "-%u%n",
|
40
|
+
unit: "$",
|
41
|
+
# These five are to override number.format and are optional
|
42
|
+
separator: ".",
|
43
|
+
delimiter: ",",
|
44
|
+
precision: 2,
|
45
|
+
significant: false,
|
46
|
+
strip_insignificant_zeros: false
|
47
|
+
}
|
48
|
+
},
|
49
|
+
|
50
|
+
# Used in number_to_percentage
|
51
|
+
percentage: {
|
52
|
+
format: {
|
53
|
+
delimiter: "",
|
54
|
+
format: "%n%"
|
55
|
+
}
|
56
|
+
},
|
57
|
+
|
58
|
+
# Used in number_to_rounded
|
59
|
+
precision: {
|
60
|
+
format: {
|
61
|
+
delimiter: ""
|
62
|
+
}
|
63
|
+
},
|
64
|
+
|
65
|
+
# Used in number_to_human_size and number_to_human
|
66
|
+
human: {
|
67
|
+
format: {
|
68
|
+
# These five are to override number.format and are optional
|
69
|
+
delimiter: "",
|
70
|
+
precision: 3,
|
71
|
+
significant: true,
|
72
|
+
strip_insignificant_zeros: true
|
73
|
+
},
|
74
|
+
# Used in number_to_human_size
|
75
|
+
storage_units: {
|
76
|
+
# Storage units output formatting.
|
77
|
+
# %u is the storage unit, %n is the number (default: 2 MB)
|
78
|
+
format: "%n %u",
|
79
|
+
units: {
|
80
|
+
byte: "Bytes",
|
81
|
+
kb: "KB",
|
82
|
+
mb: "MB",
|
83
|
+
gb: "GB",
|
84
|
+
tb: "TB"
|
85
|
+
}
|
86
|
+
},
|
87
|
+
# Used in number_to_human
|
88
|
+
decimal_units: {
|
89
|
+
format: "%n %u",
|
90
|
+
# Decimal units output formatting
|
91
|
+
# By default we will only quantify some of the exponents
|
92
|
+
# but the commented ones might be defined or overridden
|
93
|
+
# by the user.
|
94
|
+
units: {
|
95
|
+
# femto: Quadrillionth
|
96
|
+
# pico: Trillionth
|
97
|
+
# nano: Billionth
|
98
|
+
# micro: Millionth
|
99
|
+
# mili: Thousandth
|
100
|
+
# centi: Hundredth
|
101
|
+
# deci: Tenth
|
102
|
+
unit: "",
|
103
|
+
# ten:
|
104
|
+
# one: Ten
|
105
|
+
# other: Tens
|
106
|
+
# hundred: Hundred
|
107
|
+
thousand: "Thousand",
|
108
|
+
million: "Million",
|
109
|
+
billion: "Billion",
|
110
|
+
trillion: "Trillion",
|
111
|
+
quadrillion: "Quadrillion"
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
def self.convert(number, options)
|
118
|
+
new(number, options).execute
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize(number, options)
|
122
|
+
@number = number
|
123
|
+
@opts = options.symbolize_keys
|
124
|
+
end
|
125
|
+
|
126
|
+
def execute
|
127
|
+
if !number
|
128
|
+
nil
|
129
|
+
elsif validate_float? && !valid_float?
|
130
|
+
number
|
131
|
+
else
|
132
|
+
convert
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def options
|
139
|
+
@options ||= format_options.merge(opts)
|
140
|
+
end
|
141
|
+
|
142
|
+
def format_options #:nodoc:
|
143
|
+
default_format_options.merge!(i18n_format_options)
|
144
|
+
end
|
145
|
+
|
146
|
+
def default_format_options #:nodoc:
|
147
|
+
options = DEFAULTS[:format].dup
|
148
|
+
options.merge!(DEFAULTS[namespace][:format]) if namespace
|
149
|
+
options
|
150
|
+
end
|
151
|
+
|
152
|
+
def i18n_format_options #:nodoc:
|
153
|
+
locale = opts[:locale]
|
154
|
+
options = I18n.translate(:'number.format', locale: locale, default: {}).dup
|
155
|
+
|
156
|
+
if namespace
|
157
|
+
options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
|
158
|
+
end
|
159
|
+
|
160
|
+
options
|
161
|
+
end
|
162
|
+
|
163
|
+
def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
|
164
|
+
I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
|
165
|
+
end
|
166
|
+
|
167
|
+
def translate_in_locale(key, i18n_options = {})
|
168
|
+
translate_number_value_with_default(key, { locale: options[:locale] }.merge(i18n_options))
|
169
|
+
end
|
170
|
+
|
171
|
+
def default_value(key)
|
172
|
+
key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
|
173
|
+
end
|
174
|
+
|
175
|
+
def valid_float? #:nodoc:
|
176
|
+
Float(number)
|
177
|
+
rescue ArgumentError, TypeError
|
178
|
+
false
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveSupport
|
2
|
+
module NumberHelper
|
3
|
+
class NumberToDelimitedConverter < NumberConverter #:nodoc:
|
4
|
+
self.validate_float = true
|
5
|
+
|
6
|
+
DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
|
7
|
+
|
8
|
+
def convert
|
9
|
+
parts.join(options[:separator])
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def parts
|
15
|
+
left, right = number.to_s.split('.')
|
16
|
+
left.gsub!(DELIMITED_REGEX) { "#{$1}#{options[:delimiter]}" }
|
17
|
+
[left, right].compact
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ActiveSupport
|
2
|
+
module NumberHelper
|
3
|
+
class NumberToHumanSizeConverter < NumberConverter #:nodoc:
|
4
|
+
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
|
5
|
+
|
6
|
+
self.namespace = :human
|
7
|
+
self.validate_float = true
|
8
|
+
|
9
|
+
def convert
|
10
|
+
@number = Float(number)
|
11
|
+
|
12
|
+
# for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
|
13
|
+
unless options.key?(:strip_insignificant_zeros)
|
14
|
+
options[:strip_insignificant_zeros] = true
|
15
|
+
end
|
16
|
+
|
17
|
+
if smaller_than_base?
|
18
|
+
number_to_format = number.to_i.to_s
|
19
|
+
else
|
20
|
+
human_size = number / (base ** exponent)
|
21
|
+
number_to_format = NumberToRoundedConverter.convert(human_size, options)
|
22
|
+
end
|
23
|
+
conversion_format.gsub(/%n/, number_to_format).gsub(/%u/, unit)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def conversion_format
|
29
|
+
translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
|
30
|
+
end
|
31
|
+
|
32
|
+
def unit
|
33
|
+
translate_number_value_with_default(storage_unit_key, :locale => options[:locale], :count => number.to_i, :raise => true)
|
34
|
+
end
|
35
|
+
|
36
|
+
def storage_unit_key
|
37
|
+
key_end = smaller_than_base? ? 'byte' : STORAGE_UNITS[exponent]
|
38
|
+
"human.storage_units.units.#{key_end}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def exponent
|
42
|
+
max = STORAGE_UNITS.size - 1
|
43
|
+
exp = (Math.log(number) / Math.log(base)).to_i
|
44
|
+
exp = max if exp > max # avoid overflow for the highest unit
|
45
|
+
exp
|
46
|
+
end
|
47
|
+
|
48
|
+
def smaller_than_base?
|
49
|
+
number.to_i < base
|
50
|
+
end
|
51
|
+
|
52
|
+
def base
|
53
|
+
opts[:prefix] == :si ? 1000 : 1024
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|