apimaster 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +12 -0
  3. data/apimaster.gemspec +14 -0
  4. data/bin/apimaster +6 -0
  5. data/lib/apimaster.rb +21 -0
  6. data/lib/apimaster/application.rb +28 -0
  7. data/lib/apimaster/controllers/errors.rb +34 -0
  8. data/lib/apimaster/error.rb +79 -0
  9. data/lib/apimaster/generators/application.rb +112 -0
  10. data/lib/apimaster/generators/base.rb +206 -0
  11. data/lib/apimaster/generators/command.rb +697 -0
  12. data/lib/apimaster/generators/manifest.rb +51 -0
  13. data/lib/apimaster/generators/options.rb +162 -0
  14. data/lib/apimaster/generators/scripts.rb +64 -0
  15. data/lib/apimaster/generators/simple_logger.rb +44 -0
  16. data/lib/apimaster/generators/templates/Gemfile +21 -0
  17. data/lib/apimaster/generators/templates/LICENSE +20 -0
  18. data/lib/apimaster/generators/templates/README.md +10 -0
  19. data/lib/apimaster/generators/templates/Rakefile +19 -0
  20. data/lib/apimaster/generators/templates/TODO +4 -0
  21. data/lib/apimaster/generators/templates/app/controllers/index_controller.rb.erb +7 -0
  22. data/lib/apimaster/generators/templates/config.ru.erb +8 -0
  23. data/lib/apimaster/generators/templates/config/application.rb.erb +19 -0
  24. data/lib/apimaster/generators/templates/config/boot.rb.erb +17 -0
  25. data/lib/apimaster/generators/templates/config/initializer.rb.erb +13 -0
  26. data/lib/apimaster/generators/templates/config/patches.rb.erb +0 -0
  27. data/lib/apimaster/generators/templates/config/settings/app.yml.erb +3 -0
  28. data/lib/apimaster/generators/templates/config/settings/mongoid.yml.erb +66 -0
  29. data/lib/apimaster/generators/templates/config/settings/oauth.yml.erb +8 -0
  30. data/lib/apimaster/generators/templates/gitignore +10 -0
  31. data/lib/apimaster/generators/templates/lib/module.rb.erb +6 -0
  32. data/lib/apimaster/generators/templates/test/functional_test.rb.erb +2 -0
  33. data/lib/apimaster/generators/templates/test/test_helper.rb.erb +1 -0
  34. data/lib/apimaster/generators/templates/test/unit_test.rb.erb +2 -0
  35. data/lib/apimaster/generators/version.rb +3 -0
  36. data/lib/apimaster/helpers/headers.rb +30 -0
  37. data/lib/apimaster/helpers/request.rb +49 -0
  38. data/lib/apimaster/helpers/session.rb +36 -0
  39. data/lib/apimaster/mapper.rb +86 -0
  40. data/lib/apimaster/models/user.rb +33 -0
  41. data/lib/apimaster/models/user_mock.rb +13 -0
  42. data/lib/apimaster/setting.rb +68 -0
  43. metadata +45 -3
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Admaster Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,12 @@
1
+ API Master
2
+ ==========
3
+
4
+ A simple restful api framework the provides a gem that...
5
+
6
+ Developing
7
+ ----------
8
+
9
+ % gem install apimaster
10
+ % mkdir your_app_name & cd your_app_name
11
+ % apimaster install your_app_name
12
+ % bundle install
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'apimaster'
3
+ s.version = '0.0.2'
4
+ s.date = '2012-07-06'
5
+ s.summary = "ApiMaster!"
6
+ s.description = "A simple restful api framework."
7
+ s.authors = ["Sun", "Zhang", "Li"]
8
+ s.email = 'sunxiqiu@admaster.com.cn'
9
+ s.files = Dir['{bin/*,lib/**/*, test/*}'] +
10
+ %w(LICENSE README.md apimaster.gemspec)
11
+ s.homepage = 'http://rubygems.org/gems/apimaster'
12
+ s.executables << 'apimaster'
13
+ #s.add_dependency "erb"
14
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'apimaster/generators/scripts'
5
+
6
+ Apimaster::Generators::Scripts::Base.new.run(ARGV)
@@ -1 +1,22 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (C) 2011-2012 AdMaster, Inc.
1
4
 
