rswift 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +56 -0
  7. data/Rakefile +6 -0
  8. data/bin/rswift +7 -0
  9. data/lib/ext/array.rb +12 -0
  10. data/lib/ext/group.rb +24 -0
  11. data/lib/ext/native_target.rb +54 -0
  12. data/lib/ext/project.rb +68 -0
  13. data/lib/rswift.rb +22 -0
  14. data/lib/rswift/attributes_configurator.rb +20 -0
  15. data/lib/rswift/build_settings_configurator.rb +26 -0
  16. data/lib/rswift/build_settings_provider.rb +84 -0
  17. data/lib/rswift/cli.rb +33 -0
  18. data/lib/rswift/configuration.rb +41 -0
  19. data/lib/rswift/constants.rb +106 -0
  20. data/lib/rswift/device_provider.rb +25 -0
  21. data/lib/rswift/files_references_manager.rb +17 -0
  22. data/lib/rswift/group_references_manager.rb +114 -0
  23. data/lib/rswift/project_configurator.rb +41 -0
  24. data/lib/rswift/scheme_configurator.rb +10 -0
  25. data/lib/rswift/target_configurator.rb +22 -0
  26. data/lib/rswift/template_manager.rb +28 -0
  27. data/lib/rswift/templates/app/ios/.gitignore.erb +22 -0
  28. data/lib/rswift/templates/app/ios/Gemfile.erb +4 -0
  29. data/lib/rswift/templates/app/ios/Podfile.erb +11 -0
  30. data/lib/rswift/templates/app/ios/Rakefile.erb +1 -0
  31. data/lib/rswift/templates/app/ios/app/AppDelegate.swift.erb +15 -0
  32. data/lib/rswift/templates/app/ios/app/Assets.xcassets/AppIcon.appiconset/Contents.json.erb +73 -0
  33. data/lib/rswift/templates/app/ios/app/Info.plist.erb +45 -0
  34. data/lib/rswift/templates/app/ios/app/LaunchScreen.storyboard.erb +28 -0
  35. data/lib/rswift/templates/app/ios/package.json.erb +14 -0
  36. data/lib/rswift/templates/app/ios/spec/AppDelegateSpec.swift.erb +24 -0
  37. data/lib/rswift/templates/app/ios/spec/Info.plist.erb +24 -0
  38. data/lib/rswift/templates/app/osx/.gitignore.erb +21 -0
  39. data/lib/rswift/templates/app/osx/Gemfile.erb +4 -0
  40. data/lib/rswift/templates/app/osx/Podfile.erb +7 -0
  41. data/lib/rswift/templates/app/osx/Rakefile.erb +1 -0
  42. data/lib/rswift/templates/app/osx/app/AppDelegate.swift.erb +30 -0
  43. data/lib/rswift/templates/app/osx/app/Assets.xcassets/AppIcon.appiconset/Contents.json.erb +58 -0
  44. data/lib/rswift/templates/app/osx/app/Info.plist.erb +30 -0
  45. data/lib/rswift/templates/app/osx/app/main.swift.erb +5 -0
  46. data/lib/rswift/templates/app/osx/spec/AppDelegateSpec.swift.erb +24 -0
  47. data/lib/rswift/templates/app/osx/spec/Info.plist.erb +24 -0
  48. data/lib/rswift/templates/app/tvos/.gitignore.erb +21 -0
  49. data/lib/rswift/templates/app/tvos/Gemfile.erb +4 -0
  50. data/lib/rswift/templates/app/tvos/Podfile.erb +11 -0
  51. data/lib/rswift/templates/app/tvos/Rakefile.erb +1 -0
  52. data/lib/rswift/templates/app/tvos/app/AppDelegate.swift.erb +15 -0
  53. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Content.imageset/Contents.json.erb +12 -0
  54. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Back.imagestacklayer/Contents.json.erb +6 -0
  55. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Contents.json.erb +17 -0
  56. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Content.imageset/Contents.json.erb +12 -0
  57. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Front.imagestacklayer/Contents.json.erb +6 -0
  58. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json.erb +12 -0
  59. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Large.imagestack/Middle.imagestacklayer/Contents.json.erb +6 -0
  60. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Content.imageset/Contents.json.erb +12 -0
  61. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Back.imagestacklayer/Contents.json.erb +6 -0
  62. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Contents.json.erb +17 -0
  63. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Content.imageset/Contents.json.erb +12 -0
  64. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Front.imagestacklayer/Contents.json.erb +6 -0
  65. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json.erb +12 -0
  66. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/App Icon - Small.imagestack/Middle.imagestacklayer/Contents.json.erb +6 -0
  67. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/Contents.json.erb +26 -0
  68. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/AppIcon.brandassets/Top Shelf Image.imageset/Contents.json.erb +12 -0
  69. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/Contents.json.erb +6 -0
  70. data/lib/rswift/templates/app/tvos/app/Assets.xcassets/LaunchImage.launchimage/Contents.json.erb +15 -0
  71. data/lib/rswift/templates/app/tvos/app/Info.plist.erb +30 -0
  72. data/lib/rswift/templates/app/tvos/spec/AppDelegateSpec.swift.erb +24 -0
  73. data/lib/rswift/templates/app/tvos/spec/Info.plist.erb +24 -0
  74. data/lib/rswift/templates/app/watchos/wk_app/Assets.xcassets/AppIcon.appiconset/Contents.json.erb +55 -0
  75. data/lib/rswift/templates/app/watchos/wk_app/Info.plist.erb +35 -0
  76. data/lib/rswift/templates/app/watchos/wk_app/Interface.storyboard.erb +15 -0
  77. data/lib/rswift/templates/app/watchos/wk_ext/Assets.xcassets/README__ignoredByTemplate__ +1 -0
  78. data/lib/rswift/templates/app/watchos/wk_ext/ExtensionDelegate.swift.erb +4 -0
  79. data/lib/rswift/templates/app/watchos/wk_ext/Info.plist.erb +40 -0
  80. data/lib/rswift/templates/app/watchos/wk_ext/InterfaceController.swift.erb +6 -0
  81. data/lib/rswift/version.rb +3 -0
  82. data/lib/rswift/workspace_provider.rb +11 -0
  83. data/rswift.gemspec +26 -0
  84. metadata +227 -0
