elch_scan 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+

|
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
|
+
|