5
+ module Apimaster end
6
+ module Apimaster::Helpers end
7
+ module Apimaster::Controllers end
8
+ module Apimaster::Models end
9
+
10
+ require_relative './apimaster/setting'
11
+ require_relative './apimaster/error'
12
+ require_relative './apimaster/mapper'
13
+
14
+ require_relative './apimaster/helpers/headers.rb'
15
+ require_relative './apimaster/helpers/request.rb'
16
+ require_relative './apimaster/helpers/session.rb'
17
+
18
+ require_relative './apimaster/models/user.rb'
19
+ require_relative './apimaster/models/user_mock.rb'
20
+
21
+ require_relative './apimaster/controllers/errors.rb'
22
+ require_relative './apimaster/application'
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (C) 2011-2012 AdMaster, Inc.
4
+
5
+ module Apimaster
6
+ class Application < Sinatra::Base
7
+
8
+ # Helpers
9
+ superclass.helpers Sinatra::JSON
10
+ superclass.helpers Apimaster::Helpers::Request
11
+ superclass.helpers Apimaster::Helpers::Headers
12
+ superclass.helpers Apimaster::Helpers::Session
13
+
14
+ superclass.configure :development do
15
+ superclass.register Sinatra::Reloader
16
+ superclass.also_reload "./app/{controllers,models,helpers}/**/*.rb"
17
+ end
18
+
19
+ superclass.configure do
20
+ superclass.set :root, ::File.expand_path(".")
21
+ superclass.set :json_encoder, :to_json
22
+ superclass.set :show_exceptions, false
23
+ end
24
+
25
+ use Apimaster::Controllers::Errors
26
+
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (C) 2011-2012 AdMaster, Inc.
4
+
5
+ module Apimaster::Controllers
6
+
7
+ class Errors < Sinatra::Base
8
+
9
+ superclass.error Apimaster::NormalError do
10
+ e = env['sinatra.error']
11
+ error = [:resource, :code, :field].inject({}) do |err, val|
12
+ if e.respond_to?(val) and v = e.send(val)
13
+ err[val] = v
14
+ end
15
+ err
16
+ end
17
+
18
+ messages = {:message => e.message}
19
+ messages[:errors] = [error] unless error.empty?
20
+ json messages
21
+ end
22
+
23
+ superclass.error do
24
+ raise env['sinatra.error'] if development?
25
+ json :message => "Internal Server Error"
26
+ end
27
+
28
+ not_found do
29
+ json message: "Not Found"
30
+ end
31
+
32
+ end
33
+ end
34
+
@@ -0,0 +1,79 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Copyright (C) 2011-2012 AdMaster, Inc.
4
+ #
5
+ # @author: sunxiqiu@admaster.com.cn
6
+
7
+ module Apimaster
8
+
9
+ class NormalError < StandardError
10
+ attr_reader :code
11
+ attr_reader :error
12
+ attr_reader :resource
13
+ attr_reader :field
14
+
15
+ # error :missing, :missing_field, :invalid, :already_exists
16
+ def initialize(message = '', code = nil, error = :invalid, resource = nil, field = nil)
17
+ @code = code
18
+ @error = error.to_sym
19
+ @resource = resource
20
+ @field = field
21
+ super(message)
22
+ end
23
+ end
24
+
25
+ class MissingError < NormalError
26
+ def initialize(resource = nil)
27
+ super("Resource '#{resource}' does not exist.", 404, :missing, resource)
28
+ end
29
+ end
30
+
31
+ class MissingFieldError < NormalError
32
+ def initialize(resource = nil, field = nil)
33
+ super("Required field '#{field}' on a resource '#{resource}' has not been set.", 422, :missing_field, resource, field)
34
+ end
35
+ end
36
+
37
+ class InvalidFieldError < NormalError
38
+ def initialize(resource = nil, field = nil)
39
+ super("The formatting of the field '#{field}' on a resource '#{resource}' is invalid.", 422, :invalid, resource, field)
40
+ end
41
+ end
42
+
43
+ class AlreadyExistsError < NormalError
44
+ def initialize(resource = nil, field = nil)
45
+ super("Another resource '#{resource}' has the same value as this field '#{field}'. ", 409, :already_exists, resource, field)
46
+ end
47
+ end
48
+
49
+ class RelationExistsError < NormalError
50
+ def initialize(resource = nil, field = nil)
51
+ super("Our results indicate a positive relationship exists.", 409, :relation_exists, resource, field)
52
+ end
53
+ end
54
+
55
+ class RequestError < StandardError
56
+ def initialize(resource = nil, field = nil)
57
+ super("Problems parsing JSON.", 400, :parse_error, resource, field)
58
+ end
59
+ end
60
+
61
+ class UnauthorizedError < NormalError
62
+ def initialize(resource = nil, field = nil)
63
+ super("Your authorization token were invalid.", 401, :unauthorized, resource, field)
64
+ end
65
+ end
66
+
67
+ class PermissionDeniedError < NormalError
68
+ def initialize(resource = nil, field = nil)
69
+ super("Permission denied to access resource '#{resource}'.", 403, :forbidden, resource, field)
70
+ end
71
+ end
72
+
73
+ class OauthError < NormalError
74
+ def initialize(message)
75
+ super(message, 422, :oauth_error)
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,112 @@
1
+ require 'rbconfig'
2
+
3
+ module Apimaster::Generators
4
+ class Application < Create
5
+
6
+ DEFAULT_SHEBANG = File.join(RbConfig::CONFIG['bindir'],
7
+ RbConfig::CONFIG['ruby_install_name'])
8
+
9
+ default_options :shebang => DEFAULT_SHEBANG,
10
+ :an_option => 'some_default'
11
+
12
+ attr_reader :app_name, :module_name
13
+
14
+ def initialize(runtime_args, runtime_options = {})
15
+ runtime_options[:source] = File.dirname(__FILE__) + '/templates'
16
+ runtime_options[:destination] = Dir.pwd
17
+ runtime_options[:collision] = :ask
18
+ runtime_options[:stdout] = STDOUT
19
+ super
20
+ usage if args.empty?
21
+ #@destination_root = args.shift
22
+ #@app_name = File.basename(File.expand_path(@destination_root))
23
+ @app_name = args[0]
24
+ raise 'Undefined app name.' unless @app_name
25
+ @module_name = camelize(app_name)
26
+ extract_options
27
+ end
28
+
29
+ def manifest
30
+ record do |m|
31
+ # Ensure appropriate folder(s) exists
32
+ m.directory ''
33
+ BASEDIRS.each { |path| m.directory path }
34
+ m.directory "lib/#{app_name}"
35
+
36
+ # config
37
+ m.template "config/boot.rb.erb", "config/boot.rb"
38
+ m.template "config/patches.rb.erb", "config/patches.rb"
39
+ m.template "config/initializer.rb.erb", "config/initializer.rb"
40
+ m.template "config/application.rb.erb", "config/application.rb"
41
+ m.template "config/settings/mongoid.yml.erb", "config/settings/mongoid.yml"
42
+ m.template "config/settings/app.yml.erb", "config/settings/app.yml"
43
+ m.template "config/settings/oauth.yml.erb", "config/settings/oauth.yml"
44
+
45
+ # Create stubs
46
+ m.template "config.ru.erb", "config.ru"
47
+ m.template "gitignore", ".gitignore"
48
+ m.template "lib/module.rb.erb", "lib/#{app_name}.rb"
49
+ m.template "app/controllers/index_controller.rb.erb", "app/controllers/index_controller.rb"
50
+
51
+ # Test stubs
52
+ m.template "test/test_helper.rb.erb", "test/test_helper.rb"
53
+ m.template "test/functional_test.rb.erb", "test/functional/index_controller_test.rb"
54
+ m.template "test/unit_test.rb.erb", "test/unit/#{app_name}_test.rb"
55
+
56
+ %w(LICENSE Rakefile README.md Gemfile TODO).each do |file|
57
+ m.template file, file
58
+ end
59
+ end
60
+ end
61
+
62
+ protected
63
+ def banner
64
+ <<-EOS
65
+ Creates a Apimaster scaffold.
66
+
67
+ USAGE: apimaster new your_app_name"
68
+ EOS
69
+ end
70
+
71
+ def add_options!(opts)
72
+ opts.separator ''
73
+ opts.separator 'Options:'
74
+ # For each option below, place the default
75
+ # at the top of the file next to "default_options"
76
+ # opts.on("-a", "--author=\"Your Name\"", String,
77
+ # "Some comment about this option",
78
+ # "Default: none") { |x| options[:author] = x }
79
+ opts.on("-v", "--version", "Show the #{File.basename($0)} version number and quit.")
80
+ end
81
+
82
+ def extract_options
83
+ # for each option, extract it into a local variable (and create an "attr_reader :author" at the top)
84
+ # Templates can access these value via the attr_reader-generated methods, but not the
85
+ # raw instance variable value.
86
+ # @author = options[:author]
87
+ end
88
+
89
+ # Installation skeleton. Intermediate directories are automatically
90
+ # created so don't sweat their absence here.
91
+ BASEDIRS = %w(
92
+ app
93
+ app/controllers
94
+ app/views
95
+ app/models
96
+ app/helpers
97
+ bin
98
+ config
99
+ config/settings
100
+ config/locales
101
+ doc
102
+ lib
103
+ log
104
+ test
105
+ test/unit
106
+ test/functional
107
+ test/factory
108
+ tmp
109
+ public
110
+ )
111
+ end
112
+ end
@@ -0,0 +1,206 @@
1
+ # Apimaster::Generators is a code generation platform Ruby frameworks.
2
+ # Generators are easily invoked within Ruby framework instances
3
+ # to add and remove components such as library and test files.
4
+ #
5
+ # New generators are easy to create and may be distributed within RubyGems,
6
+ # user home directory, or within each Ruby framework that uses Apimaster::Generators.
7
+ #
8
+ # For example, newgem uses Apimaster::Generators to generate new RubyGems. Those
9
+ # generated RubyGems can then use Apimaster::Generators (via a generated script/generate
10
+ # application) to generate tests and executable apps, etc, for the RubyGem.
11
+ #
12
+ # Generators may subclass other generators to provide variations that
13
+ # require little or no new logic but replace the template files.
14
+ #
15
+ # For a RubyGem, put your generator classes and templates within subfolders
16
+ # of the +generators+ directory.
17
+ #
18
+ # The layout of generator files can be seen in the built-in
19
+ # +test_unit+ generator:
20
+ #
21
+ # test_unit_generators/
22
+ # test_unit/
23
+ # test_unit_generator.rb
24
+ # templates/
25
+ # test_unit.rb
26
+ #
27
+ # The directory name (+test_unit+) matches the name of the generator file
28
+ # (test_unit_generator.rb) and class (+TestUnitGenerators+). The files
29
+ # that will be copied or used as templates are stored in the +templates+
30
+ # directory.
31
+ #
32
+ # The filenames of the templates don't matter, but choose something that
33
+ # will be self-explanatory since you will be referencing these in the
34
+ # +manifest+ method inside your generator subclass.
35
+ #
36
+ #
37
+ module Apimaster::Generators
38
+ class GeneratorsError < StandardError; end
39
+ class UsageError < GeneratorsError; end
40
+
41
+
42
+ # The base code generator is bare-bones. It sets up the source and
43
+ # destination paths and tells the logger whether to keep its trap shut.
44
+ #
45
+ # It's useful for copying files such as stylesheets, images, or
46
+ # javascripts.
47
+ #
48
+ # For more comprehensive template-based passive code generation with
49
+ # arguments, you'll want Apimaster::Generators::NamedBase.
50
+ #
51
+ # Generators create a manifest of the actions they perform then hand
52
+ # the manifest to a command which replays the actions to do the heavy
53
+ # lifting (such as checking for existing files or creating directories
54
+ # if needed). Create, destroy, and list commands are included. Since a
55
+ # single manifest may be used by any command, creating new generators is
56
+ # as simple as writing some code templates and declaring what you'd like
57
+ # to do with them.
58
+ #
59
+ # The manifest method must be implemented by subclasses, returning a
60
+ # Apimaster::Generators::Manifest. The +record+ method is provided as a
61
+ # convenience for manifest creation. Example:
62
+ #
63
+ # class StylesheetGenerators < Apimaster::Generators::Base
64
+ # def manifest
65
+ # record do |m|
66
+ # m.directory('public/stylesheets')
67
+ # m.file('application.css', 'public/stylesheets/application.css')
68
+ # end
69
+ # end
70
+ # end
71
+ #
72
+ # See Apimaster::Generators::Commands::Create for a list of methods available
73
+ # to the manifest.
74
+ class Base
75
+ include Options
76
+
77
+ # Declare default options for the generator. These options
78
+ # are inherited to subclasses.
79
+ default_options :collision => :ask, :quiet => false, :stdout => STDOUT
80
+
81
+ # A logger instance available everywhere in the generator.
82
+ attr_accessor :logger
83
+
84
+ # Either Apimaster::Generators::Base, or a subclass (e.g. Rails::Generators::Base)
85
+ # Currently used to determine the lookup paths via the overriden const_missing mechansim
86
+ # in lookup.rb
87
+ attr_accessor :active
88
+
89
+ # Every generator that is dynamically looked up is tagged with a
90
+ # Spec describing where it was found.
91
+ attr_accessor :spec
92
+ #class_attribute :spec
93
+
94
+ attr_reader :source_root, :destination_root, :args, :stdout
95
+
96
+ def initialize(runtime_args, runtime_options = {})
97
+ runtime_options[:backtrace] = true
98
+ @logger = SimpleLogger.new
99
+ @args = runtime_args
100
+ parse!(@args, runtime_options)
101
+
102
+ # Derive source and destination paths.
103
+ @source_root = options[:source] || File.join(spec.path, 'templates')
104
+ if options[:destination]
105
+ @destination_root = options[:destination]
106
+ end
107
+
108
+ # Silence the logger if requested.
109
+ #logger.quiet = options[:quiet]
110
+
111
+ @stdout = options[:stdout]
112
+
113
+ # Raise usage error if help is requested.
114
+ usage if options[:help]
115
+ end
116
+
117
+ # Generators must provide a manifest. Use the +record+ method to create
118
+ # a new manifest and record your generator's actions.
119
+ def manifest
120
+ raise NotImplementedError, "No manifest for '#{spec.name}' generator."
121
+ end
122
+
123
+ # Return the full path from the source root for the given path.
124
+ # Example for source_root = '/source':
125
+ # source_path('some/path.rb') == '/source/some/path.rb'
126
+ #
127
+ # The given path may include a colon ':' character to indicate that
128
+ # the file belongs to another generator. This notation allows any
129
+ # generator to borrow files from another. Example:
130
+ # source_path('model:fixture.yml') = '/model/source/path/fixture.yml'
131
+ def source_path(relative_source)
132
+ # Check whether we're referring to another generator's file.
133
+ name, path = relative_source.split(':', 2)
134
+
135
+ # If not, return the full path to our source file.
136
+ if path.nil?
137
+ File.join(source_root, name)
138
+
139
+ # Otherwise, ask our referral for the file.
140
+ else
141
+ # FIXME: this is broken, though almost always true. Others'
142
+ # source_root are not necessarily the templates dir.
143
+ File.join(self.class.lookup(name).path, 'templates', path)
144
+ end
145
+ end
146
+
147
+ # Return the full path from the destination root for the given path.
148
+ # Example for destination_root = '/dest':
149
+ # destination_path('some/path.rb') == '/dest/some/path.rb'
150
+ def destination_path(relative_destination)
151
+ File.expand_path(File.join(destination_root, relative_destination))
152
+ end
153
+
154
+ # Return the basename of the destination_root,
155
+ # BUT, if it is trunk, tags, or branches, it continues to the
156
+ # parent path for the name
157
+ def base_name
158
+ name = File.basename(destination_root)
159
+ root = destination_root
160
+ while %w[trunk branches tags].include? name
161
+ root = File.expand_path(File.join(root, ".."))
162
+ name = File.basename(root)
163
+ end
164
+ name
165
+ end
166
+
167
+ def after_generate
168
+ end
169
+
170
+ # Run the generator script. Takes an array of unparsed arguments
171
+ # and a hash of parsed arguments, takes the generator as an option
172
+ # or first remaining argument, and invokes the requested command.
173
+ def run
174
+ # Look up generator instance and invoke command on it.
175
+ manifest.replay(self)
176
+ after_generate
177
+ rescue => e
178
+ puts e
179
+ puts " #{e.backtrace.join("\n ")}\n" if options[:backtrace]
180
+ raise SystemExit unless options[:no_exit]
181
+ end
182
+
183
+ def camelize(term, uppercase_first_letter = true)
184
+ string = term.to_s
185
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
186
+ string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
187
+ end
188
+
189
+ protected
190
+ # Convenience method for generator subclasses to record a manifest.
191
+ def record
192
+ Apimaster::Generators::Manifest.new(self) { |m| yield m }
193
+ end
194
+
195
+ # Override with your own usage banner.
196
+ def banner
197
+ "Usage: #{$0} #{spec.name} [options]"
198
+ end
199
+
200
+ # Read USAGE from file in generator base path.
201
+ def usage_message
202
+ File.read(File.join(spec.path, 'USAGE')) rescue ''
203
+ end
204
+ end
205
+
206
+ end