gena 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9394c4d63919ceaf86b70b90e8338ab8927b7b41
4
- data.tar.gz: 7028e23fd9c244d528e07ff1a5bbf7c51b36a8b5
3
+ metadata.gz: d011b05b5a46e8082c31e35c5937526e60813077
4
+ data.tar.gz: 8faa576c158597b3a126c55c380aaf9e44e0b266
5
5
  SHA512:
6
- metadata.gz: 9d3135276f6d6b0fdb7dc0e322d60d5beca16fe3e94875352f98d285985957f0b299ebb736456050e4c6770328cfc92c9b2c7bf5c81ccbf051284320a4adf01a
7
- data.tar.gz: e48bde3e2dfe2b332c53e42951e7a7bd83e3c519c19733e66b25a739547cfdd7d81ae8a92453ad9faf755cb10052711522b4cb47cd7ab9793ca0ba03e4af2d46
6
+ metadata.gz: 2329244fb94233fc4a8c538401a1047470d080c86b2dda958c3f79860f79c5832026d0eb66dfa6900becb34b6bdd17cdec7f50b4dd6213607d7f7321f90ac69a
7
+ data.tar.gz: 240c13add3eb328b128c9cb5c5a2f4831379e656f3cbcc70f718e9943664c07e76944321d0dbe2018350a2ba9fcaa988c93abae8b927231bd8c0cc8d13dd1cdc
data/bin/gena CHANGED
@@ -1,71 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'gena'
3
+ # require 'gena'
4
+ require_relative '../lib/gena'
4
5
 
5
- unless system "which xc-resave >> /dev/null"
6
- raise "\nPlease install xc-resave utility first. Run: \n\nbrew install alexgarbarev/core/xc-resave\n\n"
7
- end
6
+ Gena::Application.new.check
8
7
 
9
- TEMPLATES_PROJECT_FOLDER = File.expand_path('Templates')
10
- TEMPLATES_SYSTEM_FOLDER = File.expand_path('~/.gena/templates')
8
+ Gena::Application.start(ARGV)
11
9
 
12
- # Include all template code files
13
10
 
14
- registered = []
15
- Dir["#{TEMPLATES_PROJECT_FOLDER}/**/*.rb", "#{TEMPLATES_SYSTEM_FOLDER}/**/*.rb"].each do |file|
16
- template_name = file.split(File::SEPARATOR)[-2]
17
- unless registered.include? template_name
18
- registered << template_name
19
- require file
20
- end
21
- end
22
-
23
- cli = GenerateCli.new
24
- options = cli.parse_arguments
25
-
26
- def cleanup
27
- ramba_adapter = RambaAdapter.new(nil, nil)
28
- ramba_adapter.delete_default_template
29
- ramba_adapter.delete_rambafile
30
- end
31
-
32
- if options[:cleanup]
33
- cleanup
34
- exit
35
- end
36
-
37
- at_exit do
38
- puts 'Cleaning up..'
39
- cleanup
40
- end
41
-
42
- config = Generate::Config.new
43
- config.load_plist_config
44
-
45
- if options[:fetch]
46
- FileUtils.rm_rf File.expand_path(TEMPLATES_SYSTEM_FOLDER)
47
- FileUtils.mkdir_p File.expand_path(TEMPLATES_SYSTEM_FOLDER)
48
- command = "git clone --depth 1 #{config.config['templates_url']} #{TEMPLATES_SYSTEM_FOLDER}"
49
- exec command
50
- end
51
-
52
- template = Generate::BaseTemplate.new_from_options(options, config)
53
-
54
- unless template.class.generamba?
55
- template.run
56
- exit
57
- end
58
-
59
- ramba_adapter = RambaAdapter.new(template, config)
60
- ramba_adapter.create_rambafile
61
- ramba_adapter.regenerate_default_template
62
-
63
- cli_command = ramba_adapter.generamba_gen_command(options)
64
- cli_command << " && #{__FILE__} --cleanup"
65
- cli_command << " && xc-resave #{config.xcode_project_path}"
66
-
67
- if options[:verbose]
68
- puts "#{cli_command}"
69
- end
70
-
71
- exec "#{cli_command}"
11
+ if system "which xc-resave >> /dev/null"
12
+ # exec "xc-resave #{$config.xcode_project_path}"
13
+ else
14
+ puts "\nWarning: xc-resave utility not installed. Run: \n\nbrew install alexgarbarev/core/xc-resave\n\n"
15
+ end
data/lib/cli/cli.rb ADDED
@@ -0,0 +1,138 @@
1
+ require 'thor'
2
+
3
+ module Gena
4
+
5
+ class Application < Thor
6
+
7
+ class << self
8
+
9
+ # class_for_command - hash to store custom classes (actually Plugin subclasses) for each
10
+ # command registered with gena
11
+ def class_for_command
12
+ @class_for_command ||= Hash.new
13
+ end
14
+
15
+ def class_for_command=(commands)
16
+ @class_for_command = commands
17
+ end
18
+
19
+ def plugin_classes
20
+ class_for_command.values.uniq
21
+ end
22
+
23
+ # Override help to forward
24
+
25
+ def help(shell, subcommand = false)
26
+
27
+ #List plugin commands separately from Gena general commands
28
+ plugins = []
29
+ class_for_command.each do |command, klass|
30
+ plugins += klass.printable_commands(false)
31
+ end
32
+
33
+ plugins.uniq!
34
+
35
+ list = printable_commands(true, subcommand)
36
+ Thor::Util.thor_classes_in(self).each do |klass|
37
+ list += klass.printable_commands(false)
38
+ end
39
+
40
+ list -= plugins
41
+
42
+ # Remove this line to disable alphabetical sorting
43
+ # list.sort! { |a, b| a[0] <=> b[0] }
44
+
45
+ # Add this line to remove the help-command itself from the output
46
+ # list.reject! {|l| l[0].split[1] == 'help'}
47
+
48
+ if defined?(@package_name) && @package_name
49
+ shell.say "#{@package_name} commands:"
50
+ else
51
+ shell.say "General commands:"
52
+ end
53
+
54
+ shell.print_table(list, :indent => 2, :truncate => true)
55
+ shell.say
56
+ class_options_help(shell)
57
+
58
+ shell.say "Plugins:"
59
+ shell.print_table(plugins, :indent => 2, :truncate => true)
60
+ end
61
+
62
+
63
+ # Override start to do custom dispatch (looking for plugin for unknown command)
64
+
65
+ def start(given_args = ARGV, config = {})
66
+
67
+ config[:shell] ||= Thor::Base.shell.new
68
+
69
+ command_name = normalize_command_name(retrieve_command_name(given_args.dup))
70
+ clazz = command_name ? class_for_command[command_name] : nil
71
+
72
+ if command_name && clazz
73
+ clazz.dispatch(nil, given_args.dup, nil, config)
74
+ else
75
+ dispatch(nil, given_args.dup, nil, config)
76
+ end
77
+
78
+ finish
79
+
80
+ rescue Thor::Error => e
81
+ config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message)
82
+ exit(1) if exit_on_failure?
83
+ rescue Errno::EPIPE
84
+ # This happens if a thor command is piped to something like `head`,
85
+ # which closes the pipe when it's done reading. This will also
86
+ # mean that if the pipe is closed, further unnecessary
87
+ # computation will not occur.
88
+ exit(0)
89
+ end
90
+
91
+ def finish
92
+ XcodeUtils.shared.save_project
93
+ Application.new.check_gena_version
94
+ end
95
+
96
+
97
+ end
98
+
99
+ no_tasks do
100
+
101
+
102
+ def check_gena_version
103
+ # Read cache
104
+ say "Checking for update.." if $verbose
105
+
106
+ version_path = File.expand_path "#{GENA_HOME}/version.plist"
107
+ plist = File.exists?(version_path) ? Plist::parse_xml(version_path) : Hash.new
108
+
109
+ data = nil
110
+
111
+ if !plist['timestamp'] || (DateTime.now.to_time.to_i - plist['timestamp'].to_i) > GENA_UPDATE_CHECK_INTERVAL
112
+ last_release_text = `curl https://api.github.com/repos/alexgarbarev/gena/releases/latest -s`
113
+ data = JSON.parse(last_release_text)
114
+
115
+ plist['data'] = data
116
+ plist['timestamp'] = DateTime.now.to_time.to_i
117
+ File.open(version_path, 'w') { |f| f.write plist.to_plist }
118
+ else
119
+ data = plist['data']
120
+ end
121
+
122
+ tag_name = data['tag_name']
123
+
124
+ if tag_name > VERSION
125
+ say "New update v#{tag_name} is available for gena.\nSee release notes: #{data['url']}", Color::YELLOW
126
+ say "Please update by:\n#{set_color('gem install gena', Color::GREEN)}", Color::YELLOW
127
+ end
128
+
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ end
135
+
136
+
137
+
138
+
data/lib/cli/init.rb ADDED
@@ -0,0 +1,273 @@
1
+ module Gena
2
+
3
+ class Application < Thor
4
+
5
+ desc 'init', 'Initialize gena.plist with default parameters'
6
+
7
+ def init
8
+
9
+ xcode_project_name = `find . -name *.xcodeproj`
10
+ xcode_project_name.strip!
11
+
12
+ xcode_project_name = ask_with_default("Enter path for #{set_color('project', Color::YELLOW)} or ENTER to continue (#{xcode_project_name}):", xcode_project_name)
13
+
14
+ xcode_project = Xcodeproj::Project.open(xcode_project_name)
15
+
16
+
17
+ main_target = nil
18
+ test_target = nil
19
+ xcode_project.native_targets.each do |target|
20
+ if target.product_type == 'com.apple.product-type.application'
21
+ main_target = target
22
+ elsif target.product_type == 'com.apple.product-type.bundle.unit-test'
23
+ test_target = target
24
+ end
25
+ end
26
+
27
+ sources_path = common_path_in_target(main_target, 'main.m')
28
+ tests_path = common_path_in_target(test_target, "#{sources_path}/")
29
+
30
+ sources_path = relative_to_current_dir(sources_path)
31
+ tests_path = relative_to_current_dir(tests_path)
32
+
33
+ hash = Hash.new
34
+ hash[:plugins_url] = [
35
+ 'https://github.com/alexgarbarev/gena-plugins.git'
36
+ ]
37
+
38
+ default_build_configuration = main_target.build_configuration_list.default_configuration_name || 'Debug'
39
+ info_plist_value = main_target.build_configuration_list.get_setting('INFOPLIST_FILE')[default_build_configuration]
40
+ if info_plist_value['$(SRCROOT)/']
41
+ info_plist_value['$(SRCROOT)/'] = ''
42
+ end
43
+
44
+ hash['company'] = xcode_project.root_object.attributes['ORGANIZATIONNAME'].to_s
45
+ hash['prefix'] = xcode_project.root_object.attributes['CLASSPREFIX'].to_s
46
+ hash['project_name'] = xcode_project.root_object.name
47
+ hash['project_target'] = main_target.name
48
+ hash['test_target'] = test_target.name
49
+ hash['info_plist'] = info_plist_value
50
+ hash['sources_dir'] = ask_with_default("Enter path for #{set_color('sources', Color::YELLOW)} or ENTER to continue (#{sources_path}):", sources_path)
51
+ hash['tests_dir'] = ask_with_default("Enter path for #{set_color('tests', Color::YELLOW)} or ENTER to continue (#{tests_path}):", tests_path)
52
+
53
+ say '===============================================================', Color::YELLOW
54
+ print_table(hash)
55
+ say '===============================================================', Color::YELLOW
56
+
57
+ File.open('gena.plist', 'w') { |f| f.write hash.to_plist }
58
+
59
+
60
+ say "'gena.plist' created..", Color::GREEN
61
+ end
62
+
63
+ no_tasks do
64
+
65
+ @downloaded_urls = Hash.new
66
+
67
+
68
+ def check
69
+
70
+ $verbose = (ARGV.include? '-v')
71
+ if $verbose
72
+ ARGV.reject! { |n| n == '-v' || n == '--verbose' }
73
+ end
74
+
75
+ unless Config.exists?
76
+ if ARGV == ['init']
77
+ init
78
+ ARGV.replace([])
79
+ else
80
+ if yes? "'gena.plist' is not exists. Do you want to create new one? (Y/n)", Color::YELLOW
81
+ init
82
+ end
83
+ end
84
+ end
85
+
86
+ $config = Gena::Config.new
87
+ $config.load_plist_config
88
+
89
+ unless $config.data
90
+ say '\'gena.plist\' is corrupted! Try recreating', Color::RED
91
+ abort
92
+ end
93
+
94
+ if !$config.data['plugins_url'] || $config.data['plugins_url'].count == 0
95
+ say "'plugins_url' key is missing inside 'gena.plist'", Color::RED
96
+ abort
97
+ end
98
+ download_plugins
99
+ load_plugins
100
+ save_plugin_configs
101
+ end
102
+
103
+ def download_plugins
104
+ load_downloaded_urls
105
+ $config.data['plugins_url'].each do |plugin_url|
106
+ if remote_url? plugin_url
107
+ is_old = old_url?(plugin_url)
108
+ if !downloaded_url?(plugin_url) || is_old
109
+ message = is_old ? 'not up to date' : 'not downloaded yet'
110
+ say "Plugin at '#{plugin_url}' #{message}", Color::YELLOW
111
+ download_plugin_at plugin_url
112
+ end
113
+ end
114
+ end
115
+ save_downloaded_urls
116
+ end
117
+
118
+ def remote_url?(url)
119
+ url =~ /(.+@)*([\w\d\.]+):(.*)/
120
+ end
121
+
122
+ def downloaded_url?(url)
123
+ File.exists? File.expand_path(download_path_for(url))
124
+ end
125
+
126
+ def old_url?(url)
127
+ entity = @downloaded_urls[key_for_url(url)]
128
+ return true unless entity
129
+ elapsed_seconds = DateTime.now.to_time.to_i - entity['timestamp'].to_i
130
+ if elapsed_seconds > GENA_UPDATE_CHECK_INTERVAL
131
+ current_hash = `git ls-remote #{url} refs/heads/master | cut -f 1`.strip
132
+ entity['timestamp'] = DateTime.now.to_time.to_i
133
+ return current_hash != entity['hash']
134
+ end
135
+ false
136
+ end
137
+
138
+ def load_downloaded_urls
139
+ plist_path = File.expand_path "#{GENA_HOME}/plugins.plist"
140
+ if File.exists? plist_path
141
+ @downloaded_urls = Plist::parse_xml(plist_path)
142
+ else
143
+ @downloaded_urls = Hash.new
144
+ end
145
+ end
146
+
147
+ def save_downloaded_urls
148
+ File.open(File.expand_path("#{GENA_HOME}/plugins.plist"), 'w') { |f| f.write @downloaded_urls.to_plist }
149
+ end
150
+
151
+ def key_for_url(url)
152
+ Digest::MD5.hexdigest url
153
+ end
154
+
155
+ def download_path_for(url)
156
+ hash = Digest::MD5.hexdigest url
157
+ File.expand_path "#{GENA_HOME}/plugins/#{hash}"
158
+ end
159
+
160
+ def download_plugin_at(url)
161
+ say "Downloading plugin from '#{url}'..", Color::GREEN, ' '
162
+ output_path = File.expand_path(download_path_for(url))
163
+ FileUtils.rmtree output_path
164
+ FileUtils.mkdir_p output_path
165
+ result = gena_system "git clone --depth 1 #{url} #{output_path}"
166
+ if result
167
+ say 'success!', Color::GREEN
168
+ @downloaded_urls[key_for_url(url)] = {
169
+ 'hash' => `git ls-remote #{url} refs/heads/master | cut -f 1`.strip,
170
+ 'timestamp' => DateTime.now.to_time.to_i
171
+ }
172
+
173
+ else
174
+ say 'Failed! Run with \'-v\' to debug', Color::RED
175
+ end
176
+ end
177
+
178
+ def load_plugins
179
+ registered = []
180
+ $config.data['plugins_url'].each do |plugin_url|
181
+ path = remote_url?(plugin_url) ? download_path_for(plugin_url) : plugin_url
182
+ Dir["#{File.expand_path(path)}/**/*.rb"].each do |file|
183
+ template_name = file.split(File::SEPARATOR)[-2]
184
+ unless registered.include? template_name
185
+ registered << template_name
186
+ say "Loading '#{file}'..", Color::YELLOW if $verbose
187
+ require file
188
+ end
189
+ end
190
+ end
191
+ Gena::Plugin.descendants.each do |clazz|
192
+ clazz.setup_thor_commands
193
+ end
194
+ end
195
+
196
+ def save_plugin_configs
197
+
198
+ data = $config.data[GENA_PLUGINS_CONFIG_KEY] || Hash.new
199
+
200
+ Application.plugin_classes.each do |klass|
201
+
202
+ defaults = klass.plugin_config_defaults
203
+
204
+ if defaults.count > 0
205
+ plugin_config_name = klass.plugin_config_name
206
+
207
+ if !data[plugin_config_name] || data[plugin_config_name].count == 0
208
+ say "Writing config defaults for plugin '#{plugin_config_name}'..", Color::GREEN
209
+ data[plugin_config_name] = defaults
210
+ elsif !data[plugin_config_name].keys.to_set.eql? defaults.keys.to_set
211
+ missing_keys = defaults.keys - data[plugin_config_name].keys
212
+ say "Adding missing config keys #{missing_keys} for plugin '#{plugin_config_name}.'", Color::GREEN
213
+ missing_keys.each { |key| data[plugin_config_name][key] = defaults[key] }
214
+ end
215
+
216
+ end
217
+
218
+ end
219
+
220
+ $config.data[GENA_PLUGINS_CONFIG_KEY] = data
221
+ $config.save!
222
+
223
+ end
224
+
225
+ private
226
+
227
+ def ask_with_default(message, default)
228
+ value = ask(message)
229
+ if value.empty?
230
+ value = default
231
+ end
232
+ value
233
+ end
234
+
235
+ def relative_to_current_dir(path)
236
+
237
+ if path.empty? || !path["#{Dir.pwd}/"]
238
+ path
239
+ else
240
+ result = path.dup
241
+ result["#{Dir.pwd}/"] = ''
242
+ result
243
+ end
244
+ end
245
+
246
+ def common_path_in_target(target, except_match)
247
+ common = ''
248
+ target.source_build_phase.files.each do |file|
249
+ unless file.file_ref.real_path.to_s[except_match]
250
+ path_components = file.file_ref.real_path.to_s #.split('/')
251
+ if common.empty?
252
+ common = path_components
253
+ else
254
+ common = common.path_intersection path_components
255
+ end
256
+ end
257
+ end
258
+
259
+ if File.file? common
260
+ common = common.delete_last_path_component
261
+ end
262
+ if common[-1] == '/'
263
+ common = common[0..-2]
264
+ end
265
+ common
266
+ end
267
+
268
+ end
269
+
270
+ end
271
+
272
+
273
+ end
@@ -0,0 +1,176 @@
1
+
2
+
3
+ module Gena
4
+
5
+ module Filetype
6
+ class Context
7
+ attr_accessor :target_key, :base_dir_key, :is_resource
8
+
9
+ def initialize(target, base_dir, is_resource)
10
+ @target_key = target
11
+ @base_dir_key = base_dir
12
+ @is_resource = is_resource
13
+ end
14
+ end
15
+ SOURCE = Context.new('project_target', 'sources_dir', false)
16
+ RESOURCE = Context.new('project_target', 'sources_dir', true)
17
+ TEST_SOURCE = Context.new('test_target', 'tests_dir', false)
18
+ TEST_RESOURCE = Context.new('test_target', 'tests_dir', true)
19
+ end
20
+
21
+ class Codegen < Thor
22
+
23
+ no_tasks do
24
+
25
+
26
+ def initialize(output_path, template_params)
27
+
28
+ @output_path = output_path
29
+ @template_params = template_params
30
+
31
+ end
32
+
33
+
34
+ def add_file(template_name, file_name, type, params = nil)
35
+
36
+ # Getting path for template
37
+ plugin_dir = File.dirname(caller.first.scan(/.*rb/).first)
38
+ template_path = absolute_path_for_template(template_name, plugin_dir)
39
+
40
+ # Output path
41
+ output_path = absolute_path_for_output(file_name, type)
42
+
43
+ # Params
44
+ template_params = @template_params.merge($config.data_without_plugins)
45
+ template_params = params.merge(template_params) if params
46
+
47
+ render_template_to_file(template_path, output_path, template_params)
48
+
49
+ add_file_to_project(output_path, type)
50
+
51
+ end
52
+
53
+ def render_template(template_name, params)
54
+
55
+ plugin_dir = File.dirname(caller.first.scan(/.*rb/).first)
56
+ template_path = absolute_path_for_template(template_name, plugin_dir)
57
+
58
+ render_template_from_path(template_path, params)
59
+
60
+ end
61
+
62
+ def render_template_to_file(template_name, output_path, params)
63
+
64
+ plugin_dir = File.dirname(caller.first.scan(/.*rb/).first)
65
+ template_path = absolute_path_for_template(template_name, plugin_dir)
66
+
67
+ render_template_from_path_to_file(template_path, output_path, params)
68
+ end
69
+
70
+ def add_file_to_project(output_path, type)
71
+
72
+ target_name = $config.data[type.target_key]
73
+
74
+ target = XcodeUtils.shared.obtain_target(target_name)
75
+
76
+ dirname = File.dirname(output_path)
77
+
78
+ group = XcodeUtils.shared.make_group(dirname, dirname)
79
+
80
+ XcodeUtils.shared.add_file(target, group, output_path, type.is_resource)
81
+ end
82
+
83
+ def remove_from_project(path)
84
+
85
+ XcodeUtils.shared.delete_path(path)
86
+
87
+ end
88
+
89
+ private
90
+
91
+
92
+ def render_template_from_path(template_path, params)
93
+
94
+ if $config.header_dir.empty?
95
+ say "No 'header' field inside 'gena.plist'. You can specify path to header's liquid template there. Using default header", Color::YELLOW if $verbose
96
+ Liquid::Template.file_system = GenaStaticHeader.new
97
+ else
98
+ Liquid::Template.file_system = Liquid::LocalFileSystem.new($config.header_dir, '%s.liquid')
99
+ end
100
+
101
+ file_source = IO.read(template_path)
102
+ template = Liquid::Template.parse(file_source)
103
+
104
+ template.render(params)
105
+ end
106
+
107
+ def render_template_from_path_to_file(template_path, output_path, params)
108
+
109
+ content = render_template(template_path, params)
110
+
111
+ FileUtils.mkpath(File.dirname(output_path))
112
+
113
+ say "Writing to file: #{output_path}", Color::GREEN
114
+ File.open(output_path, 'w+') do |f|
115
+ f.write(content)
116
+ end
117
+ end
118
+
119
+ def absolute_path_for_output(file_name, type)
120
+ # Output path
121
+ base_dir = $config.expand_to_project($config.data[type.base_dir_key])
122
+
123
+ if @output_path[0] == '/'
124
+ puts 'Output path is absolute' if $verbose
125
+ output_path = File.join(@output_path, file_name)
126
+ else
127
+ puts 'Output path is relative' if $verbose
128
+ output_path = File.join(base_dir, @output_path, file_name)
129
+ end
130
+ output_path
131
+ end
132
+
133
+ def absolute_path_for_template(template_path, plugin_dir)
134
+ result = ''
135
+
136
+ expanded = File.expand_path(template_path)
137
+ if File.exists? expanded
138
+ result = expanded
139
+ else
140
+ joined = File.join(plugin_dir, template_path)
141
+ if File.exists? joined
142
+ result = joined
143
+ end
144
+ end
145
+
146
+ if !result.empty?
147
+ say "Found template at path '#{result}'", Color::YELLOW if $verbose
148
+ else
149
+ say "Can't find path for template '#{template_path}'", Color::RED
150
+ abort
151
+ end
152
+ result
153
+ end
154
+
155
+ end
156
+
157
+ end
158
+
159
+ class GenaStaticHeader < Liquid::BlankFileSystem
160
+
161
+ def read_template_file(name)
162
+
163
+ if name == 'header'
164
+ '////////////////////////////////////////////////////////////////////////////////
165
+ //
166
+ // Generated by Gena.
167
+ //
168
+ ////////////////////////////////////////////////////////////////////////////////'
169
+ else
170
+ ''
171
+ end
172
+ end
173
+
174
+ end
175
+
176
+ end