jekyll-uj-powertools 1.3.1 → 1.4.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/README.md +23 -5
- data/jekyll-uj-powertools.gemspec +1 -2
- data/lib/filters/main.rb +114 -0
- data/lib/generators/inject-properties.rb +8 -1
- data/lib/jekyll-uj-powertools.rb +5 -65
- data/lib/tags/ifistruthy.rb +161 -0
- metadata +5 -8
- data/lib/_old/generators/translate-pages.rb +0 -223
- data/lib/_old/hooks/translate-pages.rb +0 -253
- data/lib/jekyll-uj-powertools/version.rb +0 -5
- data/spec/jekyll-uj-powertools_spec.rb +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf2e2fcda199ac3fbca2062352312b94e177a99193a85ee3ebfdfae4315063c2
|
4
|
+
data.tar.gz: 6036a0be11527263de051855fe4015338db44f4c8d6b7227dc64bda174fe7b4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 180f0afccbbe441fbfdc32e1340893de4e0dea5c4adf688eef606bfadd92a7c4515857e699115204f4c62ffce0412e5170cb8187a86d6a14ae88d9abf13d27f7
|
7
|
+
data.tar.gz: 2f71ba1926df6fde7f6d0f1ebd0cc506a9ec5ccf76fc3350aabbecac25d58c26e720e757122288a7e2aae0a5c84ef4ced76371c89a1abe3a2d70165faade6383
|
data/README.md
CHANGED
@@ -46,8 +46,9 @@ gem install jekyll-uj-powertools
|
|
46
46
|
```
|
47
47
|
|
48
48
|
## ⚡️ Usage
|
49
|
-
Now you can use the
|
49
|
+
Now you can use all the custom filters and variables provided by `jekyll-uj-powertools` in your Jekyll site.
|
50
50
|
|
51
|
+
## Filters
|
51
52
|
### `uj_strip_ads` Filter
|
52
53
|
Remove ads from a string, such as a blog post or article.
|
53
54
|
|
@@ -69,11 +70,26 @@ Convert a string to title case.
|
|
69
70
|
{{ "hello world" | uj_title_case }}
|
70
71
|
```
|
71
72
|
|
72
|
-
|
73
|
-
|
73
|
+
## Global Variables
|
74
|
+
### `site.uj.cache_breaker` Variable
|
75
|
+
Use the `site.uj.cache_breaker` variable to append a cache-busting query parameter to your assets.
|
74
76
|
|
75
77
|
```liquid
|
76
|
-
<link rel="stylesheet" href="{{ "/assets/css/style.css" | prepend: site.baseurl }}?v={{ uj.cache_breaker }}">
|
78
|
+
<link rel="stylesheet" href="{{ "/assets/css/style.css" | prepend: site.baseurl }}?v={{ site.uj.cache_breaker }}">
|
79
|
+
```
|
80
|
+
|
81
|
+
### Page Variables
|
82
|
+
### `page.random_id` Variable
|
83
|
+
Generate a random ID for each page, useful for sorting randomly or for unique identifiers.
|
84
|
+
|
85
|
+
### `page.extension` Variable
|
86
|
+
Get the file extension of the current page, useful for determining how to process or display the page.
|
87
|
+
|
88
|
+
### `page.layout_data` Variable
|
89
|
+
Access the layout data of the current page, which can be useful for debugging or displaying layout-specific information.
|
90
|
+
|
91
|
+
```liquid
|
92
|
+
{{ page.extension }}
|
77
93
|
```
|
78
94
|
|
79
95
|
These examples show how you can use the features of `jekyll-uj-powertools` in your Jekyll site.
|
@@ -81,7 +97,9 @@ These examples show how you can use the features of `jekyll-uj-powertools` in yo
|
|
81
97
|
## 🔧 Development
|
82
98
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
83
99
|
|
84
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
100
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
101
|
+
|
102
|
+
To release a new version, update the version number in the `.gemspec` and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
85
103
|
|
86
104
|
## ⚠️ Testing
|
87
105
|
Run the tests
|
@@ -1,12 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'jekyll-uj-powertools/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
6
|
# Gem info
|
8
7
|
spec.name = "jekyll-uj-powertools"
|
9
|
-
spec.version =
|
8
|
+
spec.version = "1.4.0"
|
10
9
|
|
11
10
|
# Author info
|
12
11
|
spec.authors = ["ITW Creative Works"]
|
data/lib/filters/main.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# Libraries
|
2
|
+
require "jekyll"
|
3
|
+
|
4
|
+
# Module
|
5
|
+
module Jekyll
|
6
|
+
module UJPowertools
|
7
|
+
# Initialize a timestamp that will remain consistent across calls
|
8
|
+
@cache_timestamp = Time.now.to_i.to_s
|
9
|
+
|
10
|
+
# Strip ads from the input
|
11
|
+
def uj_strip_ads(input)
|
12
|
+
input
|
13
|
+
# Remove HTML <ad-units>
|
14
|
+
.gsub(/\s*<ad-unit>[\s\S]*?<\/ad-unit>\s*/m, '')
|
15
|
+
# Remove includes starting with "/master/modules/adunits/"
|
16
|
+
.gsub(/\s*\{% include \/master\/modules\/adunits\/.*? %\}\s*/m, '')
|
17
|
+
end
|
18
|
+
|
19
|
+
# Escape a string for use in JSON
|
20
|
+
# def uj_json_escape(value)
|
21
|
+
# value
|
22
|
+
# .gsub('\\', '\\\\') # Escape backslashes
|
23
|
+
# .gsub('"', '\"') # Escape double quotes
|
24
|
+
# .gsub("\b", '\\b') # Escape backspace
|
25
|
+
# .gsub("\f", '\\f') # Escape formfeed
|
26
|
+
# .gsub("\n", '\\n') # Escape newline
|
27
|
+
# .gsub("\r", '\\r') # Escape carriage return
|
28
|
+
# .gsub("\t", '\\t') # Escape tab
|
29
|
+
# end
|
30
|
+
def uj_json_escape(value)
|
31
|
+
value.to_json[1..-2] # Convert to JSON and remove the surrounding quotes
|
32
|
+
end
|
33
|
+
|
34
|
+
# Increment a global counter that can be accessed from any page then return the new value
|
35
|
+
# def uj_increment_return(input)
|
36
|
+
# @context.registers[:uj_incremental_return] ||= 0
|
37
|
+
# @context.registers[:uj_incremental_return]
|
38
|
+
# @context.registers[:uj_incremental_return] += input
|
39
|
+
# end
|
40
|
+
def uj_increment_return(input)
|
41
|
+
@context ||= { registers: {} }
|
42
|
+
@context[:registers][:uj_incremental_return] ||= 0
|
43
|
+
@context[:registers][:uj_incremental_return] += input
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return a random number between 0 and the input
|
47
|
+
def uj_random(input)
|
48
|
+
rand(input)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return the current year
|
52
|
+
def uj_year(input)
|
53
|
+
Time.now.year
|
54
|
+
end
|
55
|
+
|
56
|
+
# Title case
|
57
|
+
def uj_title_case(input)
|
58
|
+
input.split(' ').map(&:capitalize).join(' ')
|
59
|
+
end
|
60
|
+
|
61
|
+
# Check if a value is truthy (not nil, empty string, or 'null')
|
62
|
+
# def uj_istruthy(input)
|
63
|
+
# return false if input.nil?
|
64
|
+
# return false if input.respond_to?(:empty?) && input.empty?
|
65
|
+
# return false if input.to_s.downcase == 'null'
|
66
|
+
# return false if input == false
|
67
|
+
# true
|
68
|
+
# end
|
69
|
+
|
70
|
+
# Accessor for the consistent timestamp
|
71
|
+
def self.cache_timestamp
|
72
|
+
@cache_timestamp
|
73
|
+
end
|
74
|
+
|
75
|
+
# Check if a string ends with a specific suffix
|
76
|
+
# def uj_ends_with(input, suffix)
|
77
|
+
# input.end_with?(suffix)
|
78
|
+
# end
|
79
|
+
|
80
|
+
# Format content based on file extension - apply liquify and markdownify for .md files
|
81
|
+
def uj_content_format(input)
|
82
|
+
# Get the current page from context
|
83
|
+
page = @context.registers[:page] if @context.respond_to?(:registers)
|
84
|
+
page ||= @context[:registers][:page] if @context.is_a?(Hash)
|
85
|
+
|
86
|
+
# Apply liquify first
|
87
|
+
liquified = if @context.respond_to?(:registers)
|
88
|
+
Liquid::Template.parse(input).render(@context)
|
89
|
+
else
|
90
|
+
Liquid::Template.parse(input).render(@context[:registers] || {})
|
91
|
+
end
|
92
|
+
|
93
|
+
# Check if the page extension is .md
|
94
|
+
if page && page['extension'] == '.md'
|
95
|
+
# Apply markdownify for markdown files
|
96
|
+
site = @context.registers[:site] if @context.respond_to?(:registers)
|
97
|
+
site ||= @context[:registers][:site] if @context.is_a?(Hash)
|
98
|
+
|
99
|
+
if site
|
100
|
+
converter = site.find_converter_instance(Jekyll::Converters::Markdown)
|
101
|
+
converter.convert(liquified)
|
102
|
+
else
|
103
|
+
liquified
|
104
|
+
end
|
105
|
+
else
|
106
|
+
# Return just liquified content for non-markdown files
|
107
|
+
liquified
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Register the filter
|
114
|
+
Liquid::Template.register_filter(Jekyll::UJPowertools)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# Libraries
|
2
2
|
# ...
|
3
3
|
|
4
|
+
# Module
|
4
5
|
module Jekyll
|
5
6
|
class InjectData < Generator
|
6
7
|
safe true
|
@@ -29,7 +30,13 @@ module Jekyll
|
|
29
30
|
# Inject a random number into the item's data
|
30
31
|
item.data['random_id'] = rand(100) # Random number between 0 and 99
|
31
32
|
|
32
|
-
|
33
|
+
# Inject the file extension into the item's data
|
34
|
+
if item.respond_to?(:path)
|
35
|
+
item.data['extension'] = File.extname(item.path)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Skip items without layouts
|
39
|
+
return unless item.data['layout']
|
33
40
|
|
34
41
|
# Find the layout file by its name
|
35
42
|
layout_name = item.data['layout']
|
data/lib/jekyll-uj-powertools.rb
CHANGED
@@ -1,76 +1,16 @@
|
|
1
1
|
# Libraries
|
2
2
|
require "jekyll"
|
3
3
|
|
4
|
-
# Module
|
5
4
|
module Jekyll
|
6
|
-
|
7
|
-
|
8
|
-
@cache_timestamp = Time.now.to_i.to_s
|
9
|
-
|
10
|
-
# Strip ads from the input
|
11
|
-
def uj_strip_ads(input)
|
12
|
-
input
|
13
|
-
# Remove HTML <ad-units>
|
14
|
-
.gsub(/\s*<ad-unit>[\s\S]*?<\/ad-unit>\s*/m, '')
|
15
|
-
# Remove includes starting with "/master/modules/adunits/"
|
16
|
-
.gsub(/\s*\{% include \/master\/modules\/adunits\/.*? %\}\s*/m, '')
|
17
|
-
end
|
18
|
-
|
19
|
-
# Escape a string for use in JSON
|
20
|
-
# def uj_json_escape(value)
|
21
|
-
# value
|
22
|
-
# .gsub('\\', '\\\\') # Escape backslashes
|
23
|
-
# .gsub('"', '\"') # Escape double quotes
|
24
|
-
# .gsub("\b", '\\b') # Escape backspace
|
25
|
-
# .gsub("\f", '\\f') # Escape formfeed
|
26
|
-
# .gsub("\n", '\\n') # Escape newline
|
27
|
-
# .gsub("\r", '\\r') # Escape carriage return
|
28
|
-
# .gsub("\t", '\\t') # Escape tab
|
29
|
-
# end
|
30
|
-
def uj_json_escape(value)
|
31
|
-
value.to_json[1..-2] # Convert to JSON and remove the surrounding quotes
|
32
|
-
end
|
33
|
-
|
34
|
-
# Increment a global counter that can be accessed from any page then return the new value
|
35
|
-
# def uj_increment_return(input)
|
36
|
-
# @context.registers[:uj_incremental_return] ||= 0
|
37
|
-
# @context.registers[:uj_incremental_return]
|
38
|
-
# @context.registers[:uj_incremental_return] += input
|
39
|
-
# end
|
40
|
-
def uj_increment_return(input)
|
41
|
-
@context ||= { registers: {} }
|
42
|
-
@context[:registers][:uj_incremental_return] ||= 0
|
43
|
-
@context[:registers][:uj_incremental_return] += input
|
44
|
-
end
|
45
|
-
|
46
|
-
# Return a random number between 0 and the input
|
47
|
-
def uj_random(input)
|
48
|
-
rand(input)
|
49
|
-
end
|
50
|
-
|
51
|
-
# Return the current year
|
52
|
-
def uj_year(input)
|
53
|
-
Time.now.year
|
54
|
-
end
|
55
|
-
|
56
|
-
# Title case
|
57
|
-
def uj_title_case(input)
|
58
|
-
input.split(' ').map(&:capitalize).join(' ')
|
59
|
-
end
|
60
|
-
|
61
|
-
# Accessor for the consistent timestamp
|
62
|
-
def self.cache_timestamp
|
63
|
-
@cache_timestamp
|
64
|
-
end
|
65
|
-
end
|
5
|
+
# Load Filters
|
6
|
+
require_relative "filters/main"
|
66
7
|
|
67
8
|
# Load Generators
|
68
9
|
require_relative "generators/inject-properties"
|
69
10
|
|
70
11
|
# Load Hooks
|
71
12
|
require_relative "hooks/inject-properties"
|
72
|
-
# require_relative "hooks/translate-pages"
|
73
|
-
end
|
74
13
|
|
75
|
-
#
|
76
|
-
|
14
|
+
# Load Tags
|
15
|
+
# require_relative "tags/ifistruthy"
|
16
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Jekyll
|
2
|
+
module UJPowertools
|
3
|
+
class IfIsTruthyTag < Liquid::Block
|
4
|
+
Syntax = /(\w+)/
|
5
|
+
|
6
|
+
def initialize(tag_name, markup, tokens)
|
7
|
+
super
|
8
|
+
|
9
|
+
if markup =~ Syntax
|
10
|
+
@variable_name = $1
|
11
|
+
else
|
12
|
+
raise SyntaxError, "Invalid syntax for ifistruthy tag. Usage: {% ifistruthy variable_name %}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(context)
|
17
|
+
variable = context[@variable_name]
|
18
|
+
is_truthy = check_truthy(variable)
|
19
|
+
|
20
|
+
# Split content at else tag
|
21
|
+
else_index = nil
|
22
|
+
@nodelist.each_with_index do |node, index|
|
23
|
+
if node.respond_to?(:tag_name) && node.tag_name == 'else'
|
24
|
+
else_index = index
|
25
|
+
break
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
if is_truthy
|
30
|
+
if else_index
|
31
|
+
render_nodelist(@nodelist[0...else_index], context)
|
32
|
+
else
|
33
|
+
super(context)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
if else_index
|
37
|
+
render_nodelist(@nodelist[(else_index + 1)..-1], context)
|
38
|
+
else
|
39
|
+
''
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def unknown_tag(tag_name, markup, tokens)
|
45
|
+
if tag_name == 'else'
|
46
|
+
@nodelist << Liquid::ElseTag.new(tag_name, markup, tokens)
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def check_truthy(value)
|
55
|
+
return false if value.nil?
|
56
|
+
return false if value.respond_to?(:empty?) && value.empty?
|
57
|
+
return false if value.to_s.downcase == 'null'
|
58
|
+
return false if value == false
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
def render_nodelist(nodelist, context)
|
63
|
+
output = []
|
64
|
+
nodelist.each do |token|
|
65
|
+
case token
|
66
|
+
when String
|
67
|
+
output << token
|
68
|
+
else
|
69
|
+
if token.respond_to?(:render)
|
70
|
+
output << token.render(context)
|
71
|
+
else
|
72
|
+
output << token.to_s
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
output.join
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class UnlessIsTruthyTag < Liquid::Block
|
81
|
+
Syntax = /(\w+)/
|
82
|
+
|
83
|
+
def initialize(tag_name, markup, tokens)
|
84
|
+
super
|
85
|
+
|
86
|
+
if markup =~ Syntax
|
87
|
+
@variable_name = $1
|
88
|
+
else
|
89
|
+
raise SyntaxError, "Invalid syntax for unlessistruthy tag. Usage: {% unlessistruthy variable_name %}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def render(context)
|
94
|
+
variable = context[@variable_name]
|
95
|
+
is_truthy = check_truthy(variable)
|
96
|
+
|
97
|
+
# Split content at else tag
|
98
|
+
else_index = nil
|
99
|
+
@nodelist.each_with_index do |node, index|
|
100
|
+
if node.respond_to?(:tag_name) && node.tag_name == 'else'
|
101
|
+
else_index = index
|
102
|
+
break
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if !is_truthy
|
107
|
+
if else_index
|
108
|
+
render_nodelist(@nodelist[0...else_index], context)
|
109
|
+
else
|
110
|
+
super(context)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
if else_index
|
114
|
+
render_nodelist(@nodelist[(else_index + 1)..-1], context)
|
115
|
+
else
|
116
|
+
''
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def unknown_tag(tag_name, markup, tokens)
|
122
|
+
if tag_name == 'else'
|
123
|
+
@nodelist << Liquid::ElseTag.new(tag_name, markup, tokens)
|
124
|
+
else
|
125
|
+
super
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def check_truthy(value)
|
132
|
+
return false if value.nil?
|
133
|
+
return false if value.respond_to?(:empty?) && value.empty?
|
134
|
+
return false if value.to_s.downcase == 'null'
|
135
|
+
return false if value == false
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
def render_nodelist(nodelist, context)
|
140
|
+
output = []
|
141
|
+
nodelist.each do |token|
|
142
|
+
case token
|
143
|
+
when String
|
144
|
+
output << token
|
145
|
+
else
|
146
|
+
if token.respond_to?(:render)
|
147
|
+
output << token.render(context)
|
148
|
+
else
|
149
|
+
output << token.to_s
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
output.join
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Register the tags
|
160
|
+
Liquid::Template.register_tag('ifistruthy', Jekyll::UJPowertools::IfIsTruthyTag)
|
161
|
+
Liquid::Template.register_tag('unlessistruthy', Jekyll::UJPowertools::UnlessIsTruthyTag)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-uj-powertools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ITW Creative Works
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jekyll
|
@@ -99,13 +99,11 @@ files:
|
|
99
99
|
- README.md
|
100
100
|
- Rakefile
|
101
101
|
- jekyll-uj-powertools.gemspec
|
102
|
-
- lib/
|
103
|
-
- lib/_old/hooks/translate-pages.rb
|
102
|
+
- lib/filters/main.rb
|
104
103
|
- lib/generators/inject-properties.rb
|
105
104
|
- lib/hooks/inject-properties.rb
|
106
105
|
- lib/jekyll-uj-powertools.rb
|
107
|
-
- lib/
|
108
|
-
- spec/jekyll-uj-powertools_spec.rb
|
106
|
+
- lib/tags/ifistruthy.rb
|
109
107
|
homepage: https://github.com/itw-creative-works/jekyll-uj-powertools
|
110
108
|
licenses:
|
111
109
|
- MIT
|
@@ -129,5 +127,4 @@ rubygems_version: 3.2.3
|
|
129
127
|
signing_key:
|
130
128
|
specification_version: 4
|
131
129
|
summary: A powerful set of utilities for Jekyll
|
132
|
-
test_files:
|
133
|
-
- spec/jekyll-uj-powertools_spec.rb
|
130
|
+
test_files: []
|
@@ -1,223 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'net/http'
|
3
|
-
require 'fileutils'
|
4
|
-
require 'nokogiri'
|
5
|
-
require 'digest'
|
6
|
-
|
7
|
-
module Jekyll
|
8
|
-
class TranslatePages < Generator
|
9
|
-
safe true
|
10
|
-
priority :low
|
11
|
-
|
12
|
-
# Variables
|
13
|
-
# Translation path
|
14
|
-
CACHE_DIR = '.temp/translations'
|
15
|
-
# Re-translate pages older than this many days
|
16
|
-
RECHECK_DAYS = 30
|
17
|
-
|
18
|
-
def generate(site)
|
19
|
-
# Get target languages from site config
|
20
|
-
target_langs = site.config.dig('translation', 'languages') || []
|
21
|
-
|
22
|
-
# Log
|
23
|
-
puts "🔁 Starting translation process for supported languages (#{target_langs.length}): #{target_langs.join(', ')}"
|
24
|
-
# puts "📂 Cache directory: #{CACHE_DIR}"
|
25
|
-
# puts "🔍 All environment variables:"
|
26
|
-
# ENV.each { |k, v| puts " #{k}=#{v}" }
|
27
|
-
puts "🔍 UJ_ environment variables:"
|
28
|
-
ENV.select { |k, _| k.start_with?('UJ_') }.each { |k, v| puts " #{k}=#{v}" }
|
29
|
-
|
30
|
-
# Skip if site config translation is disabled
|
31
|
-
unless site.config.dig('translation', 'enabled')
|
32
|
-
puts "🚫 Translation is disabled in _config.yml (translation.enabled: false)"
|
33
|
-
return
|
34
|
-
end
|
35
|
-
|
36
|
-
# Quit if UJ_BUILD_MODE is false
|
37
|
-
if ENV['UJ_BUILD_MODE'] == 'false' && ENV['UJ_TRANSLATION_FORCE'] != 'true'
|
38
|
-
puts "🚫 UJ_BUILD_MODE is set to 'false' (set UJ_TRANSLATION_FORCE=true). Exiting translation process."
|
39
|
-
return
|
40
|
-
end
|
41
|
-
|
42
|
-
# Ensure OpenAI API key is set
|
43
|
-
unless ENV['OPENAI_API_KEY'] && !ENV['OPENAI_API_KEY'].strip.empty?
|
44
|
-
puts "❌ OPENAI_API_KEY not found in environment. Exiting translation process."
|
45
|
-
return
|
46
|
-
end
|
47
|
-
|
48
|
-
# Quit if no languages are configured
|
49
|
-
if target_langs.empty?
|
50
|
-
puts "🚫 No target languages configured in _config.yml (translation.languages). Exiting translation process."
|
51
|
-
return
|
52
|
-
end
|
53
|
-
|
54
|
-
# Keep track of skipped files
|
55
|
-
skipped_files = []
|
56
|
-
|
57
|
-
# Loop through all pages in the site
|
58
|
-
site.pages.clone.each do |page|
|
59
|
-
next unless page.output_ext == '.html'
|
60
|
-
original_content = page.content
|
61
|
-
original_hash = Digest::SHA256.hexdigest(original_content)
|
62
|
-
page_path = page.path.sub(/^_?site\//, '')
|
63
|
-
|
64
|
-
# Skip if page.translation.enabled is false
|
65
|
-
if page.data['translation'] && page.data['translation']['enabled'] == false
|
66
|
-
skipped_files << "#{page_path} (translation disabled)"
|
67
|
-
next
|
68
|
-
end
|
69
|
-
|
70
|
-
# Skip if page.redirect.url is set
|
71
|
-
if page.data['redirect'] && page.data['redirect']['url']
|
72
|
-
skipped_files << "#{page_path} (redirect set)"
|
73
|
-
next
|
74
|
-
end
|
75
|
-
|
76
|
-
target_langs.each do |lang|
|
77
|
-
translated_path = File.join(CACHE_DIR, lang, page_path)
|
78
|
-
meta_path = "#{translated_path}.meta.json"
|
79
|
-
|
80
|
-
# @TODO: Remove this
|
81
|
-
# Unless its pages/legal/terms.md, QUIT
|
82
|
-
uj_translation_only = ENV['UJ_TRANSLATION_ONLY']
|
83
|
-
if uj_translation_only && page_path != uj_translation_only
|
84
|
-
skipped_files << "#{page_path} (UJ_TRANSLATION_ONLY is set)"
|
85
|
-
next
|
86
|
-
end
|
87
|
-
|
88
|
-
# Log
|
89
|
-
puts "🌐 Processing page '#{page_path}' for language '#{lang}'"
|
90
|
-
|
91
|
-
# Either read cached translation or generate a new one
|
92
|
-
translated = read_or_translate(original_content, original_hash, lang, translated_path, meta_path)
|
93
|
-
|
94
|
-
next unless translated # skip this lang if translation failed
|
95
|
-
|
96
|
-
# Build new page with translated content
|
97
|
-
new_page = page.dup
|
98
|
-
new_page.data = page.data.dup
|
99
|
-
new_page.data['lang'] = lang
|
100
|
-
new_page.data['permalink'] = "/#{lang}#{page.url}"
|
101
|
-
new_page.content = rewrite_links(translated, lang)
|
102
|
-
|
103
|
-
site.pages << new_page
|
104
|
-
puts "✅ Added translated page: /#{lang}#{page.url}"
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
# Log skipped files at the end
|
109
|
-
if skipped_files.any?
|
110
|
-
puts "\n🚫 Skipped files:"
|
111
|
-
skipped_files.each { |f| puts " - #{f}" }
|
112
|
-
end
|
113
|
-
|
114
|
-
puts "🎉 Translation process complete."
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
# Return cached translation or generate new one via API
|
120
|
-
def read_or_translate(content, hash, lang, path, meta_path)
|
121
|
-
if File.exist?(path) && File.exist?(meta_path)
|
122
|
-
meta = JSON.parse(File.read(meta_path)) rescue {}
|
123
|
-
last_hash = meta['hash']
|
124
|
-
last_time = Time.at(meta['timestamp'].to_i) rescue Time.at(0)
|
125
|
-
|
126
|
-
age_days = ((Time.now - last_time) / (60 * 60 * 24)).round
|
127
|
-
puts "📅 Cache age: #{age_days}/#{RECHECK_DAYS} days"
|
128
|
-
|
129
|
-
# Determine whether we should re-check based on age
|
130
|
-
recheck_due_to_age = RECHECK_DAYS && RECHECK_DAYS > 0 && age_days >= RECHECK_DAYS
|
131
|
-
|
132
|
-
# Re-translate if hash changed or translation is too old (only if RECHECK_DAYS is truthy)
|
133
|
-
if last_hash == hash && !recheck_due_to_age
|
134
|
-
puts "📦 Using cached translation at: #{path}"
|
135
|
-
return File.read(path)
|
136
|
-
else
|
137
|
-
puts "🔁 Cache stale or hash changed, regenerating translation..."
|
138
|
-
end
|
139
|
-
else
|
140
|
-
puts "📭 No cache found, generating translation..."
|
141
|
-
end
|
142
|
-
|
143
|
-
# Log before/after content
|
144
|
-
puts "\n--- BEFORE CONTENT (#{lang}) ---\n#{content[0..500]}..."
|
145
|
-
|
146
|
-
# Translate the content using OpenAI API
|
147
|
-
begin
|
148
|
-
result = translate_with_api(content, lang)
|
149
|
-
rescue => e
|
150
|
-
puts "❌ Skipping translation for '#{lang}' due to error: #{e.message}"
|
151
|
-
return nil
|
152
|
-
end
|
153
|
-
|
154
|
-
# Log the first 500 characters of the result
|
155
|
-
puts "\n--- AFTER TRANSLATION (#{lang}) ---\n#{result[0..500]}..."
|
156
|
-
|
157
|
-
# Save the translation and metadata
|
158
|
-
FileUtils.mkdir_p(File.dirname(path))
|
159
|
-
File.write(path, result)
|
160
|
-
File.write(meta_path, {
|
161
|
-
timestamp: Time.now.to_i,
|
162
|
-
hash: hash
|
163
|
-
}.to_json)
|
164
|
-
|
165
|
-
puts "📝 Cached translation and metadata written to: #{path}"
|
166
|
-
|
167
|
-
result
|
168
|
-
end
|
169
|
-
|
170
|
-
# Perform translation via OpenAI API
|
171
|
-
def translate_with_api(content, lang)
|
172
|
-
system_prompt = "You are a professional translator. Translate the provided HTML content, preserving all original formatting, HTML structure, metadata, and links. Do not explain anything — just return the translated HTML. Translate to #{lang}."
|
173
|
-
user_message = content
|
174
|
-
|
175
|
-
uri = URI('https://api.openai.com/v1/chat/completions')
|
176
|
-
|
177
|
-
res = Net::HTTP.post(
|
178
|
-
uri,
|
179
|
-
{
|
180
|
-
model: 'gpt-4',
|
181
|
-
messages: [
|
182
|
-
{ role: 'system', content: system_prompt },
|
183
|
-
{ role: 'user', content: user_message }
|
184
|
-
],
|
185
|
-
temperature: 0.3
|
186
|
-
}.to_json,
|
187
|
-
{
|
188
|
-
'Authorization' => "Bearer #{ENV['OPENAI_API_KEY']}",
|
189
|
-
'Content-Type' => 'application/json'
|
190
|
-
}
|
191
|
-
)
|
192
|
-
|
193
|
-
if res.code.to_i != 200
|
194
|
-
raise "HTTP #{res.code}: #{res.body}"
|
195
|
-
end
|
196
|
-
|
197
|
-
json = JSON.parse(res.body)
|
198
|
-
# Log json
|
199
|
-
puts "🔍 API response: #{json.inspect}"
|
200
|
-
result = json.dig('choices', 0, 'message', 'content')
|
201
|
-
|
202
|
-
if result.nil? || result.strip.empty?
|
203
|
-
raise "Translation returned empty or invalid content"
|
204
|
-
end
|
205
|
-
|
206
|
-
puts "🔤 Translation complete."
|
207
|
-
result
|
208
|
-
end
|
209
|
-
|
210
|
-
# Rewrite internal links to language-prefixed versions
|
211
|
-
def rewrite_links(html, lang)
|
212
|
-
doc = Nokogiri::HTML::DocumentFragment.parse(html)
|
213
|
-
doc.css('a[href^="/"]').each do |a|
|
214
|
-
href = a['href']
|
215
|
-
next if href.start_with?("/#{lang}") || href.include?('.') || href.start_with?('//')
|
216
|
-
new_href = "/#{lang}#{href}"
|
217
|
-
puts "🔗 Rewriting link: #{href} -> #{new_href}"
|
218
|
-
a['href'] = new_href
|
219
|
-
end
|
220
|
-
doc.to_html
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
@@ -1,253 +0,0 @@
|
|
1
|
-
# Libraries
|
2
|
-
require 'json'
|
3
|
-
require 'net/http'
|
4
|
-
require 'fileutils'
|
5
|
-
require 'nokogiri'
|
6
|
-
require 'digest'
|
7
|
-
|
8
|
-
# Hook
|
9
|
-
Jekyll::Hooks.register :site, :post_write do |site|
|
10
|
-
# Variables
|
11
|
-
# Translation path
|
12
|
-
CACHE_DIR = '.temp/translations'
|
13
|
-
# Re-translate pages older than this many days
|
14
|
-
RECHECK_DAYS = 30
|
15
|
-
|
16
|
-
# Get target languages from site config
|
17
|
-
target_langs = site.config.dig('translation', 'languages') || []
|
18
|
-
|
19
|
-
# Log
|
20
|
-
puts "🔁 Starting translation process for supported languages (#{target_langs.length}): #{target_langs.join(', ')}"
|
21
|
-
puts "🔍 UJ_ environment variables:"
|
22
|
-
ENV.select { |k, _| k.start_with?('UJ_') }.each { |k, v| puts " #{k}=#{v}" }
|
23
|
-
|
24
|
-
# Skip if site config translation is disabled
|
25
|
-
unless site.config.dig('translation', 'enabled')
|
26
|
-
puts "🚫 Translation is disabled in _config.yml (translation.enabled: false)"
|
27
|
-
next
|
28
|
-
end
|
29
|
-
|
30
|
-
# Quit if UJ_BUILD_MODE is false
|
31
|
-
if ENV['UJ_BUILD_MODE'] == 'false' && ENV['UJ_TRANSLATION_FORCE'] != 'true'
|
32
|
-
puts "🚫 UJ_BUILD_MODE is set to 'false' (set UJ_TRANSLATION_FORCE=true). Exiting translation process."
|
33
|
-
next
|
34
|
-
end
|
35
|
-
|
36
|
-
# Ensure OpenAI API key is set
|
37
|
-
unless ENV['OPENAI_API_KEY'] && !ENV['OPENAI_API_KEY'].strip.empty?
|
38
|
-
puts "❌ OPENAI_API_KEY not found in environment. Exiting translation process."
|
39
|
-
next
|
40
|
-
end
|
41
|
-
|
42
|
-
# Quit if no languages are configured
|
43
|
-
if target_langs.empty?
|
44
|
-
puts "🚫 No target languages configured in _config.yml (translation.languages). Exiting translation process."
|
45
|
-
next
|
46
|
-
end
|
47
|
-
|
48
|
-
# Keep track of skipped files
|
49
|
-
skipped_files = []
|
50
|
-
|
51
|
-
# Loop through all pages in the site
|
52
|
-
site.pages.clone.each do |page|
|
53
|
-
# Quit if its not an HTML page
|
54
|
-
next unless page.output_ext == '.html'
|
55
|
-
|
56
|
-
# Get original content
|
57
|
-
original_content = page.output
|
58
|
-
|
59
|
-
# Extract body content
|
60
|
-
doc = Nokogiri::HTML(original_content)
|
61
|
-
original_content_body = doc.at('body')&.inner_html.to_s
|
62
|
-
|
63
|
-
# Compute original hash
|
64
|
-
original_hash = Digest::SHA256.hexdigest(original_content_body)
|
65
|
-
|
66
|
-
# Get page path and URL
|
67
|
-
page_path = page.path.sub(/^_?site\//, '')
|
68
|
-
page_url = page.url
|
69
|
-
|
70
|
-
# Skip if page.translation.enabled is false
|
71
|
-
if page.data['translation'] && page.data['translation']['enabled'] == false
|
72
|
-
skipped_files << "#{page_path} (translation disabled)"
|
73
|
-
next
|
74
|
-
end
|
75
|
-
|
76
|
-
# Skip if page.redirect.url is set
|
77
|
-
if page.data['redirect'] && page.data['redirect']['url']
|
78
|
-
skipped_files << "#{page_path} (redirect set)"
|
79
|
-
next
|
80
|
-
end
|
81
|
-
|
82
|
-
# Loop through target languages
|
83
|
-
target_langs.each do |lang|
|
84
|
-
page_new_url = "/#{lang}#{page.url}"
|
85
|
-
page_new_path = File.join(CACHE_DIR, lang, page_new_url)
|
86
|
-
page_new_meta_path = "#{page_new_path}.meta.json"
|
87
|
-
|
88
|
-
# See if we only want to test a specific page
|
89
|
-
uj_translation_only = ENV['UJ_TRANSLATION_ONLY']
|
90
|
-
if uj_translation_only && page_path != uj_translation_only
|
91
|
-
skipped_files << "#{page_path} (UJ_TRANSLATION_ONLY is set)"
|
92
|
-
next
|
93
|
-
end
|
94
|
-
|
95
|
-
# Log
|
96
|
-
puts "🌐 Processing page '#{page_url}' for language '#{lang}'"
|
97
|
-
|
98
|
-
# LOG new_page.data
|
99
|
-
# Log permalink
|
100
|
-
puts "🔗 New permalink: #{page_new_url}"
|
101
|
-
|
102
|
-
# Either read cached translation or generate a new one
|
103
|
-
translated = read_or_translate(original_content_body, original_hash, lang, page_new_path, page_new_meta_path)
|
104
|
-
|
105
|
-
# Fallback if translation failed
|
106
|
-
if translated.nil?
|
107
|
-
puts "⚠️ Translation failed for #{page_url}, using original content and marking for retry"
|
108
|
-
|
109
|
-
# Force a retry next time by setting bad hash + old timestamp
|
110
|
-
FileUtils.mkdir_p(File.dirname(page_new_meta_path))
|
111
|
-
File.write(page_new_meta_path, {
|
112
|
-
timestamp: 0,
|
113
|
-
hash: '__fail__'
|
114
|
-
}.to_json)
|
115
|
-
|
116
|
-
translated = original_content_body
|
117
|
-
end
|
118
|
-
|
119
|
-
# Rewrite internal links
|
120
|
-
translated_html = rewrite_links(translated, lang)
|
121
|
-
|
122
|
-
# Inject translated content into original HTML structure
|
123
|
-
translated_doc = Nokogiri::HTML(original_content)
|
124
|
-
translated_doc.at('body').inner_html = translated_html
|
125
|
-
final_html = translated_doc.to_html
|
126
|
-
|
127
|
-
# Determine output path
|
128
|
-
output_dir = site.config['destination']
|
129
|
-
translated_output_path = File.join(output_dir, lang, page.url)
|
130
|
-
translated_output_path = File.join(translated_output_path, 'index.html') if translated_output_path.end_with?('/')
|
131
|
-
|
132
|
-
# Write translated page to disk
|
133
|
-
FileUtils.mkdir_p(File.dirname(translated_output_path))
|
134
|
-
File.write(translated_output_path, final_html)
|
135
|
-
puts "✅ Wrote translated file: #{translated_output_path}"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
# Log skipped files at the end
|
140
|
-
if skipped_files.any?
|
141
|
-
puts "\n🚫 Skipped files:"
|
142
|
-
skipped_files.each { |f| puts " - #{f}" }
|
143
|
-
end
|
144
|
-
|
145
|
-
# Log
|
146
|
-
puts "🎉 Translation process complete."
|
147
|
-
end
|
148
|
-
|
149
|
-
def read_or_translate(content, hash, lang, path, page_new_meta_path)
|
150
|
-
if File.exist?(path) && File.exist?(page_new_meta_path)
|
151
|
-
meta = JSON.parse(File.read(page_new_meta_path)) rescue {}
|
152
|
-
last_hash = meta['hash']
|
153
|
-
last_time = Time.at(meta['timestamp'].to_i) rescue Time.at(0)
|
154
|
-
|
155
|
-
age_days = ((Time.now - last_time) / (60 * 60 * 24)).round
|
156
|
-
puts "📅 Cache age: #{age_days}/#{RECHECK_DAYS} days"
|
157
|
-
|
158
|
-
# Determine whether we should re-check based on age
|
159
|
-
recheck_due_to_age = RECHECK_DAYS && RECHECK_DAYS > 0 && age_days >= RECHECK_DAYS
|
160
|
-
|
161
|
-
# Re-translate if hash changed or translation is too old (only if RECHECK_DAYS is truthy)
|
162
|
-
if last_hash == hash && !recheck_due_to_age
|
163
|
-
puts "📦 Using cached translation at: #{path}"
|
164
|
-
return File.read(path)
|
165
|
-
else
|
166
|
-
puts "🔁 Cache stale or hash changed, regenerating translation..."
|
167
|
-
end
|
168
|
-
else
|
169
|
-
puts "📭 No cache found, generating translation..."
|
170
|
-
end
|
171
|
-
|
172
|
-
# Log before/after content
|
173
|
-
puts "\n--- BEFORE CONTENT (#{lang}) ---\n#{content[0..500]}..."
|
174
|
-
|
175
|
-
# Translate the content using OpenAI API
|
176
|
-
begin
|
177
|
-
result = translate_with_api(content, lang)
|
178
|
-
rescue => e
|
179
|
-
puts "❌ Skipping translation for '#{lang}' due to error: #{e.message}"
|
180
|
-
return nil
|
181
|
-
end
|
182
|
-
|
183
|
-
# Log the first 500 characters of the result
|
184
|
-
puts "\n--- AFTER TRANSLATION (#{lang}) ---\n#{result[0..500]}..."
|
185
|
-
|
186
|
-
# Save the translation and metadata
|
187
|
-
FileUtils.mkdir_p(File.dirname(path))
|
188
|
-
File.write(path, result)
|
189
|
-
File.write(page_new_meta_path, {
|
190
|
-
timestamp: Time.now.to_i,
|
191
|
-
hash: hash
|
192
|
-
}.to_json)
|
193
|
-
|
194
|
-
puts "📝 Cached translation and metadata written to: #{path}"
|
195
|
-
|
196
|
-
result
|
197
|
-
end
|
198
|
-
|
199
|
-
def translate_with_api(content, lang)
|
200
|
-
system_prompt = "You are a professional translator. Translate the provided HTML content, preserving all original formatting, HTML structure, metadata, and links. Do not explain anything — just return the translated HTML. Translate to #{lang}."
|
201
|
-
user_message = content
|
202
|
-
|
203
|
-
uri = URI('https://api.openai.com/v1/chat/completions')
|
204
|
-
|
205
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
206
|
-
http.use_ssl = true
|
207
|
-
http.read_timeout = 30 # seconds
|
208
|
-
http.open_timeout = 10 # seconds
|
209
|
-
|
210
|
-
request = Net::HTTP::Post.new(uri.path, {
|
211
|
-
'Authorization' => "Bearer #{ENV['OPENAI_API_KEY']}",
|
212
|
-
'Content-Type' => 'application/json'
|
213
|
-
})
|
214
|
-
|
215
|
-
request.body = {
|
216
|
-
model: 'gpt-4o',
|
217
|
-
messages: [
|
218
|
-
{ role: 'system', content: system_prompt },
|
219
|
-
{ role: 'user', content: user_message }
|
220
|
-
],
|
221
|
-
temperature: 0.3,
|
222
|
-
max_tokens: 4096
|
223
|
-
}.to_json
|
224
|
-
|
225
|
-
response = http.request(request)
|
226
|
-
|
227
|
-
if response.code.to_i != 200
|
228
|
-
raise "HTTP #{response.code}: #{response.body}"
|
229
|
-
end
|
230
|
-
|
231
|
-
json = JSON.parse(response.body)
|
232
|
-
puts "🔍 API response: #{json.inspect}"
|
233
|
-
result = json.dig('choices', 0, 'message', 'content')
|
234
|
-
|
235
|
-
if result.nil? || result.strip.empty?
|
236
|
-
raise "Translation returned empty or invalid content"
|
237
|
-
end
|
238
|
-
|
239
|
-
puts "🔤 Translation complete."
|
240
|
-
result
|
241
|
-
end
|
242
|
-
|
243
|
-
def rewrite_links(html, lang)
|
244
|
-
doc = Nokogiri::HTML::DocumentFragment.parse(html)
|
245
|
-
doc.css('a[href^="/"]').each do |a|
|
246
|
-
href = a['href']
|
247
|
-
next if href.start_with?("/#{lang}") || href.include?('.') || href.start_with?('//')
|
248
|
-
new_href = "/#{lang}#{href}"
|
249
|
-
puts "🔗 Rewriting link: #{href} -> #{new_href}"
|
250
|
-
a['href'] = new_href
|
251
|
-
end
|
252
|
-
doc.to_html
|
253
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
require 'jekyll-uj-powertools'
|
2
|
-
|
3
|
-
RSpec.describe Jekyll::UJPowertools do
|
4
|
-
# Dummy class to include the filter methods
|
5
|
-
class DummyClass
|
6
|
-
include Jekyll::UJPowertools
|
7
|
-
end
|
8
|
-
|
9
|
-
let(:dummy) { DummyClass.new }
|
10
|
-
|
11
|
-
# Test Strip Ads method
|
12
|
-
describe '.uj_strip_ads' do
|
13
|
-
it 'removes ads from the string with custom HTML elements' do
|
14
|
-
expect(dummy.uj_strip_ads('This is <ad-unit>and ad</ad-unit>')).to eq('This is')
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'returns the original string if no ads are present' do
|
18
|
-
expect(dummy.uj_strip_ads('No ads here')).to eq('No ads here')
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'removes multiple ads from the string' do
|
22
|
-
expect(dummy.uj_strip_ads("First part\n<ad-unit>ad content</ad-unit>\nSecond part\n<ad-unit>more ad content</ad-unit>\nThird part")).to eq('First partSecond partThird part')
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'removes surrounding whitespace' do
|
26
|
-
expect(dummy.uj_strip_ads("Start\n<ad-unit>\n ad content\n</ad-unit>\nEnd")).to eq("StartEnd")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# Test JSON Escape method
|
31
|
-
describe '.uj_json_escape' do
|
32
|
-
it 'escapes double quotes in JSON string' do
|
33
|
-
expect(dummy.uj_json_escape('this is a "quote"')).to eq('this is a \"quote\"')
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'escapes backslashes in JSON string' do
|
37
|
-
expect(dummy.uj_json_escape('this is a nothing')).to eq('this is a nothing')
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# Test Increment Return method
|
42
|
-
describe '.uj_increment_return' do
|
43
|
-
it 'increments a global counter' do
|
44
|
-
expect(dummy.uj_increment_return(1)).to eq(1)
|
45
|
-
expect(dummy.uj_increment_return(1)).to eq(2)
|
46
|
-
expect(dummy.uj_increment_return(1)).to eq(3)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# Test Random method
|
51
|
-
describe '.uj_random' do
|
52
|
-
it 'returns a random number between 0 and the input' do
|
53
|
-
srand(1)
|
54
|
-
expect(dummy.uj_random(10)).to eq(5)
|
55
|
-
srand(2)
|
56
|
-
expect(dummy.uj_random(10)).to eq(8)
|
57
|
-
srand(3)
|
58
|
-
expect(dummy.uj_random(10)).to eq(8)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# Test Cache Buster method
|
63
|
-
describe '.uj_cache' do
|
64
|
-
it 'returns the current timestamp as a string' do
|
65
|
-
expect(dummy.uj_cache('unused')).to eq(Time.now.to_i.to_s)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Test Title Case method
|
70
|
-
describe '.uj_title_case' do
|
71
|
-
it 'capitalizes the first letter of each word' do
|
72
|
-
expect(dummy.uj_title_case('this is a title')).to eq('This Is A Title')
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|