defgen 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +56 -0
  4. data/Guardfile +9 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +151 -0
  7. data/Rakefile +1 -0
  8. data/bin/defgen +5 -0
  9. data/defgen.gemspec +24 -0
  10. data/lib/defgen/.DS_Store +0 -0
  11. data/lib/defgen/cli.rb +47 -0
  12. data/lib/defgen/decorators/getter_decorator.rb +17 -0
  13. data/lib/defgen/decorators/setter_decorator.rb +11 -0
  14. data/lib/defgen/decorators.rb +2 -0
  15. data/lib/defgen/defaultfile.rb +65 -0
  16. data/lib/defgen/defaultfile_builder.rb +14 -0
  17. data/lib/defgen/defaultfile_parser.rb +41 -0
  18. data/lib/defgen/generators/.DS_Store +0 -0
  19. data/lib/defgen/generators/template/template_generator.rb +38 -0
  20. data/lib/defgen/generators/template/templates/constant.erb +1 -0
  21. data/lib/defgen/generators/template/templates/getter.erb +4 -0
  22. data/lib/defgen/generators/template/templates/implementation.erb +19 -0
  23. data/lib/defgen/generators/template/templates/interface.erb +17 -0
  24. data/lib/defgen/generators/template/templates/property.erb +1 -0
  25. data/lib/defgen/generators/template/templates/setter.erb +4 -0
  26. data/lib/defgen/generators/template.rb +1 -0
  27. data/lib/defgen/generators.rb +1 -0
  28. data/lib/defgen/installer.rb +75 -0
  29. data/lib/defgen/property/attributes.rb +29 -0
  30. data/lib/defgen/property.rb +30 -0
  31. data/lib/defgen/string_ext.rb +5 -0
  32. data/lib/defgen/version.rb +3 -0
  33. data/lib/defgen/xcodeproj_adapter.rb +48 -0
  34. data/lib/defgen.rb +14 -0
  35. data/spec/integration/defaultfile_parse_spec.rb +28 -0
  36. data/spec/support/property_accessor_examples.rb +12 -0
  37. data/spec/support/property_examples.rb +58 -0
  38. data/spec/support/spec_helper.rb +2 -0
  39. data/spec/unit/decorators/getter_decorator_spec.rb +31 -0
  40. data/spec/unit/decorators/setter_decorator_spec.rb +42 -0
  41. data/spec/unit/defaultfile_builder_spec.rb +12 -0
  42. data/spec/unit/defaultfile_parser_spec.rb +42 -0
  43. data/spec/unit/defautfile_spec.rb +11 -0
  44. data/spec/unit/property/attributes_spec.rb +48 -0
  45. data/spec/unit/property/property_spec.rb +10 -0
  46. data/spec/unit/string_ext_spec.rb +17 -0
  47. data/spec/unit/xcodeproj_adapter_spec.rb +10 -0
  48. metadata +186 -0
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in defgen.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ defgen (0.0.1)
5
+ xcodeproj
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (3.2.9)
11
+ i18n (~> 0.6)
12
+ multi_json (~> 1.0)
13
+ coderay (1.0.8)
14
+ colored (1.2)
15
+ diff-lcs (1.1.3)
16
+ guard (1.6.0)
17
+ listen (>= 0.6.0)
18
+ lumberjack (>= 1.0.2)
19
+ pry (>= 0.9.10)
20
+ thor (>= 0.14.6)
21
+ guard-rspec (2.3.3)
22
+ guard (>= 1.1)
23
+ rspec (~> 2.11)
24
+ i18n (0.6.1)
25
+ listen (0.6.0)
26
+ lumberjack (1.0.2)
27
+ method_source (0.8.1)
28
+ multi_json (1.5.0)
29
+ pry (0.9.10)
30
+ coderay (~> 1.0.5)
31
+ method_source (~> 0.8)
32
+ slop (~> 3.3.1)
33
+ rb-fsevent (0.9.2)
34
+ rspec (2.12.0)
35
+ rspec-core (~> 2.12.0)
36
+ rspec-expectations (~> 2.12.0)
37
+ rspec-mocks (~> 2.12.0)
38
+ rspec-core (2.12.2)
39
+ rspec-expectations (2.12.1)
40
+ diff-lcs (~> 1.1.3)
41
+ rspec-mocks (2.12.1)
42
+ slop (3.3.3)
43
+ thor (0.16.0)
44
+ xcodeproj (0.4.0)
45
+ activesupport (~> 3.2.6)
46
+ colored (~> 1.2)
47
+
48
+ PLATFORMS
49
+ ruby
50
+
51
+ DEPENDENCIES
52
+ defgen!
53
+ guard
54
+ guard-rspec
55
+ rb-fsevent
56
+ rspec
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012-2013 Paul Samuels
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,151 @@
1
+ ##NSUserDefaults using properties
2
+
3
+ Using properties to access data from `NSUserDefaults` is a million times better than using the verbose API. To do this you simply make a category on `NSUserDefaults` and then chuck in some boiler plate code to hook it all up.
4
+
5
+ It couldn't be simpler...
6
+
7
+ ##Defgen makes it simpler
8
+
9
+ Boiler plate is boring so why write it? Using Defgen we can have the computer write it for us. Supply a `Defaultfile` written in the super simple DSL and `defgen` will do the rest.
10
+
11
+ ##Features
12
+
13
+ This is firmly in the realms of Proof of Concept at the minute but.
14
+
15
+ - Uses class prefix used in your Xcodeproj
16
+ - Uses the OrganizationName in your Xcodeproj
17
+ - Customizable boiler plate
18
+ - No runtime tricks and all the `NSUserDefault` supported types are supported
19
+ - Automatically updates your Xcodeproj
20
+
21
+ ##Usage
22
+
23
+ ####Step 1. Supply a Defaultfile
24
+
25
+ # ├── AwesomeApp/
26
+ # ├── AwesomeApp/
27
+ # ├── AwesomeApp.xcodeproj/
28
+ # ├── Defaultfile <----- Your Defaultfile
29
+
30
+ # Defaultfile
31
+ array 'cheatCodes'
32
+ bool 'firstUse'
33
+ data 'someData'
34
+ dictionary 'config'
35
+ float 'ratio'
36
+ integer 'year'
37
+ object 'mmmmmm'
38
+ string 'name'
39
+ double 'betterRatio'
40
+ url 'homepage'
41
+
42
+ The DSL should be pretty self explanatory call the `type` of the object followed by what you want to call it.
43
+
44
+ ####Step 2. Run Defgen
45
+
46
+ $ defgen
47
+
48
+ ####Step 3. Enjoy the generated boiler plate
49
+
50
+ ###Customisation
51
+
52
+ If you don't like the supplied boiler plate then you can tweak it.
53
+
54
+ ####Step 1. Call generate the stubs
55
+
56
+ $ defgen stubs
57
+
58
+ This will generate the templates in your current project so that you can edit as much as you like
59
+
60
+ create defgen/constant.erb
61
+ create defgen/getter.erb
62
+ create defgen/implementation.erb
63
+ create defgen/interface.erb
64
+ create defgen/property.erb
65
+ create defgen/setter.erb
66
+
67
+ ##Example input/output/usage
68
+
69
+ ####Input
70
+
71
+ #Defaultfile
72
+
73
+ array 'aliases'
74
+
75
+ ####Output
76
+
77
+ //
78
+ // NSUserDefaults+PASProperties.h
79
+ // AwesomeApp
80
+ //
81
+ // Created by Paul Samuels on 04/01/2013.
82
+ // Copyright (c) 2013 Paul Samuels. All rights reserved.
83
+ //
84
+ // This is an autogenerated file any changes will be lost when running `defgen`
85
+ //
86
+
87
+ #import <Foundation/Foundation.h>
88
+
89
+ @interface NSUserDefaults (PASProperties)
90
+
91
+ @property (nonatomic, strong) NSArray *pas_aliases;
92
+
93
+ @end
94
+
95
+ //
96
+ // NSUserDefaults+PASProperties.m
97
+ // AwesomeApp
98
+ //
99
+ // Created by Paul Samuels on 04/01/2013.
100
+ // Copyright (c) 2013 Paul Samuels. All rights reserved.
101
+ //
102
+ // This is an autogenerated file any changes will be lost when running `defgen`
103
+ //
104
+
105
+ #import "NSUserDefaults+PASProperties.h"
106
+
107
+ NSString * const PASAliases = @"PASAliases";
108
+
109
+ @implementation NSUserDefaults (PASProperties)
110
+
111
+ - (NSArray *)pas_aliases;
112
+ {
113
+ return [self arrayForKey:PASAliases];
114
+ }
115
+
116
+ - (void)setPas_aliases:(NSArray *)aliases;
117
+ {
118
+ [self setObject:aliases forKey:PASAliases];
119
+ }
120
+
121
+ @end
122
+
123
+ ####Usage
124
+
125
+ #import "NSUserDefaults+PASProperties.h"
126
+
127
+ ...
128
+
129
+ userDefaults.pas_aliases = @[ @1, @2 ];
130
+ NSLog(@"%@", userDefaults.pas_aliases);
131
+
132
+ ## Installation
133
+
134
+ $ gem install defgen
135
+
136
+ ## Contributing
137
+
138
+ 1. Fork it
139
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
140
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
141
+ 4. Push to the branch (`git push origin my-new-feature`)
142
+ 5. Create new Pull Request
143
+
144
+ ##Wish List
145
+
146
+ - Support for provising customer getter e.g. `(nonatomic, strong, getter=isValid)`
147
+ - Support for overriding ownership qualifiers
148
+
149
+ ##Licence
150
+
151
+ Defgen is available under the MIT license. See the LICENSE file for more info.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/defgen ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'defgen'
4
+
5
+ Defgen::CLI.start
data/defgen.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'defgen/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "defgen"
8
+ gem.version = Defgen::VERSION
9
+ gem.authors = ["Paul Samuels"]
10
+ gem.email = ["paulio1987@gmail.com"]
11
+ gem.description = %q{Build an NSUserDefaults category using a simple DSL}
12
+ gem.summary = %q{Build an NSUserDefaults category using a simple DSL}
13
+ gem.homepage = "https://github.com/paulsamuels/Defgen"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ gem.add_dependency 'xcodeproj'
20
+ gem.add_development_dependency "rspec"
21
+ gem.add_development_dependency "guard"
22
+ gem.add_development_dependency "guard-rspec"
23
+ gem.add_development_dependency "rb-fsevent"
24
+ end
Binary file
data/lib/defgen/cli.rb ADDED
@@ -0,0 +1,47 @@
1
+ require 'Thor'
2
+ require 'fileutils'
3
+
4
+ module Defgen
5
+ class CLI < Thor
6
+
7
+ desc "stubs", "Generate template stubs"
8
+ def stubs
9
+ TemplateGenerator.create_stubs
10
+ end
11
+
12
+ desc "parse", 'Parse a "Defaultfile" and modify the Xcodeproj'
13
+ method_option :filename, default: 'Defaultfile',
14
+ aliases: '-f',
15
+ desc: 'Specify the input file path'
16
+ def parse
17
+
18
+ path = Dir['*.xcodeproj'].first
19
+
20
+ if path.nil?
21
+ puts('.xcodeproj file not found')
22
+ puts
23
+ help
24
+ exit 1
25
+ end
26
+
27
+ unless File.exist? options[:filename]
28
+ puts('no Defaultfile found')
29
+ puts
30
+ help
31
+ exit 1
32
+ end
33
+
34
+ proj = XcodeprojAdapter.new(Xcodeproj::Project.new(path), path)
35
+
36
+ defaultfile = DefaultfileBuilder.build { |builder|
37
+ builder.project_settings = proj
38
+ builder.parser = DefaultfileParser
39
+ builder.defaultfile = File.open(options[:filename]).read
40
+ }
41
+
42
+ Installer.install(path, defaultfile, proj)
43
+ end
44
+
45
+ default_task :parse
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ require 'delegate'
2
+
3
+ module Defgen
4
+ class GetterDecorator < SimpleDelegator
5
+ def objc_type
6
+ super.strip
7
+ end
8
+
9
+ def prefix
10
+ super.downcase
11
+ end
12
+
13
+ def get_binding
14
+ binding
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Defgen
2
+ class SetterDecorator < GetterDecorator
3
+ def message_prefix
4
+ object_setter ? 'Object' : super.upcase_first
5
+ end
6
+
7
+ def prefix
8
+ super.downcase.upcase_first
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,2 @@
1
+ require 'defgen/decorators/getter_decorator'
2
+ require 'defgen/decorators/setter_decorator'
@@ -0,0 +1,65 @@
1
+ require 'date'
2
+
3
+ module Defgen
4
+ class Defaultfile
5
+ attr_accessor :project_settings
6
+ attr_reader :class_properties
7
+
8
+ def initialize class_properties, project_settings
9
+ @class_properties = class_properties
10
+ self.project_settings = project_settings
11
+ end
12
+
13
+ def to_iface
14
+ render TemplateGenerator.interface_template
15
+ end
16
+
17
+ def to_imp
18
+ render TemplateGenerator.implementation_template
19
+ end
20
+
21
+ private
22
+
23
+ def render template
24
+ prefix = project_settings.prefix
25
+ project_name = project_settings.name
26
+ author = project_settings.organization
27
+
28
+ ERB.new(template, nil, '<>').result binding
29
+ end
30
+
31
+ def key_constants
32
+ padding = class_properties.max_by { |p| p.key.length }.key.length
33
+ constant_template = TemplateGenerator.constant_template
34
+
35
+ String.new.tap { |out|
36
+ class_properties.each do |property|
37
+ out << ERB.new(constant_template).result(binding)
38
+ end
39
+ }
40
+ end
41
+
42
+ def accessors
43
+ getter_template = TemplateGenerator.getter_template
44
+ setter_template = TemplateGenerator.setter_template
45
+
46
+ String.new.tap { |out|
47
+ class_properties.each do |property|
48
+ out << ERB.new(getter_template).result(GetterDecorator.new(property).get_binding) + "\n"
49
+ out << ERB.new(setter_template).result(SetterDecorator.new(property).get_binding)
50
+ out << "\n" unless class_properties.last == property
51
+ end
52
+ }
53
+ end
54
+
55
+ def properties
56
+ property_template = TemplateGenerator.property_template
57
+
58
+ String.new.tap { |out|
59
+ class_properties.each do |property|
60
+ out << ERB.new(property_template).result(property.get_binding)
61
+ end
62
+ }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,14 @@
1
+ module Defgen
2
+ class DefaultfileBuilder
3
+ attr_accessor :parser, :project_settings, :defaultfile
4
+
5
+ def self.build
6
+ new.tap { |instance| yield(instance) }.build
7
+ end
8
+
9
+ def build
10
+ properties = parser.parse(defaultfile).each do |property| property.prefix = project_settings.prefix end
11
+ Defaultfile.new properties, project_settings
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,41 @@
1
+ module Defgen
2
+ class DefaultfileParser
3
+ def self.parse string
4
+ new.parse string
5
+ end
6
+
7
+ def initialize
8
+ @properties = []
9
+ end
10
+
11
+ [
12
+ [ 'array', 'array', 'NSArray *', :strong, true ],
13
+ [ 'bool', 'bool', 'BOOL ', :assign, false ],
14
+ [ 'data', 'data', 'NSData *', :strong, true ],
15
+ [ 'dictionary', 'dictionary', 'NSDictionary *', :strong, true ],
16
+ [ 'float', 'float', 'float ', :assign, false ],
17
+ [ 'integer', 'integer', 'NSInteger ', :assign, false ],
18
+ [ 'object', 'object', 'id ', :strong, true ],
19
+ [ 'string', 'string', 'NSString *', :copy, true ],
20
+ [ 'double', 'double', 'double ', :assign, false ],
21
+ [ 'url', 'URL', 'NSURL *', :strong, false ]
22
+ ].each do |method_name, prefix, objc_type, ownership, object_setter|
23
+ define_method method_name do |name|
24
+ Property.new do |property, attributes|
25
+ property.name = name
26
+ property.object_setter = object_setter
27
+ property.message_prefix = prefix
28
+ property.objc_type = objc_type
29
+ attributes.ownership = ownership
30
+
31
+ @properties << property
32
+ end
33
+ end
34
+ end
35
+
36
+ def parse string
37
+ instance_eval string
38
+ @properties
39
+ end
40
+ end
41
+ end
Binary file
@@ -0,0 +1,38 @@
1
+ require 'thor/group'
2
+
3
+ class TemplateGenerator < Thor::Group
4
+ include Thor::Actions
5
+
6
+ def self.source_root
7
+ File.join(File.dirname(__FILE__), 'templates')
8
+ end
9
+
10
+ templates = Dir.glob("#{source_root}/*.erb")
11
+ TEMPLATES = templates.map { |f| File.basename(f, '.erb') }
12
+
13
+ TEMPLATES.each do |template|
14
+ const_set "#{template.upcase}_SOURCE", File.join(source_root, "#{template}.erb")
15
+ const_set "#{template.upcase}_DEST", "defgen/#{template}.erb"
16
+
17
+ self.class.send :define_method, "#{template}_template" do
18
+ load_template const_get("#{template.upcase}_SOURCE"), const_get("#{template.upcase}_DEST")
19
+ end
20
+ end
21
+
22
+ def self.create_stubs
23
+ new.create_stubs
24
+ end
25
+
26
+ def create_stubs
27
+ TEMPLATES.map(&:upcase).each do |template_file|
28
+ template self.class.const_get("#{template_file}_SOURCE"), self.class.const_get("#{template_file}_DEST")
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def self.load_template source, destination
35
+ return File.open(destination).read if File.exist? destination
36
+ ERB.new(File.open(source).read).result
37
+ end
38
+ end
@@ -0,0 +1 @@
1
+ NSString * const <%%= property.key.ljust(padding) %> = @"<%%= property.key %>";
@@ -0,0 +1,4 @@
1
+ - (<%%= objc_type %>)<%%= prefix %>_<%%= parameter %>;
2
+ {
3
+ return [self <%%= message_prefix %>ForKey:<%%= key %>];
4
+ }
@@ -0,0 +1,19 @@
1
+ //
2
+ // NSUserDefaults+<%%= prefix %>Properties.m
3
+ // <%%= project_name %>
4
+ //
5
+ // Created by <%%= author %> on <%= Date.today.strftime('%d/%m/%Y') %>.
6
+ // Copyright (c) <%= Date.today.year %> <%%= author %>. All rights reserved.
7
+ //
8
+ // This is an autogenerated file any changes will be lost when running `defgen`
9
+ //
10
+
11
+ #import "NSUserDefaults+<%%= prefix %>Properties.h"
12
+
13
+ <%%= key_constants %>
14
+
15
+ @implementation NSUserDefaults (<%%= prefix %>Properties)
16
+
17
+ <%%= accessors %>
18
+
19
+ @end
@@ -0,0 +1,17 @@
1
+ //
2
+ // NSUserDefaults+<%%= prefix %>Properties.h
3
+ // <%%= project_name %>
4
+ //
5
+ // Created by <%%= author %> on <%= Date.today.strftime('%d/%m/%Y') %>.
6
+ // Copyright (c) <%= Date.today.year %> <%%= author %>. All rights reserved.
7
+ //
8
+ // This is an autogenerated file any changes will be lost when running `defgen`
9
+ //
10
+
11
+ #import <Foundation/Foundation.h>
12
+
13
+ @interface NSUserDefaults (<%%= prefix %>Properties)
14
+
15
+ <%%= properties %>
16
+
17
+ @end
@@ -0,0 +1 @@
1
+ @property <%%= attributes %> <%%= objc_type %><%%= prefix.downcase %>_<%%= name %>;
@@ -0,0 +1,4 @@
1
+ - (void)set<%%= prefix %>_<%%= name %>:(<%%= objc_type %>)<%%= parameter %>;
2
+ {
3
+ [self set<%%= message_prefix %>:<%%= parameter %> forKey:<%%= key %>];
4
+ }
@@ -0,0 +1 @@
1
+ require 'defgen/generators/template/template_generator'
@@ -0,0 +1 @@
1
+ require 'defgen/generators/template'