xcassetscop 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []