rpictogrify 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rpictogrify
4
+ class Configuration
5
+ # default theme, one of these themes: avataars_female, avataars_male, male_flat, monsters. default is :monsters
6
+ attr_accessor :theme
7
+
8
+ # pictogram directory. default is 'public/system'
9
+ attr_accessor :base_path
10
+
11
+ def initialize
12
+ @theme = :monsters
13
+
14
+ @base_path = 'public/system'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rpictogrify/pictogram'
4
+
5
+ module Rpictogrify
6
+ module Generator
7
+ extend self
8
+
9
+ # example
10
+ # Rpictogrify::Generator.call 'jim', theme: :avataars_male
11
+ def call(text, options = {})
12
+ pictogram = Rpictogrify::Pictogram.new(text, options)
13
+ pictogram.generate
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rpictogrify
4
+ module Inflector
5
+ extend self
6
+
7
+ def underscore(camel_cased_word)
8
+ return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
9
+ word = camel_cased_word.to_s.gsub("::", "/")
10
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
11
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
12
+ word.tr!('-', '_')
13
+ word.downcase!
14
+ word
15
+ end
16
+
17
+ def camelize(term, uppercase_first_letter = true)
18
+ if uppercase_first_letter
19
+ term = term.sub(/^[a-z\d]*/) { |match| match.capitalize }
20
+ else
21
+ term = term.sub(/^(?:(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
22
+ end
23
+ term.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rpictogrify/theme'
4
+
5
+ module Rpictogrify
6
+ class Pictogram
7
+ # BUMP UP if avatar algorithm changes
8
+ VERSION = 1
9
+
10
+ attr_reader :text, :options, :theme, :uid
11
+
12
+ def initialize(text, options = {})
13
+ @text = text.to_s
14
+ @options = options
15
+ @theme = Rpictogrify::Theme.find(options[:theme] || Rpictogrify.config.theme)
16
+ @uid = generate_uid
17
+ end
18
+
19
+ def generate
20
+ File.write(path, svg) unless File.exist?(path)
21
+ path
22
+ end
23
+
24
+ def svg
25
+ includes = symbols.map do |shape, symbol|
26
+ fillable = fill[shape] ? "fill='#{fill[shape]}'" : ''
27
+ "<svg class='#{shape}' #{fillable} xmlns='http://www.w3.org/2000/svg'>#{symbol}</svg>"
28
+ end
29
+
30
+ <<-XML.strip
31
+ <svg viewBox="0 0 #{size} #{size}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
32
+ <g>
33
+ <rect fill="#{colors['background']}" x="0" y="0" width="#{size}" height="#{size}"></rect>
34
+ #{includes.join("\n")}
35
+ </g>
36
+ </svg>
37
+ XML
38
+ end
39
+
40
+ def base64
41
+ "data:image/svg+xml;base64,#{Base64.encode64(svg)[0...-1]}"
42
+ end
43
+
44
+ def size
45
+ @size ||= theme.width || 300
46
+ end
47
+
48
+ def path
49
+ @path ||= begin
50
+ dir = File.join(self.class.base_path, theme.ident)
51
+ FileUtils.mkdir_p(dir)
52
+
53
+ File.join(dir, "#{[text, uid].join('-')}.svg")
54
+ end
55
+ end
56
+
57
+ class << self
58
+ def base_path
59
+ File.join (Rpictogrify.config.base_path || 'public/system'), 'rpictogrify', "#{VERSION}"
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def generate_uid
66
+ text.hash.abs.to_s.gsub('0', '1')
67
+ end
68
+
69
+ def shapes
70
+ @shapes ||= theme.shapes.each_with_index.inject({}) do |result, ((shape, value), index)|
71
+ result[shape] = uid[index].nil? || uid[index].to_i > value['length'] ? '01' : uid[index].rjust(2, '0')
72
+ result
73
+ end
74
+ end
75
+
76
+ def colors
77
+ @colors ||= theme.colors.each_with_index.inject({}) do |result, ((part, values), index)|
78
+ result[part] = values[uid[index].to_i] || values.first
79
+ result
80
+ end
81
+ end
82
+
83
+ def fill
84
+ @fill ||= theme.shapes.inject({}) do |result, (shape, value)|
85
+ color = colors[value['fill']]
86
+ result[shape] = color if color
87
+ result
88
+ end
89
+ end
90
+
91
+ def symbols
92
+ @symbols ||= shapes.inject({}) do |result, (shape, value)|
93
+ symbol = theme.symbol("#{shape}-#{value}")
94
+ result[shape] = symbol&.children.to_s.gsub('\n', '')
95
+ result
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rpictogrify/themes/base'
4
+ require 'rpictogrify/themes/avataars_female'
5
+ require 'rpictogrify/themes/avataars_male'
6
+ require 'rpictogrify/themes/male_flat'
7
+ require 'rpictogrify/themes/monsters'
8
+
9
+ module Rpictogrify
10
+ class Theme
11
+ class << self
12
+ def find(ident)
13
+ theme_name = Rpictogrify::Inflector.camelize(ident.to_s)
14
+ Object.const_get("Rpictogrify::Themes::#{theme_name}")
15
+ rescue NameError
16
+ raise ArgumentError.new('The ident argument must be one of themes: avataars_female, avataars_male, male_flat, monsters')
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,6 @@
1
+ module Rpictogrify
2
+ module Themes
3
+ class AvataarsFemale < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Rpictogrify
2
+ module Themes
3
+ class AvataarsMale < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'singleton'
5
+ require 'json'
6
+
7
+ module Rpictogrify
8
+ module Themes
9
+ class Base
10
+ include Singleton
11
+ extend SingleForwardable
12
+
13
+ attr_reader :mapping, :resource, :ident
14
+ def_delegators :instance, :mapping, :resource, :ident,
15
+ :width, :height, :shapes, :colors, :symbol
16
+
17
+ def initialize
18
+ @ident = get_ident
19
+ @mapping = parse_mapping
20
+ @resource = parse_resource
21
+ end
22
+
23
+ def width
24
+ view_box[2]
25
+ end
26
+
27
+ def height
28
+ view_box[3]
29
+ end
30
+
31
+ def shapes
32
+ mapping['shapes']
33
+ end
34
+
35
+ def colors
36
+ mapping['colors']
37
+ end
38
+
39
+ def symbol(id)
40
+ resource.at_xpath("//*[@id='#{id}']")
41
+ end
42
+
43
+ private
44
+
45
+ def get_ident
46
+ Rpictogrify::Inflector.underscore(self.class.name).split('/').last
47
+ end
48
+
49
+ def parse_mapping
50
+ JSON.parse File.read(Rpictogrify.themes_assets_path.join(ident, 'mapping.json'))
51
+ end
52
+
53
+ def parse_resource
54
+ Nokogiri.XML File.read(Rpictogrify.themes_assets_path.join(ident, 'resource.svg'))
55
+ end
56
+
57
+ def view_box
58
+ @view_box ||= mapping['viewBox'].split(' ')
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,6 @@
1
+ module Rpictogrify
2
+ module Themes
3
+ class MaleFlat < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Rpictogrify
2
+ module Themes
3
+ class Monsters < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rpictogrify
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+ require 'rpictogrify'
3
+
4
+ describe Rpictogrify do
5
+ context 'configure' do
6
+ it 'load default config' do
7
+ expect(Rpictogrify.config.theme).to eq(:monsters)
8
+ expect(Rpictogrify.config.base_path).to eq('public/system')
9
+ end
10
+
11
+ it 'change after configure' do
12
+ Rpictogrify.configure do
13
+ self.theme = :avataars_female
14
+ self.base_path = 'public'
15
+ end
16
+
17
+ expect(Rpictogrify.config.theme).to eq(:avataars_female)
18
+ expect(Rpictogrify.config.base_path).to eq('public')
19
+
20
+ Rpictogrify.configure do
21
+ self.theme = :monsters
22
+ self.base_path = 'public/system'
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'generate' do
28
+ it 'return pictogram path with default theme' do
29
+ path = Rpictogrify.generate 'jim.cheung'
30
+ expect(path).to include('monsters')
31
+ end
32
+
33
+ it 'return pictogram path with given theme' do
34
+ path = Rpictogrify.generate 'jim.cheung', theme: :avataars_female
35
+ expect(path).to include('avataars_female')
36
+ end
37
+
38
+ it 'return pictogram path with changed configuration' do
39
+ Rpictogrify.configure do
40
+ self.theme = :avataars_female
41
+ self.base_path = 'public/custom'
42
+ end
43
+
44
+ path = Rpictogrify.generate 'jim.cheung'
45
+ expect(path).to include('avataars_female')
46
+ expect(path).to include('public/custom')
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,100 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+ RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
47
+ # The settings below are suggested to provide a good initial experience
48
+ # with RSpec, but feel free to customize to your heart's content.
49
+ =begin
50
+ # This allows you to limit a spec run to individual examples or groups
51
+ # you care about by tagging them with `:focus` metadata. When nothing
52
+ # is tagged with `:focus`, all examples get run. RSpec also provides
53
+ # aliases for `it`, `describe`, and `context` that include `:focus`
54
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55
+ config.filter_run_when_matching :focus
56
+
57
+ # Allows RSpec to persist some state between runs in order to support
58
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
59
+ # you configure your source control system to ignore this file.
60
+ config.example_status_persistence_file_path = "spec/examples.txt"
61
+
62
+ # Limits the available syntax to the non-monkey patched syntax that is
63
+ # recommended. For more details, see:
64
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
65
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
67
+ config.disable_monkey_patching!
68
+
69
+ # This setting enables warnings. It's recommended, but in some cases may
70
+ # be too noisy due to issues in dependencies.
71
+ config.warnings = true
72
+
73
+ # Many RSpec users commonly either run the entire suite or an individual
74
+ # file, and it's useful to allow more verbose output when running an
75
+ # individual spec file.
76
+ if config.files_to_run.one?
77
+ # Use the documentation formatter for detailed output,
78
+ # unless a formatter has already been configured
79
+ # (e.g. via a command-line flag).
80
+ config.default_formatter = "doc"
81
+ end
82
+
83
+ # Print the 10 slowest examples and example groups at the
84
+ # end of the spec run, to help surface which specs are running
85
+ # particularly slow.
86
+ config.profile_examples = 10
87
+
88
+ # Run specs in random order to surface order dependencies. If you find an
89
+ # order dependency and want to debug it, you can fix the order by providing
90
+ # the seed, which is printed after each run.
91
+ # --seed 1234
92
+ config.order = :random
93
+
94
+ # Seed global randomization in this process using the `--seed` CLI option.
95
+ # Setting this allows you to use `--seed` to deterministically reproduce
96
+ # test failures related to randomization by passing the same `--seed` value
97
+ # as the one that triggered the failure.
98
+ Kernel.srand config.seed
99
+ =end
100
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rpictogrify
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jim Cheung
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-01-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
42
+ email:
43
+ - hi.jinhu.zhang@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - MIT-LICENSE
49
+ - README.md
50
+ - assets/themes/avataars_female/mapping.json
51
+ - assets/themes/avataars_female/resource.svg
52
+ - assets/themes/avataars_male/mapping.json
53
+ - assets/themes/avataars_male/resource.svg
54
+ - assets/themes/male_flat/mapping.json
55
+ - assets/themes/male_flat/resource.svg
56
+ - assets/themes/monsters/mapping.json
57
+ - assets/themes/monsters/resource.svg
58
+ - lib/rpictogrify.rb
59
+ - lib/rpictogrify/configuration.rb
60
+ - lib/rpictogrify/generator.rb
61
+ - lib/rpictogrify/inflector.rb
62
+ - lib/rpictogrify/pictogram.rb
63
+ - lib/rpictogrify/theme.rb
64
+ - lib/rpictogrify/themes/avataars_female.rb
65
+ - lib/rpictogrify/themes/avataars_male.rb
66
+ - lib/rpictogrify/themes/base.rb
67
+ - lib/rpictogrify/themes/male_flat.rb
68
+ - lib/rpictogrify/themes/monsters.rb
69
+ - lib/rpictogrify/version.rb
70
+ - spec/rpictogrify_spec.rb
71
+ - spec/spec_helper.rb
72
+ homepage: https://github.com/jinhucheung/rpictogrify
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.0.8
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Ruby version of the pictogrify to generate unique pictograms
95
+ test_files:
96
+ - spec/spec_helper.rb
97
+ - spec/rpictogrify_spec.rb