rest_kat 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rest_kat.rb ADDED
@@ -0,0 +1,342 @@
1
+ module RestKit
2
+ class Resource
3
+ attr_accessor :hash
4
+ def initialize resource
5
+ self.hash = resource
6
+ end
7
+
8
+ def objc_resource_type
9
+ hash[:type][:name]
10
+ end
11
+
12
+ def objc_class
13
+ "MSRestResource#{hash[:name].camelize}"
14
+ end
15
+
16
+ def permissions
17
+ hash[:permissions]
18
+ end
19
+
20
+ def c_permission_for type
21
+ if permissions.include? type
22
+ "true"
23
+ else
24
+ "false"
25
+ end
26
+ end
27
+
28
+ def singleton?
29
+ @singleton ||= permissions.include? 's'
30
+ end
31
+
32
+ def create?
33
+ @create ||= permissions.include? 'c'
34
+ end
35
+
36
+ def read?
37
+ @read ||= permissions.include? 'r'
38
+ end
39
+
40
+ def update?
41
+ @update ||= permissions.include? 'u'
42
+ end
43
+
44
+ def delete?
45
+ @delete ||= permissions.include? 'd'
46
+ end
47
+
48
+ def queries &block
49
+ if hash.has_key?("queries")
50
+ if block_given?
51
+ hash["queries"].each &block
52
+ else
53
+ hash["queries"]
54
+ end
55
+ else
56
+ []
57
+ end
58
+ end
59
+
60
+ def c_bool r
61
+ if r
62
+ "true"
63
+ else
64
+ "false"
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+ class ObjCProperty
71
+ attr_accessor :klass
72
+ attr_accessor :name
73
+ def initialize(klass, name)
74
+ raise Exception.new(":klass parameter cannot be nil") unless klass
75
+ raise Exception.new(":name parameter cannot be nil") unless name
76
+ self.klass = klass
77
+ self.name = name
78
+ end
79
+ end
80
+
81
+ class ObjCClass
82
+ attr_accessor :properties
83
+ attr_accessor :sequence_of
84
+ attr_accessor :type
85
+ attr_accessor :parent
86
+ attr_accessor :json_type
87
+ attr_accessor :node
88
+ attr_accessor :resource
89
+
90
+ def objc_super_class
91
+ if resource
92
+ "MSRestSerializableResource"
93
+ else
94
+ "MSRestSerializable"
95
+ end
96
+ end
97
+
98
+ def complex?
99
+ map? || seq?
100
+ end
101
+
102
+ def map?
103
+ properties && (not properties.empty?)
104
+ end
105
+
106
+ def seq?
107
+ !!sequence_of
108
+ end
109
+
110
+ def objc_class
111
+ self.type
112
+ end
113
+
114
+ def enum?
115
+ (node["type"] == "str") && node.has_key?("enum")
116
+ end
117
+
118
+ def enum
119
+ node["enum"]
120
+ end
121
+
122
+ def objc_property_decl name
123
+ "@property (nonatomic, strong) #{objc_class} * #{name}"
124
+ end
125
+
126
+ def objc_property_arg_decl name
127
+ "#{name}: (#{objc_class} *) #{name}"
128
+ end
129
+
130
+ def objc_properites_arg_list_decl
131
+ properties.reject{|p| p.name == 'id'}.map do |p|
132
+ p.klass.objc_property_arg_decl p.name
133
+ end.join "\n "
134
+ end
135
+
136
+ def initialize type, json_type, node
137
+ self.properties = nil
138
+ self.type = type
139
+ self.json_type = json_type
140
+ self.node = node
141
+ end
142
+
143
+ end
144
+
145
+ class IosMapping
146
+
147
+ def initialize api_schema
148
+ raise "#{api_schema} does not exist" unless File.exist?(api_schema)
149
+ @schema_file = api_schema
150
+ end
151
+
152
+ def resources_from_schema
153
+ schema[:resources]
154
+ end
155
+
156
+ def schema
157
+ require 'kwalify'
158
+ @schema ||= HashWithIndifferentAccess.new Kwalify::Yaml.load_file(@schema_file)
159
+ end
160
+
161
+ def classes
162
+ @classes ||= []
163
+ end
164
+
165
+ def resources
166
+ @resources ||= []
167
+ end
168
+
169
+ def template name
170
+ require 'erb'
171
+ ERB.new File.read(File.expand_path(File.join("..", name), __FILE__)), 0, '<>'
172
+ end
173
+
174
+ def h_template
175
+ template "model.h.erb"
176
+ end
177
+
178
+ def m_template
179
+ template "model.m.erb"
180
+ end
181
+
182
+ # Generate the required objective c class AST's for th +name+ resource in
183
+ # the correct order so they can be written to code in the correct order in
184
+ # the templates
185
+ def generate_objective_c_classes
186
+ @classes = []
187
+ resources_from_schema.collect do |resource_hash|
188
+ Resource.new(resource_hash).tap do |resource|
189
+ # Generate the query classes first
190
+ resource.queries.each do |query|
191
+ to_objective_c_class query["type"]
192
+ end
193
+ # Generate the resource classes next
194
+ klass = to_objective_c_class resource.hash[:type]
195
+ # Mark this class as being a REST resource
196
+ klass.resource = resource
197
+ end
198
+ end
199
+ end
200
+
201
+ # Generate the objective C header for +name+ resource
202
+ def to_h name
203
+ @resources = generate_objective_c_classes
204
+ h_template.result binding
205
+ end
206
+
207
+ # Generate the objective C implementation file for +name+ resource
208
+ def to_m name
209
+ @resources = generate_objective_c_classes
210
+ #TODO below 'header' must change
211
+ header = (File.basename name, ".*") + ".h"
212
+ m_template.result binding
213
+ end
214
+
215
+ def self.obj_c_type_for_property parent_node, property_name
216
+ parent_type = parent_node[:name].gsub /MSRest/, ''
217
+ "MSRest#{parent_type.camelize}#{property_name.camelize}"
218
+ end
219
+
220
+ def find_processed_class node
221
+ classes.find{|c| c.type == node[:name]}
222
+ end
223
+
224
+ # Perform a depth first traversal of the type tree. The resulting
225
+ # classes array will have the top level class last in the array. In
226
+ # the code generation phase this classes will be injected last into
227
+ # the code making sure the dependencies are define in the correct
228
+ # order.
229
+ def to_objective_c_class node
230
+ unless node
231
+ raise Exception.new("node is nil for name '#{name}'")
232
+ end
233
+
234
+ case node[:type]
235
+ when "map"
236
+
237
+ unless klass = find_processed_class(node)
238
+ klass = ObjCClass.new(node[:name], node[:type], node)
239
+
240
+ klass.properties = node[:mapping].collect do |property_name, property_node|
241
+ if property_node[:type] == "map"
242
+ property_node[:name] ||= IosMapping.obj_c_type_for_property(node, property_name)
243
+ end
244
+ ObjCProperty.new(to_objective_c_class(property_node), property_name)
245
+ end
246
+
247
+ self.classes << klass
248
+ end
249
+
250
+ when "seq"
251
+ klass = ObjCClass.new(node[:name] || "NSArray", node[:type], node)
252
+ if node[:sequence].length != 1
253
+ raise "Only support sequence of map with one map type"
254
+ end
255
+ sequence_node = node[:sequence].first
256
+ if sequence_node["type"] == "map"
257
+ if not sequence_node["name"]
258
+ raise Exception.new ("sequence of map nodes must have a :name to generate the objective C types")
259
+ end
260
+ klass.sequence_of = to_objective_c_class sequence_node
261
+ end
262
+ when "str", "text"
263
+ klass = ObjCClass.new(node[:name] || "NSString", node[:type], node)
264
+ when "int"
265
+ klass = ObjCClass.new(node[:name] || "NSNumber", node[:type], node)
266
+ when "float"
267
+ klass = ObjCClass.new(node[:name] || "NSNumber", node[:type], node)
268
+ when "bool"
269
+ klass = ObjCClass.new(node[:name] || "NSNumber", node[:type], node)
270
+ when "any"
271
+ klass = ObjCClass.new(node[:name] || "NSObject", node[:type], node)
272
+ else
273
+ raise Exception.new("Unhandled type '#{node[:type]} for node with name #{node[:name]}")
274
+ end
275
+
276
+ raise Exception.new("klass cannot be nil") unless klass
277
+
278
+ klass
279
+
280
+ end
281
+ end
282
+ end
283
+
284
+ module RestKat
285
+
286
+ class MySugrIphone
287
+ class <<self
288
+ def root
289
+ ENV["MYSUGR_IPHONE_ROOT"]
290
+ end
291
+ end
292
+ end
293
+
294
+ def self.generate_item(api_location, schema_location, ext)
295
+ deps = %w[
296
+ model.h.erb
297
+ model.m.erb
298
+ rest_kat.rb
299
+ validator.rb
300
+ ].collect do |d|
301
+ File.expand_path "../#{d}", __FILE__
302
+ end
303
+
304
+ deps << schema_location
305
+
306
+ file = "#{api_location}.#{ext}"
307
+
308
+ file_task = Rake::FileTask.define_task file => deps do
309
+ File.open file, 'w' do |f|
310
+ puts "Generating #{file}"
311
+ f.write RestKit::IosMapping.new(schema_location).send("to_#{ext}", file)
312
+ end
313
+ end
314
+ end
315
+
316
+ def self.generate_api(api_location, schema_location)
317
+
318
+ api_src = File.join api_location, "MSRestApiAutoGen"
319
+ m_file_task = generate_item(api_src, schema_location, "h")
320
+ h_file_task = generate_item(api_src, schema_location, "m")
321
+
322
+ src_path = File.expand_path "../../src", __FILE__
323
+
324
+ src_h = File.join src_path, "MSRestSerializable.h"
325
+ src_m = File.join src_path, "MSRestSerializable.m"
326
+
327
+ tgt_h = File.join api_location, "MSRestSerializable.h"
328
+ tgt_m = File.join api_location, "MSRestSerializable.m"
329
+
330
+
331
+ t0 = file tgt_h, src_h do
332
+ cp src_h, tgt_h, :verbose => true
333
+ end
334
+
335
+ t1 = file tgt_m, src_m do
336
+ cp src_m, tgt_m, :verbose => true
337
+ end
338
+
339
+ Rake::Task.define_task :type => [m_file_task, h_file_task, t0, t1]
340
+ end
341
+
342
+ end
File without changes
data/lib/validator.rb ADDED
@@ -0,0 +1,53 @@
1
+ require 'kwalify'
2
+
3
+ module RestKit
4
+ ## validator class for answers
5
+ class Validator < Kwalify::Validator
6
+ ## load schema definition
7
+ @@schema = Kwalify::Yaml.load_file( File.join Rails.root, "lib/rest_kit/api_schema.yml" )
8
+ def self.schema
9
+ @@schema
10
+ end
11
+
12
+ def resource_schema
13
+ @resource_schema
14
+ end
15
+
16
+ def initialize type
17
+ super (@resource_schema=type)
18
+ end
19
+
20
+ def self.find_resource resource
21
+ Validator.schema["resources"].find do |r|
22
+ r["name"].to_s == resource.to_s
23
+ end
24
+ end
25
+
26
+ def self.for_resource resource
27
+ type = find_resource(resource)["type"]
28
+ raise Exception.new("Can't find resource '#{resource}'") unless type
29
+ pp type
30
+ Validator.new type
31
+ end
32
+
33
+ def self.for_type type
34
+ type = Validator.schema[type.to_s]
35
+ raise Exception.new("Can't find resource '#{resource}'") unless type
36
+ pp type
37
+ Validator.new type
38
+ end
39
+
40
+ def self.for_resource_collection resource
41
+ type = find_resource(resource)["type"]
42
+ raise Exception.new("Can't find resource '#{resource}'") unless type
43
+ collection_type = {
44
+ "type" => "seq",
45
+ "sequence" => [ type ]
46
+ }
47
+
48
+ Validator.new collection_type
49
+ end
50
+
51
+ end
52
+
53
+ end
data/rest_kat.gemspec ADDED
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rest_kat}
8
+ s.version = "0.0.4"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Brad Phelan}]
12
+ s.date = %q{2011-11-18}
13
+ s.description = %q{The generated source code is a layer on top of the iOS REST framework http://restkit.org/}
14
+ s.email = %q{brad.phelan@mysugr.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".rspec",
23
+ ".rvmrc",
24
+ "Gemfile",
25
+ "LICENSE.txt",
26
+ "README.md",
27
+ "README.rdoc",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "lib/model.h.erb",
31
+ "lib/model.m.erb",
32
+ "lib/rest_kat.rb",
33
+ "lib/tasks/iphone.rake",
34
+ "lib/validator.rb",
35
+ "rest_kat.gemspec",
36
+ "spec/rest_kat_spec.rb",
37
+ "spec/spec_helper.rb",
38
+ "src/MSRestSerializable.h",
39
+ "src/MSRestSerializable.m"
40
+ ]
41
+ s.homepage = %q{http://github.com/mysugr/rest_kat}
42
+ s.licenses = [%q{MIT}]
43
+ s.require_paths = [%q{lib}]
44
+ s.rubygems_version = %q{1.8.8}
45
+ s.summary = %q{Generate objective C RestKit boilerplate code from a yaml schema}
46
+
47
+ if s.respond_to? :specification_version then
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<kwalify>, [">= 0"])
52
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
53
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
54
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
55
+ s.add_development_dependency(%q<rcov>, [">= 0"])
56
+ else
57
+ s.add_dependency(%q<kwalify>, [">= 0"])
58
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
59
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
60
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
61
+ s.add_dependency(%q<rcov>, [">= 0"])
62
+ end
63
+ else
64
+ s.add_dependency(%q<kwalify>, [">= 0"])
65
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
66
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
67
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
68
+ s.add_dependency(%q<rcov>, [">= 0"])
69
+ end
70
+ end
71
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "RestKat" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'rest_kat'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,79 @@
1
+ //
2
+ // NSApiBase.h
3
+ // MySugr
4
+ //
5
+ // Created by brad phelan on 10/4/11.
6
+ // Copyright (c) 2011 __MyCompanyName__. All rights reserved.
7
+ //
8
+
9
+ #import <Foundation/Foundation.h>
10
+ #import "NSDate+APIDate.h"
11
+ #import <RestKit/RestKit.h>
12
+
13
+ @interface MSRestSerializable : NSObject<NSCopying>
14
+ // The mapper for this instance
15
+ - (RKObjectMapping *) _mapper;
16
+
17
+ // The serializer for this instance
18
+ - (RKObjectMapping *) _serializer;
19
+
20
+ - (NSDictionary *)convertToDictionary;
21
+
22
+ - (NSString *)convertToFormURLEncoded;
23
+
24
+ - (NSString *)convertToJSON;
25
+
26
+ - (BOOL)isNew;
27
+ @end
28
+
29
+ @interface MSRestSerializableResource : MSRestSerializable
30
+
31
+ #pragma mark methods to be overridden
32
+
33
+ + (Class) classForResource;
34
+
35
+ // The resource path. If this
36
+ + (NSString *) resourcePath;
37
+
38
+ // Is the resource a singleton or a collection
39
+ + (bool) isSingleton;
40
+
41
+ // Can the client create resources
42
+ + (bool) canCreate;
43
+
44
+ // Can the client read resources
45
+ + (bool) canRead;
46
+
47
+ // Can the client update resources
48
+ + (bool) canUpdate;
49
+
50
+ // Can the client delete resources
51
+ + (bool) canDelete;
52
+
53
+ #pragma mark helpers
54
+
55
+ // The router for this class
56
+ + (RKObjectRouter *) router;
57
+
58
+ // Intialize the routing module. Must
59
+ // be called from +initialize in a
60
+ // subclass or the logic will not
61
+ // work.
62
+ + (void) initializeRouting;
63
+
64
+
65
+ // save the object
66
+ -(void) saveWithDelegate:(id<RKObjectLoaderDelegate>)delegate;
67
+
68
+ // Find an instance by id
69
+ + (void) find:(NSNumber *)id
70
+ withDelegate:(id<RKObjectLoaderDelegate>)delegate;
71
+
72
+ // Load collection
73
+ + (void) loadCollectionWithDelegate:(id<RKObjectLoaderDelegate>)delegate;
74
+
75
+ // Load collection with query
76
+ + (void) loadCollectionThroughQuery:(MSRestSerializable *)mappableQueryObject
77
+ withDelegate:(id<RKObjectLoaderDelegate>)delegate;
78
+
79
+ @end