storyboardlint 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 774c8e07d47f22213d691de1da856975d002cb78
4
+ data.tar.gz: 95178dea2c069f16d324a38337e42e46af9bbc3c
5
+ SHA512:
6
+ metadata.gz: 7e02928fbb6f30432d8ddf7b2af898927837acd7512f0f3e26af9a1444e0ff7f9edf2773373dcf62c3ec40f9457791bb4e1214fddc61deeba9aa0fadc504cb11
7
+ data.tar.gz: e9612e771af5f3e140ccbe31df832ccdec336916dcf1b98ed3f4b619ac42c0cbf063c5a53c51f80c8bf08a48f4e151406e0844578f08cacbbc32b37d1d0078a5
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "https://rubygems.org"
2
+
3
+ # If you make any changes in this file, please run `bundle install`.
4
+ # If new versions of your installed gems are available, run `bundle update`
5
+
6
+ gem "nokogiri", "~> 1.5.0"
7
+
8
+ group :development do
9
+ gem "bundler", "~> 1.3.5"
10
+ gem "jeweler", "~> 1.8.0"
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,51 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.3.5)
5
+ builder (3.2.2)
6
+ faraday (0.8.9)
7
+ multipart-post (~> 1.2.0)
8
+ git (1.2.6)
9
+ github_api (0.10.1)
10
+ addressable
11
+ faraday (~> 0.8.1)
12
+ hashie (>= 1.2)
13
+ multi_json (~> 1.4)
14
+ nokogiri (~> 1.5.2)
15
+ oauth2
16
+ hashie (2.0.5)
17
+ highline (1.6.20)
18
+ jeweler (1.8.8)
19
+ builder
20
+ bundler (~> 1.0)
21
+ git (>= 1.2.5)
22
+ github_api (= 0.10.1)
23
+ highline (>= 1.6.15)
24
+ nokogiri (= 1.5.10)
25
+ rake
26
+ rdoc
27
+ json (1.8.1)
28
+ jwt (0.1.11)
29
+ multi_json (>= 1.5)
30
+ multi_json (1.8.4)
31
+ multi_xml (0.5.5)
32
+ multipart-post (1.2.0)
33
+ nokogiri (1.5.10)
34
+ oauth2 (0.9.3)
35
+ faraday (>= 0.8, < 0.10)
36
+ jwt (~> 0.1.8)
37
+ multi_json (~> 1.3)
38
+ multi_xml (~> 0.5)
39
+ rack (~> 1.2)
40
+ rack (1.5.2)
41
+ rake (10.1.1)
42
+ rdoc (4.1.1)
43
+ json (~> 1.4)
44
+
45
+ PLATFORMS
46
+ ruby
47
+
48
+ DEPENDENCIES
49
+ bundler (~> 1.3.5)
50
+ jeweler (~> 1.8.0)
51
+ nokogiri (~> 1.5.0)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Johannes Fahrenkrug
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ StoryboardLint
2
+ ==============
3
+
4
+ A lint tool for UIStoryboard to find wrong classes and wrong storyboard/segue/reuse identifiers
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "storyboardlint"
16
+ gem.homepage = "https://github.com/jfahrenkrug/StoryboardLint"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{A lint tool for UIStoryboards to find wrong classes and wrong storyboard/segue/reuse identifiers}
19
+ gem.description = %Q{It's a pain to to keep identifier strings in your UIStoryboard and it your source code in sync. This tool helps you to do just that.}
20
+ gem.email = "johannes@springenwerk.com"
21
+ gem.authors = ["Johannes Fahrenkrug"]
22
+
23
+ # dependencies defined in Gemfile
24
+ end
25
+ Jeweler::RubygemsDotOrgTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'storyboardlint.rb'
4
+
5
+ exit StoryboardLint::Linter.run!(*ARGV)
@@ -0,0 +1,222 @@
1
+ # Have fun. Use at your own risk.
2
+ # Copyright (c) 2014 Johannes Fahrenkrug
3
+
4
+ require 'nokogiri'
5
+
6
+ module StoryboardLint
7
+ SEGUE_ID_PREFIX = "seg_"
8
+ STORYBOARD_ID_PREFIX = "sb_"
9
+ REUSE_ID_PREFIX = "ruid_"
10
+
11
+ class StoryboardScanner
12
+ def initialize(src_root)
13
+ @src_root = src_root
14
+ @scan_performed = false
15
+ end
16
+
17
+ def storyboard_files
18
+ return @sb_files if @sb_files
19
+
20
+ # find all storyboard files...
21
+ @sb_files = Dir.glob(File.join(@src_root, "**/*.storyboard"))
22
+ end
23
+
24
+ def scan_files
25
+ if !@scan_performed
26
+ @segue_ids ||= []
27
+ @storyboard_ids ||= []
28
+ @reuse_ids ||= []
29
+ @custom_class_names = []
30
+
31
+ storyboard_files.each do |sb_file|
32
+ sb_source = File.open(sb_file)
33
+ doc = Nokogiri::XML(sb_source)
34
+ doc.xpath('//segue').each do |element|
35
+ seg_id = element.attr('identifier')
36
+ @segue_ids << {:file => sb_file, :id => seg_id.to_s} if seg_id
37
+ end
38
+
39
+ @storyboard_ids += doc.xpath("//@storyboardIdentifier").to_a.map {|match| {:file => sb_file, :id => match.to_s}}
40
+ @reuse_ids += doc.xpath("//@reuseIdentifier").to_a.map {|match| {:file => sb_file, :id => match.to_s}}
41
+ @custom_class_names += doc.xpath("//@customClass").to_a.map {|match| {:file => sb_file, :class_name => match.to_s}}
42
+ end
43
+
44
+ @scan_performed = true
45
+ end
46
+ end
47
+
48
+ def segue_ids
49
+ scan_files
50
+ @segue_ids
51
+ end
52
+
53
+ def storyboard_ids
54
+ scan_files
55
+ @storyboard_ids
56
+ end
57
+
58
+ def reuse_ids
59
+ scan_files
60
+ @reuse_ids
61
+ end
62
+
63
+ def custom_class_names
64
+ scan_files
65
+ @custom_class_names
66
+ end
67
+ end
68
+
69
+ class SourceScanner
70
+ CLASS_REGEX = /@interface\s+([a-zA-Z_]+\w*)/
71
+ SEGUE_ID_REGEX = /@"(#{SEGUE_ID_PREFIX}(?:\\"|[^"])+)"/
72
+ STORYBOARD_ID_REGEX = /@"(#{STORYBOARD_ID_PREFIX}(?:\\"|[^"])+)"/
73
+ REUSE_ID_REGEX = /@"(#{REUSE_ID_PREFIX}(?:\\"|[^"])+)"/
74
+
75
+ def initialize(src_root)
76
+ @src_root = src_root
77
+ @scan_performed = false
78
+ end
79
+
80
+ def source_files
81
+ return @source_files if @source_files
82
+
83
+ # find all *.h, *.c, *.m and *.mm files
84
+ @source_files = Dir.glob(File.join(@src_root, "**/*.{h,c,m,mm}"))
85
+ end
86
+
87
+ def scan_files
88
+ if !@scan_performed
89
+ @class_names ||= []
90
+ @segue_ids ||= []
91
+ @storyboard_ids ||= []
92
+ @reuse_ids ||= []
93
+
94
+ source_files.each do |source_file|
95
+ File.readlines(source_file).each_with_index do |line, idx|
96
+ # class names
97
+ line.scan(CLASS_REGEX).each do |match|
98
+ @class_names << {:file => source_file, :line => idx + 1, :class_name => match[0]}
99
+ end
100
+
101
+ # segue ids
102
+ line.scan(SEGUE_ID_REGEX).each do |match|
103
+ @segue_ids << {:file => source_file, :line => idx + 1, :id => match[0]}
104
+ end
105
+
106
+ # storyboard ids
107
+ line.scan(STORYBOARD_ID_REGEX).each do |match|
108
+ @storyboard_ids << {:file => source_file, :line => idx + 1, :id => match[0]}
109
+ end
110
+
111
+ # reuse ids
112
+ line.scan(REUSE_ID_REGEX).each do |match|
113
+ @reuse_ids << {:file => source_file, :line => idx + 1, :id => match[0]}
114
+ end
115
+ end
116
+ end
117
+
118
+ @scan_performed = true
119
+ end
120
+ end
121
+
122
+ def class_names
123
+ scan_files
124
+ @class_names
125
+ end
126
+
127
+ def segue_ids
128
+ scan_files
129
+ @segue_ids
130
+ end
131
+
132
+ def storyboard_ids
133
+ scan_files
134
+ @storyboard_ids
135
+ end
136
+
137
+ def reuse_ids
138
+ scan_files
139
+ @reuse_ids
140
+ end
141
+ end
142
+
143
+ class Linter
144
+ def initialize(sb_scanner, source_scanner)
145
+ @sb_scanner = sb_scanner
146
+ @source_scanner = source_scanner
147
+ end
148
+
149
+ def lint
150
+ check_naming
151
+ check_custom_classes
152
+ check_ids
153
+ end
154
+
155
+ def check_naming
156
+ @sb_scanner.segue_ids.each do |seg_id|
157
+ if seg_id[:id] !~ /$#{SEGUE_ID_PREFIX}/
158
+ puts "warning: Segue ID '#{seg_id[:id]}' used in #{File.basename(seg_id[:file])} does not start with '#{SEGUE_ID_PREFIX}' prefix."
159
+ end
160
+ end
161
+
162
+ @sb_scanner.storyboard_ids.each do |sb_id|
163
+ if sb_id[:id] !~ /$#{STORYBOARD_ID_PREFIX}/
164
+ puts "warning: Storyboard ID '#{sb_id[:id]}' used in #{File.basename(sb_id[:file])} does not start with '#{STORYBOARD_ID_PREFIX}' prefix."
165
+ end
166
+ end
167
+
168
+ @sb_scanner.reuse_ids.each do |ru_id|
169
+ if ru_id[:id] !~ /$#{REUSE_ID_PREFIX}/
170
+ puts "warning: Reuse ID '#{ru_id[:id]}' used in #{File.basename(ru_id[:file])} does not start with '#{REUSE_ID_PREFIX}' prefix."
171
+ end
172
+ end
173
+ end
174
+
175
+ def check_custom_classes
176
+ @sb_scanner.custom_class_names.each do |custom_class|
177
+ if !@source_scanner.class_names.map {|cn| cn[:class_name]}.include?(custom_class[:class_name])
178
+ puts "error: Custom class '#{custom_class[:class_name]}' used in #{File.basename(custom_class[:file])} could not be found in source code."
179
+ end
180
+ end
181
+ end
182
+
183
+ def check_ids
184
+ @source_scanner.segue_ids.each do |seg_id|
185
+ if !@sb_scanner.segue_ids.map {|sb_seg_id| sb_seg_id[:id]}.include?(seg_id[:id])
186
+ puts "#{seg_id[:file]}:#{seg_id[:line]}: warning: Segue ID '#{seg_id[:id]}' could not be found in any Storyboard."
187
+ end
188
+ end
189
+
190
+ @source_scanner.storyboard_ids.each do |sb_id|
191
+ if !@sb_scanner.storyboard_ids.map {|sb_sb_id| sb_sb_id[:id]}.include?(sb_id[:id])
192
+ puts "#{sb_id[:file]}:#{sb_id[:line]}: warning: Storyboard ID '#{sb_id[:id]}' could not be found in any Storyboard."
193
+ end
194
+ end
195
+
196
+ @source_scanner.reuse_ids.each do |ru_id|
197
+ if !@sb_scanner.reuse_ids.map {|sb_ru_id| sb_ru_id[:id]}.include?(ru_id[:id])
198
+ puts "#{ru_id[:file]}:#{ru_id[:line]}: warning: Reuse ID '#{ru_id[:id]}' could not be found in any Storyboard."
199
+ end
200
+ end
201
+ end
202
+
203
+ def self.run!(*args)
204
+ puts "StoryboardLint"
205
+ puts "by Johannes Fahrenkrug, @jfahrenkrug, springenwerk.com"
206
+ puts
207
+
208
+ if args.size < 1
209
+ puts "Usage: storyboardlint <target directory>"
210
+ exit
211
+ end
212
+
213
+ sb_scanner = StoryboardLint::StoryboardScanner.new(ARGV[0])
214
+ source_scanner = StoryboardLint::SourceScanner.new(ARGV[0])
215
+
216
+ linter = StoryboardLint::Linter.new(sb_scanner, source_scanner)
217
+ linter.lint
218
+
219
+ return 0
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "storyboardlint"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Johannes Fahrenkrug"]
12
+ s.date = "2014-02-19"
13
+ s.description = "It's a pain to to keep identifier strings in your UIStoryboard and it your source code in sync. This tool helps you to do just that."
14
+ s.email = "johannes@springenwerk.com"
15
+ s.executables = ["storyboardlint"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/storyboardlint",
28
+ "lib/storyboardlint.rb",
29
+ "storyboardlint.gemspec"
30
+ ]
31
+ s.homepage = "https://github.com/jfahrenkrug/StoryboardLint"
32
+ s.licenses = ["MIT"]
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = "2.0.3"
35
+ s.summary = "A lint tool for UIStoryboards to find wrong classes and wrong storyboard/segue/reuse identifiers"
36
+
37
+ if s.respond_to? :specification_version then
38
+ s.specification_version = 4
39
+
40
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
41
+ s.add_runtime_dependency(%q<nokogiri>, ["~> 1.5.0"])
42
+ s.add_development_dependency(%q<bundler>, ["~> 1.3.5"])
43
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.0"])
44
+ else
45
+ s.add_dependency(%q<nokogiri>, ["~> 1.5.0"])
46
+ s.add_dependency(%q<bundler>, ["~> 1.3.5"])
47
+ s.add_dependency(%q<jeweler>, ["~> 1.8.0"])
48
+ end
49
+ else
50
+ s.add_dependency(%q<nokogiri>, ["~> 1.5.0"])
51
+ s.add_dependency(%q<bundler>, ["~> 1.3.5"])
52
+ s.add_dependency(%q<jeweler>, ["~> 1.8.0"])
53
+ end
54
+ end
55
+
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: storyboardlint
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Johannes Fahrenkrug
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-19 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.5.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.5.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.3.5
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 1.3.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: jeweler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.8.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.8.0
55
+ description: It's a pain to to keep identifier strings in your UIStoryboard and it
56
+ your source code in sync. This tool helps you to do just that.
57
+ email: johannes@springenwerk.com
58
+ executables:
59
+ - storyboardlint
60
+ extensions: []
61
+ extra_rdoc_files:
62
+ - LICENSE
63
+ - README.md
64
+ files:
65
+ - Gemfile
66
+ - Gemfile.lock
67
+ - LICENSE
68
+ - README.md
69
+ - Rakefile
70
+ - VERSION
71
+ - bin/storyboardlint
72
+ - lib/storyboardlint.rb
73
+ - storyboardlint.gemspec
74
+ homepage: https://github.com/jfahrenkrug/StoryboardLint
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.0.3
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: A lint tool for UIStoryboards to find wrong classes and wrong storyboard/segue/reuse
98
+ identifiers
99
+ test_files: []