xcassetscop 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6392c29c38a1569c9dae8edab100e6da846ecd8cf4a90ade2f0a503a26afb795
4
+ data.tar.gz: 2bec50f9927a83a4c9f8db12854a2fe9d8d55e10db972737b88a0bd196a10df7
5
+ SHA512:
6
+ metadata.gz: 591b55b55c1ea806189506308ab39620ba8b96080dc088deaa67810499fc4add36c91059ae509cb84f4b3ea9c2cefdd3d5646cfb731194c4be07ca5735e8a61c
7
+ data.tar.gz: 927b59ca999b135a55238ce15001bc99f3e65d54179690bcc7a877379ecbf2a72c6cb57e0bc789ae1f838340f5b45eb8949e6da757cc29efe3707b9a85a2c8f5
data/bin/xcassetscop ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'xcassetscop'
6
+
7
+ XCAssetsCop::CLI.run ARGV
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configfile_parser'
4
+
5
+ module XCAssetsCop
6
+ module CLI
7
+ def self.run(args)
8
+ configfile_path = File.expand_path args[0]
9
+ unless File.file? configfile_path
10
+ puts "Can't find file on path: #{configfile_path}"
11
+ return
12
+ end
13
+ puts "Using config file at #{configfile_path}"
14
+
15
+ rules = ConfigfileParser.parse configfile_path
16
+ amount_of_files = rules.reduce(0) { |acc, rule| rule.paths.size + acc }
17
+ errors = Linter.lint_files rules
18
+
19
+ puts "#{rules.size} rules found"
20
+ puts "#{amount_of_files} files checked"
21
+ if errors.size.positive?
22
+ puts "Found #{errors.size} offenses:"
23
+ puts errors
24
+ else
25
+ puts 'No errors found'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './hash_extension'
4
+ require_relative './template_rendering_intent'
5
+ require_relative './image_scale'
6
+
7
+ module XCAssetsCop
8
+ class ConfigOptions
9
+ attr_reader :file_extension, :image_scale, :same_file_and_asset_name, :template_rendering_intent
10
+
11
+ ALLOWED_KEYS = %i[
12
+ file_extension
13
+ image_scale
14
+ same_file_and_asset_name
15
+ template_rendering_intent
16
+ ].freeze
17
+
18
+ def initialize(obj)
19
+ ensure_all_keys_are_allowed obj
20
+ @file_extension = obj.sdig('file_extension')
21
+ @image_scale = obj.sdig('image_scale')
22
+ @same_file_and_asset_name = obj.sdig('same_file_and_asset_name') || false
23
+ @template_rendering_intent = obj.sdig('template_rendering_intent')
24
+ end
25
+
26
+ private
27
+
28
+ def ensure_all_keys_are_allowed(obj)
29
+ diff = obj.keys.map(&:to_sym) - ALLOWED_KEYS
30
+ raise StandardError, "Unexpected key#{'s' if diff.size > 1}: #{diff.map { |s| "'#{s}'" }.join(', ')}" if diff.size.positive?
31
+ end
32
+
33
+ def validate
34
+ TemplateRenderingIntent.validate @template_rendering_intent if @template_rendering_intent
35
+ ImageScale.validate @image_scale if @template_rendering_intent
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require_relative 'linter_rule'
5
+
6
+ module XCAssetsCop
7
+ module ConfigfileParser
8
+ def self.parse(file_path)
9
+ rules = YAML.load_file file_path
10
+ rules.map { |r| LinterRule.new(r) }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ def sdig(key)
5
+ dig(key.to_s) || dig(key.to_sym)
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'utility'
4
+
5
+ module XCAssetsCop
6
+ module ImageScale
7
+ AVAILABLE_VALUES = %i[
8
+ single
9
+ individual
10
+ individual_and_single
11
+ ].freeze
12
+
13
+ def self.validate(value)
14
+ Utility.validate_params(value, AVAILABLE_VALUES)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative './template_rendering_intent'
5
+ require_relative './image_scale'
6
+ require_relative './linter_rule'
7
+
8
+ module XCAssetsCop
9
+ module Linter
10
+ def self.get_file_name(contents_json)
11
+ contents_json&.dig('images')&.first&.dig('filename')
12
+ end
13
+
14
+ def self.file_name_matches_asset_name(contents_json, file_path)
15
+ asset_name = file_path.split('/').select { |str| str.include? '.imageset' }.first.split('.').first
16
+ file_name = get_file_name(contents_json)&.split('.')&.first
17
+ return [] if file_name == asset_name
18
+
19
+ ["Expected asset name and file name to be the same, got:\nAsset name: #{asset_name}\nFile name: #{file_name}"]
20
+ end
21
+
22
+ def self.validate_image_scale(contents_json, expected)
23
+ case contents_json&.dig('images')&.size
24
+ when 1
25
+ image_scale = :single
26
+ when 3
27
+ image_scale = :individual
28
+ when 4
29
+ image_scale = :individual_and_single
30
+ else
31
+ raise StandardError, "Couldn't figure out the image scale"
32
+ end
33
+ return [] if image_scale == expected
34
+
35
+ file_name = get_file_name contents_json
36
+ ["Expected #{file_name} scale to be '#{expected}', got '#{image_scale}' instead"]
37
+ end
38
+
39
+ def self.validate_template_rendering_intent(contents_json, expected)
40
+ template_rendering_intent = contents_json&.dig('properties')&.sdig('template-rendering-intent')&.to_sym || :default
41
+ return [] if template_rendering_intent == expected
42
+
43
+ file_name = get_file_name contents_json
44
+ ["Expected #{file_name} to be rendered as '#{expected}', got '#{template_rendering_intent}' instead"]
45
+ end
46
+
47
+ def self.validate_file_extension(contents_json, expected)
48
+ file_name = get_file_name(contents_json)
49
+ file_extension = file_name.split('.').last
50
+ return [] if expected.to_sym == file_extension.to_sym
51
+
52
+ ["Expected #{file_name} type to be #{expected}, got #{file_extension} instead"]
53
+ end
54
+
55
+ def self.lint_file(file_path, config)
56
+ file = File.read file_path
57
+ contents_json = JSON.parse file
58
+
59
+ template_rendering_intent = config.template_rendering_intent
60
+ image_scale = config.image_scale
61
+ same_file_and_asset_name = config.same_file_and_asset_name || false
62
+ file_extension = config.file_extension
63
+
64
+ errors = []
65
+
66
+ errors += validate_template_rendering_intent(contents_json, template_rendering_intent.to_sym) if template_rendering_intent
67
+ errors += validate_image_scale(contents_json, image_scale.to_sym) if image_scale
68
+ errors += file_name_matches_asset_name(contents_json, file_path) if same_file_and_asset_name
69
+ errors += validate_file_extension(contents_json, file_extension.to_sym) if file_extension
70
+
71
+ errors
72
+ end
73
+
74
+ def self.lint_files(rules)
75
+ errors = []
76
+ rules.each do |rule|
77
+ errors += rule.paths.map { |path| lint_file(path, rule.config) }.flatten
78
+ end
79
+ errors
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './config_options'
4
+
5
+ module XCAssetsCop
6
+ class LinterRule
7
+ attr_reader :paths, :config
8
+
9
+ def initialize(obj)
10
+ LinterRule.ensure_no_missing_keys obj
11
+ LinterRule.ensure_all_keys_are_allowed obj
12
+ expanded_paths = obj.sdig(:paths).map { |path| File.expand_path(path) }
13
+ @paths = Dir.glob(expanded_paths)
14
+ @config = ConfigOptions.new obj.sdig(:config)
15
+ end
16
+
17
+ def self.ensure_no_missing_keys(obj)
18
+ obj_sym = obj.keys.map(&:to_sym)
19
+ missing_keys = []
20
+ missing_keys << :paths unless obj_sym.include? :paths
21
+ missing_keys << :config unless obj_sym.include? :config
22
+ raise StandardError, "Missing key#{'s' if missing_keys.size > 1}: #{missing_keys.map { |s| "'#{s}'" }.join(', ')}" if missing_keys.size.positive?
23
+ end
24
+
25
+ def self.ensure_all_keys_are_allowed(obj)
26
+ diff = obj.keys.map(&:to_sym) - %i[paths config]
27
+ raise StandardError, "Unexpected key#{'s' if diff.size > 1}: #{diff.map { |s| "'#{s}'" }.join(', ')}" if diff.size.positive?
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module XCAssetsCop
4
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'utility'
4
+
5
+ module XCAssetsCop
6
+ module TemplateRenderingIntent
7
+ AVAILABLE_VALUES = %i[
8
+ original
9
+ template
10
+ default
11
+ ].freeze
12
+
13
+ def self.validate(value)
14
+ Utility.validate_params(value, AVAILABLE_VALUES)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module XCAssetsCop
4
+ module Utility
5
+ def self.validate_params(param, valid_params)
6
+ return true if valid_params.include? param
7
+
8
+ raise StandardError, "'#{param}' is not a valid parameter.\nValid parameters: #{valid_params.join(', ')}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './xcassetscop/cli'
4
+ require_relative './xcassetscop/config_options'
5
+ require_relative './xcassetscop/configfile_parser'
6
+ require_relative './xcassetscop/image_scale'
7
+ require_relative './xcassetscop/linter'
8
+ require_relative './xcassetscop/linter_rule'
9
+ require_relative './xcassetscop/module'
10
+ require_relative './xcassetscop/template_rendering_intent'
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xcassetscop
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Aleph Retamal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Set rules for Xcode assets
14
+ email: retamal.ph@gmail.com
15
+ executables:
16
+ - xcassetscop
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/xcassetscop
21
+ - lib/xcassetscop.rb
22
+ - lib/xcassetscop/cli.rb
23
+ - lib/xcassetscop/config_options.rb
24
+ - lib/xcassetscop/configfile_parser.rb
25
+ - lib/xcassetscop/hash_extension.rb
26
+ - lib/xcassetscop/image_scale.rb
27
+ - lib/xcassetscop/linter.rb
28
+ - lib/xcassetscop/linter_rule.rb
29
+ - lib/xcassetscop/module.rb
30
+ - lib/xcassetscop/template_rendering_intent.rb
31
+ - lib/xcassetscop/utility.rb
32
+ homepage: https://lalacode.io
33
+ licenses:
34
+ - MIT
35
+ metadata: {}
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 2.7.6.2
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: XCAssetsCop
56
+ test_files: []