@@ -0,0 +1,33 @@
1
+ require 'thor'
2
+
3
+ module RSwift
4
+ class CLI < Thor
5
+ attr_accessor :template_manager
6
+ attr_accessor :project_configurator
7
+
8
+ def initialize(args = [], local_options = {}, config = {})
9
+ super
10
+ @template_manager = RSwift::TemplateManager.new
11
+ @project_configurator = RSwift::ProjectConfigurator.new
12
+ end
13
+
14
+ desc 'app NAME', 'create new empty application project'
15
+ option :template
16
+
17
+ def app(name)
18
+ template = options[:template]
19
+ template ||= 'ios'
20
+ template = template.to_sym
21
+ abort 'Available templates: ios (default), osx, tvos, watchos' unless RSwift::Constants::TEMPLATE_PROPERTIES.keys.include? template
22
+ abort 'Not implemented yet' if template == :watchos
23
+
24
+ @template_manager.create_files_for_template(name, template)
25
+
26
+ project_path = "#{name}/#{name}.xcodeproj"
27
+ project = Xcodeproj::Project.new project_path
28
+ @project_configurator.configure_project(project, template)
29
+ project.save
30
+ say_status :generate, project_path
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ require 'yaml'
2
+
3
+ module RSwift
4
+ class Configuration
5
+
6
+ def initialize
7
+ yaml_file = '.rswift.yml'
8
+ @config = YAML.load_file(yaml_file) if File.exist?(yaml_file)
9
+ end
10
+
11
+ def app_scheme_name
12
+ @config['app_scheme_name'] if @config
13
+ end
14
+
15
+ def product_name
16
+ @config['product_name'] if @config
17
+ end
18
+
19
+ def debug_build_configuration
20
+ @config['debug_build_configuration'] if @config
21
+ end
22
+
23
+ def release_build_configuration
24
+ @config['release_build_configuration'] if @config
25
+ end
26
+
27
+ def debug_product_bundle_identifier
28
+ @config['debug_product_bundle_identifier'] if @config
29
+ end
30
+
31
+ def release_product_bundle_identifier
32
+ @config['release_product_bundle_identifier'] if @config
33
+ end
34
+
35
+ def group_name(target)
36
+ group_name = @config[RSwift::Constants::TARGET_PROPERTIES[target.product_type_uti][:configuration_key]] if @config
37
+ group_name ||= RSwift::Constants::TARGET_PROPERTIES[target.product_type_uti][:group_name]
38
+ group_name
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,106 @@
1
+ require 'xcodeproj'
2
+
3
+ module RSwift
4
+ module Constants
5
+
6
+ CONFIGURATION_PROPERTIES = {
7
+ debug: {
8
+ name: 'Debug'
9
+ }.freeze,
10
+ release: {
11
+ name: 'Release'
12
+ }
13
+ }.freeze
14
+
15
+ TEMPLATE_PROPERTIES = {
16
+ ios: {
17
+ target_types: [:application, :unit_test_bundle].freeze,
18
+ sdk: "iOS #{Xcodeproj::XcodebuildHelper.new.last_ios_sdk}"
19
+ }.freeze,
20
+ osx: {
21
+ target_types: [:application, :unit_test_bundle].freeze,
22
+ sdk: "OSX #{Xcodeproj::XcodebuildHelper.new.last_osx_sdk}"
23
+ }.freeze,
24
+ tvos: {
25
+ target_types: [:application, :unit_test_bundle].freeze,
26
+ sdk: "tvOS #{Xcodeproj::XcodebuildHelper.new.last_tvos_sdk}"
27
+ }.freeze,
28
+ watchos: {
29
+ target_types: [:watch2_app, :watch2_extension].freeze,
30
+ sdk: "watchOS #{Xcodeproj::XcodebuildHelper.new.last_watchos_sdk}"
31
+ }.freeze
32
+ }.freeze
33
+
34
+ TARGET_PROPERTIES = {
35
+ application: {
36
+ group_name: 'app',
37
+ suffix: '',
38
+ configuration_key: 'app_group'
39
+ }.freeze,
40
+ unit_test_bundle: {
41
+ group_name: 'spec',
42
+ suffix: 'Specs',
43
+ configuration_key: 'spec_group'
44
+ }.freeze,
45
+ watch2_app: {
46
+ group_name: 'wk_app',
47
+ suffix: ' WatchKit App',
48
+ configuration_key: 'wk_app_group'
49
+ }.freeze,
50
+ watch2_extension: {
51
+ group_name: 'wk_ext',
52
+ suffix: ' WatchKit Extension',
53
+ configuration_key: 'wk_ext_group'
54
+ }.freeze
55
+ }.freeze
56
+
57
+ COMPILE_SOURCES_EXTENSIONS = %w(
58
+ .s
59
+ .c
60
+ .exp
61
+ .cpp
62
+ .m
63
+ .swift
64
+ .metal
65
+ .xcdatamodeld
66
+ .xcdatamodel
67
+ .xcmappingmodel
68
+ ).freeze
69
+
70
+ RESOURCES_EXTENSIONS = %w(
71
+ .xcconfig
72
+ .xcassets
73
+ .plist
74
+ .sh
75
+ .scn
76
+ .scnp
77
+ .sks
78
+ .strings
79
+ .xib
80
+ .storyboard
81
+ .apns
82
+ .bundle
83
+ .aiff
84
+ .midi
85
+ .mp3
86
+ .wav
87
+ .au
88
+ .bmp
89
+ .gif
90
+ .jpg
91
+ .png
92
+ .tiff
93
+ .avi
94
+ .mpeg
95
+ .mov
96
+ .xml
97
+ .html
98
+ .css
99
+ .json
100
+ .md
101
+ .txt
102
+ .rtf
103
+ .pdf
104
+ ).freeze
105
+ end
106
+ end
@@ -0,0 +1,25 @@
1
+ require 'json'
2
+
3
+ module RSwift
4
+ class DeviceProvider
5
+
6
+ def self.udid_for_device(device_name, template)
7
+ devices = devices_for_template(template)
8
+ devices[device_name].udid
9
+ end
10
+
11
+ private
12
+
13
+ def self.devices_for_template(template)
14
+ devices_json = `xcrun simctl list devices -j`
15
+ devices = JSON.parse(devices_json)['devices']
16
+ sdk = RSwift::Constants::TEMPLATE_PROPERTIES[template][:sdk]
17
+ devices_for_sdk = {}
18
+ devices[sdk].each do |device_hash|
19
+ device = OpenStruct.new(device_hash)
20
+ devices_for_sdk[device.name] = device
21
+ end
22
+ devices_for_sdk
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module RSwift
2
+ class FilesReferencesManager
3
+
4
+ attr_accessor :group_references_manager
5
+
6
+ def initialize
7
+ @group_references_manager = RSwift::GroupReferencesManager.new(self)
8
+ end
9
+
10
+ def update_target_references(group, target)
11
+ @group_references_manager.update_files_references(group, target)
12
+ @group_references_manager.update_directory_references(group, target)
13
+ @group_references_manager.cleanup_invalid_references(group)
14
+ @group_references_manager.cleanup_build_files(target)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,114 @@
1
+ module RSwift
2
+ class GroupReferencesManager
3
+
4
+ attr_accessor :files_references_manager
5
+
6
+ def initialize(files_references_manager)
7
+ @files_references_manager = files_references_manager
8
+ end
9
+
10
+ def update_files_references(group, target)
11
+ compile_sources_extensions = RSwift::Constants::COMPILE_SOURCES_EXTENSIONS
12
+ resources_extensions = RSwift::Constants::RESOURCES_EXTENSIONS
13
+
14
+ entries = Dir.glob("#{group.real_path.to_path}/*")
15
+ file_entries = entries.select { |entry| File.file? entry }
16
+
17
+ file_entries.each do |entry|
18
+ basename = File.basename entry
19
+ extname = File.extname entry
20
+ file_reference = group.file_for_path(basename)
21
+ if basename == 'Info.plist' || extname == '.xcdatamodel'
22
+ next
23
+ elsif compile_sources_extensions.include? extname
24
+ target.source_build_phase.add_file_reference(file_reference, true)
25
+ elsif resources_extensions.include? extname
26
+ target.resources_build_phase.add_file_reference(file_reference, true)
27
+ end
28
+ end
29
+ end
30
+
31
+ def update_directory_references(group, target)
32
+ compile_sources_extensions = RSwift::Constants::COMPILE_SOURCES_EXTENSIONS
33
+ resources_extensions = RSwift::Constants::RESOURCES_EXTENSIONS
34
+
35
+ entries = Dir.glob("#{group.real_path.to_path}/*")
36
+ directory_entries = entries.select { |entry| !File.file? entry }
37
+
38
+ directory_entries.each do |entry|
39
+ basename = File.basename entry
40
+ extname = File.extname entry
41
+ if compile_sources_extensions.include? extname
42
+ if extname != '.xcdatamodeld'
43
+ file_reference = group.file_for_path(basename)
44
+ target.source_build_phase.add_file_reference(file_reference, true)
45
+ else
46
+ add_xcdatamodel(entry, group, target)
47
+ end
48
+ elsif resources_extensions.include? extname
49
+ file_reference = group.file_for_path(basename)
50
+ target.resources_build_phase.add_file_reference(file_reference, true)
51
+ else
52
+ group_reference = group.group_for_path(basename)
53
+ @files_references_manager.update_target_references(group_reference, target)
54
+ end
55
+ end
56
+ end
57
+
58
+ def cleanup_invalid_references(group)
59
+ compile_sources_extensions = RSwift::Constants::COMPILE_SOURCES_EXTENSIONS
60
+ resources_extensions = RSwift::Constants::RESOURCES_EXTENSIONS
61
+
62
+ invalid_files_refs = group.files
63
+ invalid_groups_refs = group.groups
64
+
65
+ entries = Dir.glob("#{group.real_path.to_path}/*")
66
+ entries.each do |entry|
67
+ basename = File.basename entry
68
+ extname = File.extname entry
69
+
70
+ if compile_sources_extensions.include? extname
71
+ file_ref = invalid_files_refs.find { |invalid_file_ref| invalid_file_ref.path == basename }
72
+ invalid_files_refs.delete(file_ref)
73
+ elsif resources_extensions.include? extname
74
+ file_ref = invalid_files_refs.find { |invalid_file_ref| invalid_file_ref.path == basename }
75
+ invalid_files_refs.delete(file_ref)
76
+ elsif File.file? entry
77
+ file_ref = invalid_files_refs.find { |invalid_file_ref| invalid_file_ref.path == basename }
78
+ invalid_files_refs.delete(file_ref)
79
+ else
80
+ group_ref = invalid_groups_refs.find { |invalid_group_ref| invalid_group_ref.path == basename }
81
+ invalid_groups_refs.delete(group_ref)
82
+ end
83
+ end
84
+
85
+ invalid_files_refs.each { |file_ref| file_ref.remove_from_project }
86
+ invalid_groups_refs.each do |group_ref|
87
+ group_ref.clear
88
+ group_ref.remove_from_project
89
+ end
90
+ end
91
+
92
+ def cleanup_build_files(target)
93
+ invalid_build_sources = target.source_build_phase.files.select { |file| file.file_ref == nil }
94
+ invalid_build_sources.each { |file| target.source_build_phase.remove_build_file(file) }
95
+
96
+ invalid_resources = target.resources_build_phase.files.select { |file| file.file_ref == nil }
97
+ invalid_resources.each { |file| target.resources_build_phase.remove_build_file(file) }
98
+ end
99
+
100
+ private
101
+
102
+ def add_xcdatamodel(entry, group, target)
103
+ basename = File.basename entry
104
+ extname = File.extname entry
105
+ version_group = group.version_groups.find { |version_group| version_group.path == basename }
106
+ if version_group == nil
107
+ file_reference = group.file_for_path(basename)
108
+ target.source_build_phase.add_file_reference(file_reference, true)
109
+ version_group = group.version_groups.find { |version_group| version_group.path == basename }
110
+ @files_references_manager.update_target_references(version_group, target)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,41 @@
1
+ require 'xcodeproj'
2
+
3
+ module RSwift
4
+ class ProjectConfigurator
5
+ attr_accessor :build_settings_configurator
6
+ attr_accessor :target_configurator
7
+ attr_accessor :attributes_configurator
8
+ attr_accessor :scheme_configurator
9
+
10
+ def initialize
11
+ @build_settings_configurator = RSwift::BuildSettingsConfigurator.new
12
+ @target_configurator = RSwift::TargetConfigurator.new
13
+ @attributes_configurator = RSwift::AttributesConfigurator.new
14
+ @scheme_configurator = RSwift::SchemeConfigurator.new
15
+ end
16
+
17
+ def configure_project(project, template)
18
+ setup_targets(project, template)
19
+ setup_schemes project
20
+ @build_settings_configurator.configure_project_settings(project, template)
21
+ @attributes_configurator.configure_project_attributes project
22
+ end
23
+
24
+ private
25
+
26
+ def setup_targets(project, template)
27
+ target_types = RSwift::Constants::TEMPLATE_PROPERTIES[template][:target_types]
28
+ target_types.each do |target_type|
29
+ target_properties = RSwift::Constants::TARGET_PROPERTIES[target_type]
30
+ target = project.new_target(target_type, "#{project.name}#{target_properties[:suffix]}", template)
31
+ @target_configurator.configure_target(project, target, template)
32
+ end
33
+ @target_configurator.configure_targets_dependencies project
34
+ end
35
+
36
+ def setup_schemes(project)
37
+ scheme = Xcodeproj::XCScheme.new
38
+ @scheme_configurator.configure_app_scheme(project, scheme)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,10 @@
1
+ module RSwift
2
+ class SchemeConfigurator
3
+ def configure_app_scheme(project, scheme)
4
+ scheme.add_build_target(project.app_target)
5
+ scheme.set_launch_target(project.app_target)
6
+ scheme.add_test_target(project.spec_target)
7
+ scheme.save_as(project.path, project.name)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ module RSwift
2
+ class TargetConfigurator
3
+
4
+ attr_accessor :build_settings_configurator
5
+ attr_accessor :files_references_manager
6
+
7
+ def initialize
8
+ @build_settings_configurator = RSwift::BuildSettingsConfigurator.new
9
+ @files_references_manager = RSwift::FilesReferencesManager.new
10
+ end
11
+
12
+ def configure_target(project, target, template)
13
+ @build_settings_configurator.configure_target_settings(project, target, template)
14
+ group = project.new_group(target.group_name, target.group_name)
15
+ @files_references_manager.update_target_references(group, target)
16
+ end
17
+
18
+ def configure_targets_dependencies(project)
19
+ project.spec_target.add_dependency(project.app_target)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ module RSwift
2
+ class TemplateManager
3
+ include Thor::Base
4
+ include Thor::Actions
5
+
6
+ attr_reader :name
7
+
8
+ def self.source_root
9
+ File.dirname(__FILE__)
10
+ end
11
+
12
+ def create_files_for_template(name, template)
13
+ @name = name
14
+ current_directory_path = File.dirname(__FILE__)
15
+ template_directory = File.join(current_directory_path, 'templates/app', template.to_s)
16
+ Dir.glob("#{template_directory}/**/*.erb", File::FNM_DOTMATCH).each do |template_path|
17
+ relative_template_path = template_path.sub(current_directory_path + '/', '')
18
+
19
+ relative_erb_file_path = template_path.sub(template_directory, '')
20
+ file_name = File.basename(relative_erb_file_path, '.erb')
21
+ relative_directory_path = File.dirname(relative_erb_file_path)
22
+ relative_file_path = File.join(name, relative_directory_path, file_name)
23
+
24
+ template relative_template_path, relative_file_path
25
+ end
26
+ end
27
+ end
28
+ end