apimaster 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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