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.
- data/LICENSE +20 -0
- data/README.md +12 -0
- data/apimaster.gemspec +14 -0
- data/bin/apimaster +6 -0
- data/lib/apimaster.rb +21 -0
- data/lib/apimaster/application.rb +28 -0
- data/lib/apimaster/controllers/errors.rb +34 -0
- data/lib/apimaster/error.rb +79 -0
- data/lib/apimaster/generators/application.rb +112 -0
- data/lib/apimaster/generators/base.rb +206 -0
- data/lib/apimaster/generators/command.rb +697 -0
- data/lib/apimaster/generators/manifest.rb +51 -0
- data/lib/apimaster/generators/options.rb +162 -0
- data/lib/apimaster/generators/scripts.rb +64 -0
- data/lib/apimaster/generators/simple_logger.rb +44 -0
- data/lib/apimaster/generators/templates/Gemfile +21 -0
- data/lib/apimaster/generators/templates/LICENSE +20 -0
- data/lib/apimaster/generators/templates/README.md +10 -0
- data/lib/apimaster/generators/templates/Rakefile +19 -0
- data/lib/apimaster/generators/templates/TODO +4 -0
- data/lib/apimaster/generators/templates/app/controllers/index_controller.rb.erb +7 -0
- data/lib/apimaster/generators/templates/config.ru.erb +8 -0
- data/lib/apimaster/generators/templates/config/application.rb.erb +19 -0
- data/lib/apimaster/generators/templates/config/boot.rb.erb +17 -0
- data/lib/apimaster/generators/templates/config/initializer.rb.erb +13 -0
- data/lib/apimaster/generators/templates/config/patches.rb.erb +0 -0
- data/lib/apimaster/generators/templates/config/settings/app.yml.erb +3 -0
- data/lib/apimaster/generators/templates/config/settings/mongoid.yml.erb +66 -0
- data/lib/apimaster/generators/templates/config/settings/oauth.yml.erb +8 -0
- data/lib/apimaster/generators/templates/gitignore +10 -0
- data/lib/apimaster/generators/templates/lib/module.rb.erb +6 -0
- data/lib/apimaster/generators/templates/test/functional_test.rb.erb +2 -0
- data/lib/apimaster/generators/templates/test/test_helper.rb.erb +1 -0
- data/lib/apimaster/generators/templates/test/unit_test.rb.erb +2 -0
- data/lib/apimaster/generators/version.rb +3 -0
- data/lib/apimaster/helpers/headers.rb +30 -0
- data/lib/apimaster/helpers/request.rb +49 -0
- data/lib/apimaster/helpers/session.rb +36 -0
- data/lib/apimaster/mapper.rb +86 -0
- data/lib/apimaster/models/user.rb +33 -0
- data/lib/apimaster/models/user_mock.rb +13 -0
- data/lib/apimaster/setting.rb +68 -0
- metadata +45 -3
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
# Initialize environment
|
3
|
+
ROOT_PATH = File.expand_path(".")
|
4
|
+
ENV["RACK_ENV"] ||= "development"
|
5
|
+
Bundler.require(:default, ENV["RACK_ENV"].to_sym)
|
6
|
+
|
7
|
+
# Initialize settings
|
8
|
+
setting_files = "./config/settings/{app,oauth}.yml"
|
9
|
+
Apimaster::Setting.environment = ENV["RACK_ENV"]
|
10
|
+
Apimaster::Setting.load_file(setting_files)
|
11
|
+
|
12
|
+
# Database setting
|
13
|
+
# Mongoid.load!("./config/settings/mongoid.yml")
|
File without changes
|
@@ -0,0 +1,66 @@
|
|
1
|
+
development:
|
2
|
+
sessions:
|
3
|
+
default:
|
4
|
+
database: mongoid
|
5
|
+
hosts:
|
6
|
+
- localhost:27017
|
7
|
+
|
8
|
+
# Tell Mongoid which environment this configuration is for.
|
9
|
+
production:
|
10
|
+
# This starts the session configuration settings. You may have as
|
11
|
+
# many sessions as you like, but you must have at least 1 named
|
12
|
+
# 'default'.
|
13
|
+
sessions:
|
14
|
+
# Define the default session.
|
15
|
+
default:
|
16
|
+
# A session can have any number of hosts. Usually 1 for a single
|
17
|
+
# server setup, and at least 3 for a replica set. Hosts must be
|
18
|
+
# an array of host:port pairs. This session is single server.
|
19
|
+
hosts:
|
20
|
+
- flame.mongohq.com:27017
|
21
|
+
# Define the default database name.
|
22
|
+
database: mongoid
|
23
|
+
# Since this database points at a session connected to MongoHQ, we must
|
24
|
+
# provide the authentication details.
|
25
|
+
username: user
|
26
|
+
password: password
|
27
|
+
# This defines a secondary session at a replica set.
|
28
|
+
replica_set:
|
29
|
+
# This configuration is a 3 node replica set.
|
30
|
+
hosts:
|
31
|
+
- dedicated1.myapp.com:27017
|
32
|
+
- dedicated2.myapp.com:27017
|
33
|
+
- dedicated3.myapp.com:27017
|
34
|
+
database: mongoid
|
35
|
+
# We can set session specific options, like reads executing
|
36
|
+
# on secondary nodes, and defaulting the session to safe mode.
|
37
|
+
options:
|
38
|
+
consistency: :eventual
|
39
|
+
safe: true
|
40
|
+
# This defines a tertiary session at a Mongos fronted shard.
|
41
|
+
shard:
|
42
|
+
# This configuration is a Mongos shard server.
|
43
|
+
hosts:
|
44
|
+
- mongos.myapp.com:27017
|
45
|
+
database: mongoid
|
46
|
+
# This configuration shows an authenticated replica set via a uri.
|
47
|
+
another:
|
48
|
+
uri: mongodb://user:pass@59.1.22.1:27017,59.1.22.2:27017/mongoid
|
49
|
+
# Here we put the Mongoid specific configuration options. These are explained
|
50
|
+
# in more detail next.
|
51
|
+
options:
|
52
|
+
allow_dynamic_fields: false
|
53
|
+
identity_map_enabled: true
|
54
|
+
include_root_in_json: true
|
55
|
+
include_type_for_serialization: true
|
56
|
+
# Note this can also be true if you want to preload everything, but this is
|
57
|
+
# almost never necessary. Most of the time set this to false.
|
58
|
+
preload_models:
|
59
|
+
- Canvas
|
60
|
+
- Browser
|
61
|
+
- Firefox
|
62
|
+
scope_overwrite_exception: true
|
63
|
+
raise_not_found_error: false
|
64
|
+
skip_version_check: false
|
65
|
+
use_activesupport_time_zone: false
|
66
|
+
use_utc: true
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011-2012 AdMaster, Inc.
|
4
|
+
|
5
|
+
module Apimaster::Helpers
|
6
|
+
module Headers
|
7
|
+
|
8
|
+
def header_pagination(pagination)
|
9
|
+
path = base_url + request.path_info
|
10
|
+
next_link = path + "?" + query_string_modifier(page: pagination.next_page)
|
11
|
+
last_link = path + "?" + query_string_modifier(page: pagination.page_count)
|
12
|
+
pagination_link = "<#{next_link}>; rel=\"next\", <#{last_link}>; rel=\"last\""
|
13
|
+
headers "Link" => pagination_link
|
14
|
+
end
|
15
|
+
|
16
|
+
def header_location(path)
|
17
|
+
headers "Location" => base_url + path
|
18
|
+
end
|
19
|
+
|
20
|
+
def header_link(path, rel)
|
21
|
+
headers "Link" => "<#{base_url+path}>; rel=\"#{rel}\""
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def base_url
|
27
|
+
Apimaster::Setting.get('app.base_url')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011-2012 AdMaster, Inc.
|
4
|
+
#
|
5
|
+
# @author: sunxiqiu@admaster.com.cn
|
6
|
+
|
7
|
+
module Apimaster::Helpers
|
8
|
+
module Request
|
9
|
+
|
10
|
+
# Convert a hash to a querystring for form population
|
11
|
+
def hash_to_query_string(hash)
|
12
|
+
hash.collect {|k,v| "#{k}=#{v}"}.join("&")
|
13
|
+
end
|
14
|
+
|
15
|
+
def query_string_modifier(hash)
|
16
|
+
hash_to_query_string(CGI::parse(request.query_string).merge(hash))
|
17
|
+
end
|
18
|
+
|
19
|
+
def posts
|
20
|
+
@posts ||= to_symbol_key_hash(request_json)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def to_symbol_key_hash(hash)
|
26
|
+
return hash unless hash.is_a? Hash
|
27
|
+
result = {}
|
28
|
+
hash.map do |key, val|
|
29
|
+
val = to_symbol_key_hash(val) if val.is_a? Hash
|
30
|
+
result[key.to_sym] = val
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
def request_json
|
36
|
+
begin
|
37
|
+
@request_json ||= parse_json
|
38
|
+
rescue JSON::ParserError => e
|
39
|
+
raise Apimaster::RequestError, "Problems parsing JSON"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_json
|
44
|
+
body_data = request.body.read
|
45
|
+
body_data.empty? ? {} : JSON.parse(body_data)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011-2012 AdMaster, Inc.
|
4
|
+
#
|
5
|
+
# @author: sunxiqiu@admaster.com.cn
|
6
|
+
|
7
|
+
module Apimaster::Helpers
|
8
|
+
module Session
|
9
|
+
|
10
|
+
# Check logged in user is the owner
|
11
|
+
def is_owner? owner_id
|
12
|
+
!!current_user && current_user.id.to_i == owner_id.to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def authorize
|
16
|
+
raise Apimaster::UnauthorizedError.new :user unless current_user
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return current_user record if logged in
|
20
|
+
def current_user
|
21
|
+
@current_user ||= auth_user
|
22
|
+
end
|
23
|
+
|
24
|
+
def auth_user
|
25
|
+
@access_token ||= params[:access_token] or header_token
|
26
|
+
(test? ? Apimaster::Models::UserMock : Apimaster::Models::User).auth @access_token
|
27
|
+
end
|
28
|
+
|
29
|
+
def header_token
|
30
|
+
keys = %w{HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION}
|
31
|
+
authorization ||= keys.inject(nil) { |auth, key| auth || request.env[key] }
|
32
|
+
authorization.split[1] if authorization and authorization[/^token/i]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011-2012 AdMaster, Inc.
|
4
|
+
|
5
|
+
module Apimaster
|
6
|
+
class Mapper
|
7
|
+
# include Mongoid::Document
|
8
|
+
|
9
|
+
def post hash
|
10
|
+
from_hash hash
|
11
|
+
save
|
12
|
+
end
|
13
|
+
|
14
|
+
def put hash
|
15
|
+
from_hash hash
|
16
|
+
save
|
17
|
+
end
|
18
|
+
|
19
|
+
def patch hash
|
20
|
+
from_hash hash
|
21
|
+
save
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_hash accessor = :all
|
25
|
+
record = {}
|
26
|
+
fields = self.class.find_attrs_in_options(:accessor, accessor)
|
27
|
+
fields.each do |field|
|
28
|
+
if self.respond_to?(field)
|
29
|
+
record[field] = self.send(field)
|
30
|
+
else
|
31
|
+
raise "Dataset #{self.class} has no method with the name of #{field}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
record
|
35
|
+
end
|
36
|
+
|
37
|
+
def from_hash(hash, method = :all)
|
38
|
+
data = {}
|
39
|
+
attrs = [:required, :optional]
|
40
|
+
|
41
|
+
attrs.each do |type|
|
42
|
+
fields = self.class.get_attrs(type, method)
|
43
|
+
fields.each do |field|
|
44
|
+
if hash.has_key?(field)
|
45
|
+
data[field] = hash[field]
|
46
|
+
elsif hash.has_key?(field.to_s)
|
47
|
+
data[field] = hash[field.to_s]
|
48
|
+
else
|
49
|
+
raise Apimaster::MissingFieldError.new(self.class.get_class_name, field) if type == :required
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
data.each do |key, val|
|
55
|
+
respond_setter key, val
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def respond_setter key, val
|
60
|
+
name = (key.to_s + '=').to_sym
|
61
|
+
raise "#{self.class} lost of #{name}" unless self.respond_to?(name)
|
62
|
+
self.send name, val
|
63
|
+
end
|
64
|
+
|
65
|
+
class << self
|
66
|
+
|
67
|
+
OPTION_TYPES = [:accessor, :required, :optional]
|
68
|
+
|
69
|
+
@attr_options ||= {}
|
70
|
+
|
71
|
+
# attr_options :url, accessor: [:get, :list]
|
72
|
+
def attr_options name, options = {}
|
73
|
+
@attr_options[name] = options
|
74
|
+
end
|
75
|
+
|
76
|
+
# [:url, :name]
|
77
|
+
def find_attrs_in_options type, option = :all
|
78
|
+
raise "Unknown attribute options type: #{type}" unless OPTION_TYPES.include? type
|
79
|
+
@attr_options.select do |name, options|
|
80
|
+
type_options = options.is_a?(Hash) and options.key?(type) ? options[type] : nil
|
81
|
+
type_options.is_a?(Array) and (type_options.include?(option) or type_options.include(:all))
|
82
|
+
end.keys
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011-2012 AdMaster, Inc.
|
4
|
+
|
5
|
+
require 'net/http'
|
6
|
+
|
7
|
+
module Apimaster::Models
|
8
|
+
class User
|
9
|
+
|
10
|
+
attr_accessor :id
|
11
|
+
attr_accessor :email
|
12
|
+
attr_accessor :username
|
13
|
+
|
14
|
+
def initialize hash
|
15
|
+
hash.each do |key, val|
|
16
|
+
method_name = (key.to_s+'=').to_sym
|
17
|
+
self.send(method_name, val) if respond_to?(method_name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.auth access_token
|
22
|
+
oauth_domain = Apimaster::Setting.get('oauth.oauth_domain')
|
23
|
+
json = Net::HTTP.get(oauth_domain, "/user?access_token=#{access_token}", 80)
|
24
|
+
user_hash = JSON.parse(json)
|
25
|
+
|
26
|
+
return nil unless user_hash.is_a?(Hash)
|
27
|
+
raise Apimaster::OauthError.new(user_hash["message"]) if user_hash.key?("message")
|
28
|
+
|
29
|
+
self.new user_hash
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011-2012 AdMaster, Inc.
|
4
|
+
|
5
|
+
module Apimaster
|
6
|
+
|
7
|
+
module Setting
|
8
|
+
class << self
|
9
|
+
@@hashes = {}
|
10
|
+
|
11
|
+
# environments: development, test and production.
|
12
|
+
ENVIRONMENTS = %w[test production development]
|
13
|
+
|
14
|
+
attr_accessor :environment
|
15
|
+
|
16
|
+
def set(key, value)
|
17
|
+
@@hashes[key] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def get(key, default = nil)
|
21
|
+
current = @@hashes
|
22
|
+
key.split('.').each do |k|
|
23
|
+
if current and current.is_a?(Hash)
|
24
|
+
current = current.has_key?(k) ? current[k] : default
|
25
|
+
end
|
26
|
+
end
|
27
|
+
current
|
28
|
+
end
|
29
|
+
|
30
|
+
# Loads the configuration from the YAML files whose +paths+ are passed as
|
31
|
+
# arguments, filtering the settings for the current environment. Note that
|
32
|
+
# these +paths+ can actually be globs.
|
33
|
+
def load_file(*paths)
|
34
|
+
paths.each do |pattern|
|
35
|
+
Dir.glob(pattern) do |file|
|
36
|
+
yaml = config_for_env(YAML.load_file(file)) || {}
|
37
|
+
yaml.each_pair do |key, value|
|
38
|
+
for_env = config_for_env(value)
|
39
|
+
set key, for_env unless value and for_env.nil? and respond_to? key
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Given a +hash+ with some application configuration it returns the
|
48
|
+
# settings applicable to the current environment. Note that this can only
|
49
|
+
# be done when all the keys of +hash+ are environment names included in the
|
50
|
+
# +environments+ setting (which is an Array of Strings). Also, the
|
51
|
+
# returned config is a indifferently accessible Hash, which means that you
|
52
|
+
# can get its values using Strings or Symbols as keys.
|
53
|
+
def config_for_env(hash)
|
54
|
+
if hash.respond_to? :keys and hash.keys.all? { |k| ENVIRONMENTS.include? k.to_s }
|
55
|
+
hash = hash[environment.to_s] || hash[environment.to_sym]
|
56
|
+
end
|
57
|
+
|
58
|
+
if hash.respond_to? :to_hash
|
59
|
+
indifferent_hash = Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
|
60
|
+
indifferent_hash.merge hash.to_hash
|
61
|
+
else
|
62
|
+
hash
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|