objc-referee 0.1.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
+ SHA1:
3
+ metadata.gz: 36c9f2903bc7f510e0a34800fce52cf0a878f39d
4
+ data.tar.gz: ced79e3acb02c3044e7b8dd26c6e19a7dfc10088
5
+ SHA512:
6
+ metadata.gz: 20f102e50fcc1886f61ba630cb8676ded8b29587fa223176fb84d267cbeeaf45332fb9c85e12be6870416c886e8ad7769f07981df54d693fd283cdafd30ee850
7
+ data.tar.gz: b9f6a89b2a98626348b7b8cbc8dd7fa91b8bf610c25c24f7f9e0c3f51e682b9932175d163a082eff47fb9612414ba35bfe6c01f4470d5a9bff3934ed576df1fe
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /bin/*
10
+ !/bin/console
11
+ !/bin/setup
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ # Files to exclude - all executable dependencies from Rubygems.
4
+ AllCops:
5
+ Exclude:
6
+ - 'bin/*'
7
+
8
+ # Give us... a little more room.
9
+ Metrics/LineLength:
10
+ Max: 120
11
+
12
+ # This cop seems to produce a few false-positives.
13
+ Style/Next:
14
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,16 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2015-08-20 17:16:11 -0400 using RuboCop version 0.33.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 3
10
+ Metrics/AbcSize:
11
+ Max: 23
12
+
13
+ # Offense count: 3
14
+ # Configuration parameters: CountComments.
15
+ Metrics/MethodLength:
16
+ Max: 32
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ sudo: false
2
+ cache: bundler
3
+ language: ruby
4
+ rvm:
5
+ - 2.0.0
6
+ - 2.1
7
+ - 2.2
8
+ before_install: gem update --remote bundler
9
+ install:
10
+ - bundle install --retry=3
11
+ script:
12
+ - bundle exec rubocop
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+ This project adheres to [Semantic Versioning](http://semver.org/) and this file attempts to follow the guidelines provided by [Keep a CHANGELOG](http://keepachangelog.com/).
4
+
5
+ ## [0.1.1] - 2015-09-02
6
+ Initial public release.
7
+
8
+ [0.1.1]: https://github.com/dynamit/referee/v0.1.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in referee.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,65 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ objc-referee (0.1.0)
5
+ mustache (~> 1.0, >= 1.0.2)
6
+ nokogiri (~> 1.6, >= 1.6.6.2)
7
+ xcodeproj (~> 0.20.1)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (4.2.4)
13
+ i18n (~> 0.7)
14
+ json (~> 1.7, >= 1.7.7)
15
+ minitest (~> 5.1)
16
+ thread_safe (~> 0.3, >= 0.3.4)
17
+ tzinfo (~> 1.1)
18
+ ast (2.1.0)
19
+ astrolabe (1.3.1)
20
+ parser (~> 2.2)
21
+ coderay (1.1.0)
22
+ colored (1.2)
23
+ i18n (0.7.0)
24
+ json (1.8.3)
25
+ method_source (0.8.2)
26
+ mini_portile (0.6.2)
27
+ minitest (5.8.0)
28
+ mustache (1.0.2)
29
+ nokogiri (1.6.6.2)
30
+ mini_portile (~> 0.6.0)
31
+ parser (2.2.2.6)
32
+ ast (>= 1.1, < 3.0)
33
+ powerpack (0.1.1)
34
+ pry (0.10.1)
35
+ coderay (~> 1.1.0)
36
+ method_source (~> 0.8.1)
37
+ slop (~> 3.4)
38
+ rainbow (2.0.0)
39
+ rake (10.4.2)
40
+ rubocop (0.33.0)
41
+ astrolabe (~> 1.3)
42
+ parser (>= 2.2.2.5, < 3.0)
43
+ powerpack (~> 0.1)
44
+ rainbow (>= 1.99.1, < 3.0)
45
+ ruby-progressbar (~> 1.4)
46
+ ruby-progressbar (1.7.5)
47
+ rubygems-tasks (0.2.4)
48
+ slop (3.6.0)
49
+ thread_safe (0.3.5)
50
+ tzinfo (1.2.2)
51
+ thread_safe (~> 0.1)
52
+ xcodeproj (0.20.2)
53
+ activesupport (>= 3)
54
+ colored (~> 1.2)
55
+
56
+ PLATFORMS
57
+ ruby
58
+
59
+ DEPENDENCIES
60
+ bundler (~> 1.8)
61
+ objc-referee!
62
+ pry (~> 0.10.1)
63
+ rake (~> 10.0)
64
+ rubocop (~> 0.33.0)
65
+ rubygems-tasks (~> 0.2.4)
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Dynamit, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all 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,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # Referee
2
+
3
+ [![Build Status](https://travis-ci.org/Dynamit/referee.svg?branch=master)](https://travis-ci.org/Dynamit/referee) [![Gem Version](https://badge.fury.io/rb/objc-referee.svg)](http://badge.fury.io/rb/objc-referee)
4
+
5
+ Referee is a script that generates Objective-C classes and macros for easily referencing your resources in code, providing IDE auto-complete and compile-time safety for your app.
6
+
7
+ The currently supported resource types are:
8
+
9
+ - Storyboards
10
+ - View Controllers
11
+ - Segue Identifiers
12
+ - Table View Cell Identifiers
13
+ - Collection View Cell Identifiers
14
+
15
+ The inspiration for this script was provided by [R.swift](https://github.com/mac-cain13/R.swift).
16
+
17
+ ## Demo
18
+
19
+ Without `referee`, you can either maintain your own constants file in parallel with your storyboards or write code as such:
20
+
21
+ ```objc
22
+ UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
23
+ UIViewController *someViewController = [mainStoryboard instantiateViewControllerWithIdentifier:@"Some View Controller"];
24
+ [someViewController performSegue:@"Some Segue"];
25
+ ```
26
+
27
+ With `referee`, you can replace this with references to a few automatically generated classes:
28
+
29
+ ```objc
30
+ UIViewController *someViewController = TSTResources.viewControllers.SomeViewController;
31
+ [someViewController performSegue:TSTResources.segues.SomeSegue];
32
+ ```
33
+
34
+ Now, if you change an identifier in your storyboard, your code won't compile unless it matches as well, providing you with the confidence that your code and UI are in sync.
35
+
36
+ ## Installation
37
+
38
+ `referee` is distributed through [RubyGems](https://rubygems.org/). To install it, execute the following command:
39
+
40
+ $ gem install objc-referee
41
+
42
+ As a reminder, if you are using [rbenv](https://github.com/sstephenson/rbenv) to manage your Gems, you may additionally need to run:
43
+
44
+ $ rbenv rehash
45
+
46
+ Note that Xcode does not use your standard user `$PATH`, so we'll need to place `referee` in a standard place that we can reference in our build scripts. `/usr/local/bin/` is a good place for this executable:
47
+
48
+ $ ln -s $(which referee) /usr/local/bin/referee
49
+
50
+ Or if you'd rather do the above manually, here is an example using `rbenv`:
51
+
52
+ $ which referee
53
+ /Users/colindrake/.rbenv/shims/referee
54
+ $ ln -s /Users/colindrake/.rbenv/shims/referee /usr/local/bin/referee
55
+
56
+ Finally, if you are using a `Gemfile` to manage Ruby dependencies for your app or build system, make sure to add the following:
57
+
58
+ source 'https://rubygems.org'
59
+ gem 'objc-referee'
60
+
61
+ ## Xcode Integration
62
+ This script was designed to be easily integrated into your Xcode project workflow.
63
+
64
+ Begin by adding a Run Script to your Build Phases. For the script portion, enter:
65
+
66
+ /usr/local/bin/referee --prefix <your class prefix>
67
+
68
+ _**Ensure that this Run Script runs before the Compile Sources step. Otherwise, your new changes won't get compiled!**_
69
+
70
+ Build the project and open your source folder. By default, at the root of that folder there should be two new files: `<Prefix>Resources.h` and `<Prefix>Resources.m`. Add these files to the Xcode project, ensuring "Copy Files" is _not_ selected.
71
+
72
+ From now on, when you make changes in your Storyboard files, they will be reflected in these source files upon compiling.
73
+
74
+ If you need to setup different paths or configure `referee` further, check the `--help` flag for more information and documentation.
75
+
76
+ ## Development
77
+
78
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
79
+
80
+ ## Todos
81
+
82
+ - [ ] Add in Image support
83
+ - [ ] More robust method name generation
84
+ - [ ] NIB support
85
+ - [ ] Localized Strings?
86
+ - [ ] Per-category toggle flags, i.e. something to the effect of `--table-cells=NO`
87
+
88
+ ## Caveats
89
+
90
+ A word of caution: using Referee on your project can make your project's binary size balloon. This is a consequence of what Referee does for you - it creates convenience classes for you, thus increasing your symbol count. On most smaller projects, this won't matter. On large ones, however, please be cautious.
91
+
92
+ ## Contributing
93
+
94
+ 1. Fork it ( https://github.com/dynamit/referee/fork )
95
+ 2. Create your feature branch (`git checkout -b feature/my-new-feature`)
96
+ 3. Ensure you adhere to the style guidelines (run `rubocop`)
97
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
98
+ 5. Push to the branch (`git push origin feature/my-new-feature`)
99
+ 6. Create a new Pull Request
100
+
101
+ Note: The [Rubocop](https://github.com/bbatsov/rubocop) adherence can always be improved! Please see `.rubocop_todo.yml` for areas that need improvement.
102
+
103
+ ## License
104
+
105
+ This project is MIT licensed. Please see `LICENSE.txt` for details.
106
+
107
+ ## Sponsorship
108
+
109
+ This project is sponsored by the iOS Mobile group at [Dynamit, LLC](http://dynamit.com).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'rubygems/tasks'
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
4
+ require 'referee/version'
5
+
6
+ Gem::Tasks.new
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "referee"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require "pry"
10
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/exe/referee ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'referee'
3
+ Referee::CLI.new(ARGV).execute
@@ -0,0 +1,29 @@
1
+ module Referee
2
+ # Class providing an Xcode-parseable output mechanism.
3
+ class BuildOutput
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ # Outputs an informational message.
9
+ def info(msg)
10
+ puts msg if @config.verbose
11
+ end
12
+
13
+ # Outputs a message tagged as a warning for Xcode.
14
+ def warn(msg)
15
+ puts "warning: #{msg}"
16
+ end
17
+
18
+ # Outputs a message tagged as an error for Xcode.
19
+ def error(msg)
20
+ puts "error: #{msg}"
21
+ end
22
+
23
+ # Outputs an error message and quits with an error status.
24
+ def die(msg, status = 1)
25
+ error msg
26
+ Kernel.exit status
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,108 @@
1
+ module Referee
2
+ # Command line interface.
3
+ # Thanks to http://pewpewthespells.com/blog/buildsettings.html for the list
4
+ # of valid Xcode build variables.
5
+ class CLI
6
+ def initialize(args)
7
+ @config = parse_args(args)
8
+ validate_config
9
+ end
10
+
11
+ public
12
+
13
+ def execute
14
+ build_output.info "referee v#{VERSION} starting..."
15
+ load_project
16
+ generate_code
17
+ end
18
+
19
+ private
20
+
21
+ def parse_args(args)
22
+ # Build optparse instance to go through all command line parameters.
23
+ options = { project: default_project_directory,
24
+ prefix: nil,
25
+ output: default_output_directory,
26
+ error_on_missing_storyboard_ids: false,
27
+ verbose: false }
28
+
29
+ optparse = OptionParser.new do |opts|
30
+ opts.on('-x', '--xcode-project PROJECT', 'Set the Xcode project to parse') do |proj|
31
+ options[:project] = proj
32
+ end
33
+
34
+ opts.on('-p', '--prefix PREFIX', 'Set the class prefix for generated code') do |prefix|
35
+ options[:prefix] = prefix
36
+ end
37
+
38
+ opts.on('-o', '--output OUTPUT', 'Output generated files to this directory') do |output|
39
+ options[:output] = output
40
+ end
41
+
42
+ opts.on('-e', '--error-on-missing-ids', 'Issue errors on missing storyboard IDs') do
43
+ options[:error_on_missing_storyboard_ids] = true
44
+ end
45
+
46
+ opts.on('-v', '--verbose', 'Enable verbose mode') do
47
+ options[:verbose] = true
48
+ end
49
+
50
+ opts.on('-h', '--help', 'Display this screen') do
51
+ puts opts
52
+ Kernel.exit
53
+ end
54
+
55
+ opts.on('--version', 'Display version info') do
56
+ puts "v#{VERSION}"
57
+ Kernel.exit
58
+ end
59
+ end
60
+
61
+ # Parse arguments and convert to `Configuration` type.
62
+ optparse.parse!(args)
63
+ Configuration.new(options)
64
+ end
65
+
66
+ def validate_config
67
+ output_valid = File.exist?(@config.output) && File.writable?(@config.output)
68
+ build_output.die 'Class prefix must be provided' unless @config.prefix
69
+ build_output.die 'Output directory must exist and be writable' unless output_valid
70
+ build_output.die 'An Xcode project is required!' unless @config.project
71
+ build_output.die 'Xcode project must exist!' unless File.exist?(@config.project)
72
+ build_output.info 'Loaded and validated configuration...'
73
+ end
74
+
75
+ def default_project_directory
76
+ # Attempt to find a compiling project.
77
+ ENV['PROJECT_FILE_PATH']
78
+ end
79
+
80
+ def default_output_directory
81
+ # Assume standard <project_directory>/<project_name>/... for source.
82
+ # Otherwise, just output to the current directory.
83
+ proj_dir = ENV['PROJECT_DIR']
84
+ proj_name = ENV['PROJECT_NAME']
85
+
86
+ if proj_dir && proj_name
87
+ File.join proj_dir, proj_name
88
+ else
89
+ Dir.pwd
90
+ end
91
+ end
92
+
93
+ def load_project
94
+ build_output.info 'Parsing project resources...'
95
+ @project = Project.new_with_config(@config)
96
+ end
97
+
98
+ def generate_code
99
+ build_output.info 'Generating code...'
100
+ @generator = CodeGenerator.new(@project, @config)
101
+ @generator.generate!
102
+ end
103
+
104
+ def build_output
105
+ @build_output ||= BuildOutput.new(@config)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,60 @@
1
+ module Referee
2
+ # Code generation module. Outputs to a .h/.m file specified by configuration.
3
+ class CodeGenerator
4
+ def initialize(project, config)
5
+ @project = project
6
+ @config = config
7
+ end
8
+
9
+ public
10
+
11
+ def generate!
12
+ # Substitute out template information.
13
+ iface = renderer.render_interface(dictionary_representation)
14
+ impl = renderer.render_implementation(dictionary_representation)
15
+
16
+ # Generate paths.
17
+ interface_path = resource_file('h')
18
+ implementation_path = resource_file('m')
19
+
20
+ # Write to files.
21
+ File.write(interface_path, iface)
22
+ File.write(implementation_path, impl)
23
+ end
24
+
25
+ private
26
+
27
+ def dictionary_representation
28
+ @dictionary_representation ||= create_dictionary_representation
29
+ end
30
+
31
+ # Converts the resources into a Mustache-compatible template dictionary.
32
+ def create_dictionary_representation
33
+ dict = { storyboards: [],
34
+ table_cells: [],
35
+ collection_cells: [],
36
+ view_controllers: [],
37
+ segues: [],
38
+ prefix: @config.prefix }
39
+
40
+ @project.resources.each do |group|
41
+ dict[:storyboards] << group.storyboard
42
+ dict[:table_cells] += group.table_cells
43
+ dict[:collection_cells] += group.collection_cells
44
+ dict[:view_controllers] += group.view_controllers
45
+ dict[:segues] += group.segues
46
+ end
47
+
48
+ dict
49
+ end
50
+
51
+ def resource_file(extension)
52
+ filename = "#{@config.prefix}Resources.#{extension}"
53
+ File.join(@config.output, filename)
54
+ end
55
+
56
+ def renderer
57
+ @render ||= TemplateRenderer.new
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,12 @@
1
+ module Referee
2
+ # Configuration container.
3
+ class Configuration
4
+ attr_accessor :project, :prefix, :output, :error_on_missing_storyboard_ids, :verbose
5
+
6
+ def initialize(config)
7
+ config.each do |key, value|
8
+ send("#{key}=", value)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,94 @@
1
+ module Referee
2
+ # Helper subclass of `Xcode::Project`.
3
+ # Provides accessors into resources, etc.
4
+ class Project < ::Xcodeproj::Project
5
+ STORYBOARD_FILETYPE = 'file.storyboard'
6
+ VIEW_CONTROLLER_TAGS = %w(viewController
7
+ tableViewController
8
+ navigationController
9
+ glkViewController
10
+ pageViewController
11
+ collectionViewController
12
+ splitViewController
13
+ avPlayerViewController
14
+ tabBarController)
15
+
16
+ attr_accessor :config, :resources
17
+
18
+ # Instantiate a new `Project` instance given a `Configuration`.
19
+ def self.new_with_config(config)
20
+ project = new(config.project)
21
+ project.initialize_from_file
22
+ project.config = config
23
+ project.parse_project
24
+ project
25
+ end
26
+
27
+ def parse_project
28
+ @resources = []
29
+
30
+ find_storyboards.each do |storyboard|
31
+ # Parse out XML representation.
32
+ xml = storyboard_xml(storyboard)
33
+ table_cells = table_cells(xml)
34
+ collection_cells = collection_cells(xml)
35
+ view_controllers = view_controllers(xml)
36
+ non_empty_view_controllers = view_controllers.compact.uniq
37
+ segues = segues(xml)
38
+
39
+ # Add to resources collection
40
+ group = ResourceGroup.new(storyboard, table_cells, collection_cells, non_empty_view_controllers, segues)
41
+ @resources << group
42
+
43
+ # Check for missing view controller IDs in this storyboard.
44
+ if non_empty_view_controllers.count != view_controllers.count
45
+ msg = "Missing view controller ID(s) in '#{group.storyboard_name}' storyboard!"
46
+
47
+ if @config.error_on_missing_storyboard_ids
48
+ build_output.die msg
49
+ else
50
+ build_output.warn msg
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ # Search project for files matching the known storyboard type.
59
+ def find_storyboards
60
+ all_files = files
61
+ all_files.keep_if { |f| f.last_known_file_type == STORYBOARD_FILETYPE }
62
+ end
63
+
64
+ def storyboard_xml(storyboard)
65
+ file = File.open(storyboard.real_path)
66
+ Nokogiri::XML(file)
67
+ end
68
+
69
+ def table_cells(xml)
70
+ cells = xml.css('tableViewCell')
71
+ cells.map { |n| n['reuseIdentifier'] }.compact.uniq
72
+ end
73
+
74
+ def collection_cells(xml)
75
+ cells = xml.css('collectionViewCell')
76
+ cells.map { |n| n['reuseIdentifier'] }.compact.uniq
77
+ end
78
+
79
+ def view_controllers(xml)
80
+ selector = VIEW_CONTROLLER_TAGS.join(',')
81
+ controllers = xml.css(selector)
82
+ controllers.map { |n| n['storyboardIdentifier'] }
83
+ end
84
+
85
+ def segues(xml)
86
+ segues = xml.css('segue')
87
+ segues.map { |n| n['identifier'] }.compact.uniq
88
+ end
89
+
90
+ def build_output
91
+ @build_output ||= BuildOutput.new(@config)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,34 @@
1
+ module Referee
2
+ # Abstract class defining how renderable resources must behave.
3
+ class Renderable
4
+ attr_accessor :type
5
+
6
+ def initialize
7
+ fail 'Abstract class Renderable may not be instantiated!'
8
+ end
9
+
10
+ # NOTE: Subclasses must implement this method.
11
+ def declaration
12
+ fail 'Subclasses must implement declaration()!'
13
+ end
14
+
15
+ # NOTE: Subclasses must implement this method.
16
+ def implementation
17
+ fail 'Subclasses must implement implementation()!'
18
+ end
19
+
20
+ def methodize_name(name)
21
+ name.gsub(/[^a-zA-Z0-9]/, '')
22
+ end
23
+
24
+ def simple_method_declaration(name)
25
+ clean_name = methodize_name(name)
26
+ "- (#{@type})#{clean_name};"
27
+ end
28
+
29
+ def simple_method_implementation(name, body)
30
+ clean_name = methodize_name(name)
31
+ "- (#{@type})#{clean_name}\n{\n\treturn #{body};\n}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,17 @@
1
+ module Referee
2
+ # Renderable implementation for UICollectionViewCell identifiers embedded in storyboards.
3
+ class CollectionCell < Renderable
4
+ def initialize(identifier)
5
+ @identifier = identifier
6
+ @type = 'NSString *'
7
+ end
8
+
9
+ def declaration
10
+ simple_method_declaration @identifier
11
+ end
12
+
13
+ def implementation
14
+ simple_method_implementation @identifier, "@\"#{@identifier}\""
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Referee
2
+ # Renderable implementation for segue identifiers embedded in storyboards.
3
+ class Segue < Renderable
4
+ def initialize(identifier)
5
+ @identifier = identifier
6
+ @type = 'NSString *'
7
+ end
8
+
9
+ def declaration
10
+ simple_method_declaration @identifier
11
+ end
12
+
13
+ def implementation
14
+ simple_method_implementation @identifier, "@\"#{@identifier}\""
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module Referee
2
+ # Renderable implementation for UIStoryboards in a project.
3
+ class Storyboard < Renderable
4
+ def initialize(name)
5
+ @name = name
6
+ @type = 'UIStoryboard *'
7
+ end
8
+
9
+ def declaration
10
+ simple_method_declaration @name
11
+ end
12
+
13
+ def implementation
14
+ body = "[UIStoryboard storyboardWithName:@\"#{@name}\" bundle:[NSBundle mainBundle]]"
15
+ simple_method_implementation @name, body
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module Referee
2
+ # Renderable implementation for UITableViewCell identifiers embedded in storyboards.
3
+ class TableCell < Renderable
4
+ def initialize(identifier)
5
+ @identifier = identifier
6
+ @type = 'NSString *'
7
+ end
8
+
9
+ def declaration
10
+ simple_method_declaration @identifier
11
+ end
12
+
13
+ def implementation
14
+ simple_method_implementation @identifier, "@\"#{@identifier}\""
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Referee
2
+ # Renderable implementation for UIViewControllers embedded in storyboards.
3
+ class ViewController < Renderable
4
+ attr_accessor :name, :storyboard
5
+
6
+ def initialize(name, storyboard)
7
+ @name = name
8
+ @storyboard = storyboard
9
+ @type = 'UIViewController *'
10
+ end
11
+
12
+ def declaration
13
+ simple_method_declaration @name
14
+ end
15
+
16
+ def implementation
17
+ body = "[[UIStoryboard storyboardWithName:@\"#{@storyboard}\" bundle:[NSBundle mainBundle]] " \
18
+ "instantiateViewControllerWithIdentifier:@\"#{@name}\"]"
19
+ simple_method_implementation @name, body
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ module Referee
2
+ # Representation of all of the resources contained in a given storyboard.
3
+ class ResourceGroup
4
+ attr_accessor :storyboard, :table_cells, :collection_cells, :view_controllers, :segues
5
+
6
+ def initialize(storyboard, table_cells, collection_cells, view_controllers, segues)
7
+ @storyboard = storyboard
8
+ @table_cells = table_cells
9
+ @collection_cells = collection_cells
10
+ @view_controllers = view_controllers
11
+ @segues = segues
12
+ end
13
+
14
+ def storyboard_name
15
+ basename = File.basename(@storyboard.real_path)
16
+ basename.gsub(/\.storyboard/, '')
17
+ end
18
+
19
+ def storyboard
20
+ Storyboard.new(storyboard_name)
21
+ end
22
+
23
+ def table_cells
24
+ @table_cells.map { |c| TableCell.new(c) }
25
+ end
26
+
27
+ def collection_cells
28
+ @collection_cells.map { |c| CollectionCell.new(c) }
29
+ end
30
+
31
+ def view_controllers
32
+ @view_controllers.map { |c| ViewController.new(c, storyboard_name) }
33
+ end
34
+
35
+ def segues
36
+ @segues.map { |c| Segue.new(c) }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,32 @@
1
+ module Referee
2
+ # Manages finding and rendering code templates to strings.
3
+ class TemplateRenderer
4
+ INTERFACE_TEMPLATE_NAME = 'interface.h'
5
+ IMPLEMENTATION_TEMPLATE_NAME = 'implementation.m'
6
+
7
+ private
8
+
9
+ def filepath_from_root(path)
10
+ app_dir = File.dirname(File.expand_path(__FILE__))
11
+ File.join(app_dir, '../..', path)
12
+ end
13
+
14
+ def template(name)
15
+ path = filepath_from_root(File.join('resources', name))
16
+ contents = File.read(path)
17
+ proc { |dict| ::Mustache.render(contents, dict) }
18
+ end
19
+
20
+ public
21
+
22
+ # Renders the implementation (.m) file as a string.
23
+ def render_implementation(dict)
24
+ template(IMPLEMENTATION_TEMPLATE_NAME).call(dict)
25
+ end
26
+
27
+ # Renders the interface (.h) file as a string.
28
+ def render_interface(dict)
29
+ template(INTERFACE_TEMPLATE_NAME).call(dict)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ # Version information.
2
+ module Referee
3
+ VERSION = '0.1.1'
4
+ end
data/lib/referee.rb ADDED
@@ -0,0 +1,28 @@
1
+ # Standard library dependencies.
2
+ require 'optparse'
3
+
4
+ # Third party dependencies.
5
+ require 'mustache'
6
+ require 'nokogiri'
7
+ require 'rubygems'
8
+ require 'xcodeproj'
9
+
10
+ # Internal modules, etc.
11
+ require 'referee/buildoutput'
12
+ require 'referee/cli'
13
+ require 'referee/configuration'
14
+ require 'referee/codegenerator'
15
+ require 'referee/project'
16
+ require 'referee/renderable'
17
+ require 'referee/renderables/collectioncell'
18
+ require 'referee/renderables/segue'
19
+ require 'referee/renderables/storyboard'
20
+ require 'referee/renderables/tablecell'
21
+ require 'referee/renderables/viewcontroller'
22
+ require 'referee/resourcegroup'
23
+ require 'referee/templaterenderer'
24
+ require 'referee/version'
25
+
26
+ # Main module declaration.
27
+ module Referee
28
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'referee/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'objc-referee'
8
+ spec.version = Referee::VERSION
9
+ spec.authors = ['Colin Drake']
10
+ spec.email = ['colin.f.drake@gmail.com']
11
+
12
+ spec.summary = 'Resource file generator for Objective-C.'
13
+ spec.description = 'Resource file generator for Objective-C. Parses Xcode projects and ' \
14
+ 'creates a .m/.h file of compile-time checked resources.'
15
+ spec.homepage = 'http://dynamit.com'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.8'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rubocop', '~> 0.33.0'
26
+ spec.add_development_dependency 'pry', '~> 0.10.1'
27
+ spec.add_development_dependency 'rubygems-tasks', '~> 0.2.4'
28
+
29
+ spec.add_runtime_dependency 'xcodeproj', '~> 0.20.1'
30
+ spec.add_runtime_dependency 'mustache', '~> 1.0', '>= 1.0.2'
31
+ spec.add_runtime_dependency 'nokogiri', '~> 1.6', '>= 1.6.6.2'
32
+ end
@@ -0,0 +1,76 @@
1
+ #import "{{ prefix }}Resources.h"
2
+
3
+ /** UIStoryboard accessors. */
4
+ @implementation __{{ prefix }}Storyboards
5
+
6
+ {{# storyboards #}}
7
+ {{{ implementation }}}
8
+ {{/ storyboards }}
9
+
10
+ @end
11
+
12
+ /** UITableViewCell identifier accessors. */
13
+ @implementation __{{ prefix }}TableViewCells
14
+
15
+ {{# table_cells #}}
16
+ {{{ implementation }}}
17
+ {{/ table_cells }}
18
+
19
+ @end
20
+
21
+ /** UICollectionViewCell identifier accessors. */
22
+ @implementation __{{ prefix }}CollectionViewCells
23
+
24
+ {{# collection_cells #}}
25
+ {{{ implementation }}}
26
+ {{/ collection_cells }}
27
+
28
+ @end
29
+
30
+ /** UIViewController accessors. */
31
+ @implementation __{{ prefix }}ViewControllers
32
+
33
+ {{# view_controllers #}}
34
+ {{{ implementation }}}
35
+ {{/ view_controllers }}
36
+
37
+ @end
38
+
39
+ /** Segue identifier accessors. */
40
+ @implementation __{{ prefix }}Segues
41
+
42
+ {{# segues #}}
43
+ {{{ implementation }}}
44
+ {{/ segues }}
45
+
46
+ @end
47
+
48
+ /** Main resource reference class. */
49
+ @implementation __{{ prefix }}Resources
50
+
51
+ - (instancetype)init
52
+ {
53
+ self = [super init];
54
+
55
+ if (self) {
56
+ self.storyboards = [[__{{ prefix }}Storyboards alloc] init];
57
+ self.tableCells = [[__{{ prefix }}TableViewCells alloc] init];
58
+ self.collectionCells = [[__{{ prefix }}CollectionViewCells alloc] init];
59
+ self.viewControllers = [[__{{ prefix }}ViewControllers alloc] init];
60
+ self.segues = [[__{{ prefix }}Segues alloc] init];
61
+ }
62
+
63
+ return self;
64
+ }
65
+
66
+ + (instancetype)sharedInstance
67
+ {
68
+ static id _self = nil;
69
+ static dispatch_once_t once_Token;
70
+ dispatch_once(&once_Token, ^{
71
+ _self = [[super alloc] init];
72
+ });
73
+ return _self;
74
+ }
75
+
76
+ @end
@@ -0,0 +1,59 @@
1
+ @import UIKit;
2
+
3
+ /** ATTENTION: Use this macro to access resources. */
4
+ #define {{ prefix }}Resources ([__{{ prefix }}Resources sharedInstance])
5
+
6
+ @interface __{{ prefix }}Storyboards : NSObject
7
+
8
+ {{# storyboards #}}
9
+ {{{ declaration }}}
10
+ {{/ storyboards }}
11
+
12
+ @end
13
+
14
+ @interface __{{ prefix }}TableViewCells : NSObject
15
+
16
+ {{# table_cells #}}
17
+ {{{ declaration }}}
18
+ {{/ table_cells }}
19
+
20
+ @end
21
+
22
+ @interface __{{ prefix }}CollectionViewCells : NSObject
23
+
24
+ {{# collection_cells #}}
25
+ {{{ declaration }}}
26
+ {{/ collection_cells }}
27
+
28
+ @end
29
+
30
+ @interface __{{ prefix }}ViewControllers : NSObject
31
+
32
+ {{# view_controllers #}}
33
+ {{{ declaration }}}
34
+ {{/ view_controllers }}
35
+
36
+ @end
37
+
38
+ @interface __{{ prefix }}Segues : NSObject
39
+
40
+ {{# segues #}}
41
+ {{{ declaration }}}
42
+ {{/ segues }}
43
+
44
+ @end
45
+
46
+ /** Global resource reference accessor class. */
47
+ @interface __{{ prefix }}Resources : NSObject
48
+
49
+ /** Singleton accessor. */
50
+ + (instancetype)sharedInstance;
51
+
52
+ /** Resource accessors. */
53
+ @property (nonatomic, strong) __{{ prefix }}Storyboards *storyboards;
54
+ @property (nonatomic, strong) __{{ prefix }}TableViewCells *tableCells;
55
+ @property (nonatomic, strong) __{{ prefix }}CollectionViewCells *collectionCells;
56
+ @property (nonatomic, strong) __{{ prefix }}ViewControllers *viewControllers;
57
+ @property (nonatomic, strong) __{{ prefix }}Segues *segues;
58
+
59
+ @end
metadata ADDED
@@ -0,0 +1,201 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: objc-referee
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Colin Drake
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.33.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.33.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.10.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.10.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubygems-tasks
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.4
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.4
83
+ - !ruby/object:Gem::Dependency
84
+ name: xcodeproj
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.20.1
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.20.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: mustache
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: 1.0.2
107
+ type: :runtime
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: '1.0'
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 1.0.2
117
+ - !ruby/object:Gem::Dependency
118
+ name: nokogiri
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.6'
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: 1.6.6.2
127
+ type: :runtime
128
+ prerelease: false
129
+ version_requirements: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: '1.6'
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: 1.6.6.2
137
+ description: Resource file generator for Objective-C. Parses Xcode projects and creates
138
+ a .m/.h file of compile-time checked resources.
139
+ email:
140
+ - colin.f.drake@gmail.com
141
+ executables:
142
+ - referee
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rubocop.yml"
148
+ - ".rubocop_todo.yml"
149
+ - ".travis.yml"
150
+ - CHANGELOG.md
151
+ - Gemfile
152
+ - Gemfile.lock
153
+ - LICENSE.txt
154
+ - README.md
155
+ - Rakefile
156
+ - bin/console
157
+ - bin/setup
158
+ - exe/referee
159
+ - lib/referee.rb
160
+ - lib/referee/buildoutput.rb
161
+ - lib/referee/cli.rb
162
+ - lib/referee/codegenerator.rb
163
+ - lib/referee/configuration.rb
164
+ - lib/referee/project.rb
165
+ - lib/referee/renderable.rb
166
+ - lib/referee/renderables/collectioncell.rb
167
+ - lib/referee/renderables/segue.rb
168
+ - lib/referee/renderables/storyboard.rb
169
+ - lib/referee/renderables/tablecell.rb
170
+ - lib/referee/renderables/viewcontroller.rb
171
+ - lib/referee/resourcegroup.rb
172
+ - lib/referee/templaterenderer.rb
173
+ - lib/referee/version.rb
174
+ - objc-referee.gemspec
175
+ - resources/implementation.m
176
+ - resources/interface.h
177
+ homepage: http://dynamit.com
178
+ licenses:
179
+ - MIT
180
+ metadata: {}
181
+ post_install_message:
182
+ rdoc_options: []
183
+ require_paths:
184
+ - lib
185
+ required_ruby_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ required_rubygems_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ requirements: []
196
+ rubyforge_project:
197
+ rubygems_version: 2.4.5
198
+ signing_key:
199
+ specification_version: 4
200
+ summary: Resource file generator for Objective-C.
201
+ test_files: []