apipie-rails 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/.autotest +3 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +5 -0
  6. data/APACHE-LICENSE-2.0 +202 -0
  7. data/Gemfile +3 -0
  8. data/Gemfile.lock +115 -0
  9. data/MIT-LICENSE +20 -0
  10. data/NOTICE +4 -0
  11. data/README.rdoc +365 -0
  12. data/Rakefile +13 -0
  13. data/apipie-rails.gemspec +27 -0
  14. data/app/controllers/apipie/apipies_controller.rb +60 -0
  15. data/app/public/apipie/javascripts/apipie.js +6 -0
  16. data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +138 -0
  17. data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
  18. data/app/public/apipie/javascripts/bundled/jquery-1.7.2.js +9404 -0
  19. data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
  20. data/app/public/apipie/stylesheets/application.css +7 -0
  21. data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  22. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
  23. data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
  24. data/app/views/apipie/apipies/_params.html.erb +22 -0
  25. data/app/views/apipie/apipies/_params_plain.html.erb +16 -0
  26. data/app/views/apipie/apipies/index.html.erb +36 -0
  27. data/app/views/apipie/apipies/method.html.erb +63 -0
  28. data/app/views/apipie/apipies/plain.html.erb +70 -0
  29. data/app/views/apipie/apipies/resource.html.erb +82 -0
  30. data/app/views/apipie/apipies/static.html.erb +101 -0
  31. data/app/views/layouts/apipie/apipie.html.erb +37 -0
  32. data/lib/apipie-rails.rb +12 -0
  33. data/lib/apipie/apipie_module.rb +105 -0
  34. data/lib/apipie/application.rb +225 -0
  35. data/lib/apipie/client/generator.rb +105 -0
  36. data/lib/apipie/client/template/Gemfile.tt +5 -0
  37. data/lib/apipie/client/template/README.tt +3 -0
  38. data/lib/apipie/client/template/base.rb.tt +33 -0
  39. data/lib/apipie/client/template/bin.rb.tt +110 -0
  40. data/lib/apipie/client/template/cli.rb.tt +25 -0
  41. data/lib/apipie/client/template/cli_command.rb.tt +129 -0
  42. data/lib/apipie/client/template/client.rb.tt +10 -0
  43. data/lib/apipie/client/template/resource.rb.tt +17 -0
  44. data/lib/apipie/dsl_definition.rb +139 -0
  45. data/lib/apipie/error_description.rb +21 -0
  46. data/lib/apipie/extractor.rb +143 -0
  47. data/lib/apipie/extractor/collector.rb +113 -0
  48. data/lib/apipie/extractor/recorder.rb +122 -0
  49. data/lib/apipie/extractor/writer.rb +356 -0
  50. data/lib/apipie/helpers.rb +24 -0
  51. data/lib/apipie/markup.rb +45 -0
  52. data/lib/apipie/method_description.rb +150 -0
  53. data/lib/apipie/param_description.rb +87 -0
  54. data/lib/apipie/railtie.rb +9 -0
  55. data/lib/apipie/resource_description.rb +83 -0
  56. data/lib/apipie/routing.rb +13 -0
  57. data/lib/apipie/static_dispatcher.rb +60 -0
  58. data/lib/apipie/validator.rb +292 -0
  59. data/lib/apipie/version.rb +3 -0
  60. data/lib/tasks/apipie.rake +156 -0
  61. data/rel-eng/packages/.readme +3 -0
  62. data/rel-eng/tito.props +5 -0
  63. data/rubygem-apipie-rails.spec +72 -0
  64. data/spec/controllers/apipies_controller_spec.rb +132 -0
  65. data/spec/controllers/users_controller_spec.rb +390 -0
  66. data/spec/dummy/Rakefile +7 -0
  67. data/spec/dummy/app/controllers/application_controller.rb +6 -0
  68. data/spec/dummy/app/controllers/twitter_example_controller.rb +302 -0
  69. data/spec/dummy/app/controllers/users_controller.rb +223 -0
  70. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  71. data/spec/dummy/config.ru +4 -0
  72. data/spec/dummy/config/application.rb +45 -0
  73. data/spec/dummy/config/boot.rb +10 -0
  74. data/spec/dummy/config/database.yml +21 -0
  75. data/spec/dummy/config/environment.rb +8 -0
  76. data/spec/dummy/config/environments/development.rb +25 -0
  77. data/spec/dummy/config/environments/production.rb +49 -0
  78. data/spec/dummy/config/environments/test.rb +35 -0
  79. data/spec/dummy/config/initializers/apipie.rb +64 -0
  80. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  81. data/spec/dummy/config/initializers/inflections.rb +10 -0
  82. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  83. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  84. data/spec/dummy/config/initializers/session_store.rb +8 -0
  85. data/spec/dummy/config/locales/en.yml +5 -0
  86. data/spec/dummy/config/routes.rb +21 -0
  87. data/spec/dummy/doc/apipie_examples.yml +28 -0
  88. data/spec/dummy/public/404.html +26 -0
  89. data/spec/dummy/public/422.html +26 -0
  90. data/spec/dummy/public/500.html +26 -0
  91. data/spec/dummy/public/favicon.ico +0 -0
  92. data/spec/dummy/public/javascripts/application.js +2 -0
  93. data/spec/dummy/public/javascripts/controls.js +965 -0
  94. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  95. data/spec/dummy/public/javascripts/effects.js +1123 -0
  96. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  97. data/spec/dummy/public/javascripts/rails.js +202 -0
  98. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  99. data/spec/dummy/script/rails +6 -0
  100. data/spec/spec_helper.rb +32 -0
  101. metadata +312 -0
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ gem 'thor', '>= 0.15.4'
4
+ gem 'json'
5
+ gem 'rest-client', '>= 1.6.1'
@@ -0,0 +1,3 @@
1
+ This is a readme file for this gem.
2
+
3
+ Let the code be with you!
@@ -0,0 +1,33 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+
4
+ module <%= class_base %>Client
5
+ class Base
6
+ attr_reader :client
7
+
8
+ def initialize(config)
9
+ @client = RestClient::Resource.new(config[:base_url], :user => config[:username], :password => config[:password])
10
+ end
11
+
12
+ def call(method, path, params = {})
13
+ ret = client[path].send(method, params)
14
+ data = JSON.parse(ret.body) rescue ret.body
15
+ return data, ret
16
+ end
17
+
18
+ def validate_params!(options, valid_keys)
19
+ return unless options.is_a?(Hash)
20
+ invalid_keys = options.keys - (valid_keys.is_a?(Hash) ? valid_keys.keys : valid_keys)
21
+ raise ArgumentError, "Invalid keys: #{invalid_keys.join(", ")}" unless invalid_keys.empty?
22
+
23
+ if valid_keys.is_a? Hash
24
+ valid_keys.each do |key, keys|
25
+ if options[key]
26
+ validate_params!(options[key], keys)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems" # ruby1.9 doesn't "require" it though
3
+ require "pathname"
4
+ require "thor"
5
+ require 'thor/core_ext/file_binary_read'
6
+
7
+ $: << File.expand_path("../../lib", __FILE__)
8
+ require "<%= name %>_client"
9
+ require "<%= name %>_client/cli_command"
10
+
11
+ module <%= class_base %>Cli
12
+ class Main < Thor
13
+
14
+ def help(meth = nil)
15
+ if meth && !self.respond_to?(meth)
16
+ initialize_thorfiles(meth)
17
+ klass, task = Thor::Util.find_class_and_task_by_namespace(meth)
18
+ self.class.handle_no_task_error(task, false) if klass.nil?
19
+ klass.start(["-h", task].compact, :shell => self.shell)
20
+ else
21
+ say "<%= name.capitalize %> CLI"
22
+ say
23
+ invoke :commands
24
+ end
25
+ end
26
+
27
+ desc "commands [SEARCH]", "List the available commands"
28
+ def commands(search="")
29
+ initialize_thorfiles
30
+ klasses = Thor::Base.subclasses
31
+ display_klasses(false, false, klasses)
32
+ end
33
+
34
+ class << self
35
+ private
36
+ def dispatch(task, given_args, given_options, config)
37
+ parser = Thor::Options.new({:username => Thor::Option.parse(%w[username -u], :string),
38
+ :password => Thor::Option.parse(%w[password -p], :string)})
39
+ opts = parser.parse(given_args)
40
+ <%= class_base %>Client.client_config[:username] = opts["username"]
41
+ <%= class_base %>Client.client_config[:password] = opts["password"]
42
+ #remaining = parser.instance_variable_get("@unknown") # TODO: this is an ugly hack :(
43
+ remaining = parser.remaining
44
+ super(task, remaining, given_options, config)
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def method_missing(meth, *args)
51
+ meth = meth.to_s
52
+ initialize_thorfiles(meth)
53
+ klass, task = Thor::Util.find_class_and_task_by_namespace(meth)
54
+ args.unshift(task) if task
55
+ klass.start(args, :shell => self.shell)
56
+ end
57
+
58
+ # Load the thorfiles. If relevant_to is supplied, looks for specific files
59
+ # in the thor_root instead of loading them all.
60
+ #
61
+ # By default, it also traverses the current path until find Thor files, as
62
+ # described in thorfiles. This look up can be skipped by suppliying
63
+ # skip_lookup true.
64
+ #
65
+ def initialize_thorfiles(relevant_to=nil, skip_lookup=false)
66
+ thorfiles.each do |f|
67
+ Thor::Util.load_thorfile(f, nil, options[:debug])
68
+ end
69
+ end
70
+
71
+ def thorfiles
72
+ Dir[File.expand_path("../../lib/<%= name %>_client/commands/*.thor", __FILE__)]
73
+ end
74
+
75
+ # Display information about the given klasses. If with_module is given,
76
+ # it shows a table with information extracted from the yaml file.
77
+ #
78
+ def display_klasses(with_modules=false, show_internal=false, klasses=Thor::Base.subclasses)
79
+ klasses -= [Thor, Main, ::<%= class_base %>Client::CliCommand] unless show_internal
80
+
81
+ show_modules if with_modules && !thor_yaml.empty?
82
+
83
+ list = Hash.new { |h,k| h[k] = [] }
84
+ groups = []
85
+
86
+ # Get classes which inherit from Thor
87
+ (klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_tasks(false) }
88
+
89
+ # Get classes which inherit from Thor::Base
90
+ groups.map! { |k| k.printable_tasks(false).first }
91
+ list["root"] = groups
92
+
93
+ # Order namespaces with default coming first
94
+ list = list.sort{ |a,b| a[0].sub(/^default/, '') <=> b[0].sub(/^default/, '') }
95
+ list.each { |n, tasks| display_tasks(n, tasks) unless tasks.empty? }
96
+ end
97
+
98
+ def display_tasks(namespace, list) #:nodoc:
99
+ say namespace
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ begin
106
+ <%= class_base %>Cli::Main.start
107
+ rescue RestClient::Exception => e
108
+ $stderr.puts e.message
109
+ exit 1
110
+ end
@@ -0,0 +1,25 @@
1
+ class <%= resource_name.camelize %> < <%= class_base %>Client::CliCommand
2
+
3
+ <% resource[:methods].each do |method| -%>
4
+ desc '<%= method[:name] %>', '<%= api(method)[:short_description] %>'
5
+ <% params_in_path(method).each do |param| -%>
6
+ method_option :<%= param %>, :required => 'true'
7
+ <% end
8
+ method[:params].map {|p| p[:expected_type] == "hash" ? (p[:params] || p) : p}.flatten.each do |param| -%>
9
+ method_option :<%= param[:name] %>, :required => <%= param[:required] ? 'true' : 'false' %>, :desc => '<%= plaintext(param[:description]) %>', :type => :<%= param[:expected_type] %>
10
+ <% end -%>
11
+ def <%= method[:name] %>
12
+ <% if params_in_path(method).any? || transformation_hash(method).any?
13
+ transform_options_params = [params_in_path(method).inspect]
14
+ transform_options_params << transformation_hash(method).inspect if transformation_hash(method).any? -%>
15
+ <%= (params_in_path(method) + ["options"]).join(", ") %>, *_ = transform_options(<%= transform_options_params.join(", ").html_safe %>)
16
+ <% end
17
+
18
+ client_args = params_in_path(method).dup
19
+ client_args << "options" if method[:params].any? -%>
20
+ data, resp = client.<%= method[:name] %><%= "(#{client_args.join(", ")})" if client_args.any? %>
21
+ print_data(data)
22
+ end
23
+
24
+ <% end -%>
25
+ end
@@ -0,0 +1,129 @@
1
+ module <%= class_base %>Client
2
+ class CliCommand < Thor
3
+ no_tasks do
4
+ def client
5
+ resource_class = <%= class_base %>Client::Resources.const_get(self.class.name[/[^:]*$/])
6
+ @client ||= resource_class.new(<%= class_base %>Client.client_config)
7
+ end
8
+
9
+ def transform_options(inline_params, transform_hash = {})
10
+ ret = inline_params.map { |p| options[p] }
11
+
12
+ # we use not mentioned params without change
13
+ transformed_options = (options.keys - transform_hash.values.flatten - inline_params).reduce({}) { |h, k| h.update(k => options[k]) }
14
+
15
+ transform_hash.each do |sub_key, params|
16
+ transformed_options[sub_key] = {}
17
+ params.each { |p| transformed_options[sub_key][p] = options[p] if options.has_key?(p) }
18
+ end
19
+
20
+ ret << transformed_options
21
+ return *ret
22
+ end
23
+
24
+ def print_data(data)
25
+ case data
26
+ when Array
27
+ print_big_table(table_from_array(data))
28
+ when Hash
29
+ print_table(table_from_hash(data))
30
+ else
31
+ print_unknown(data)
32
+ end
33
+ end
34
+
35
+ # unifies the data for further processing. e.g.
36
+ #
37
+ # { "user" => {"username" => "test", "password" => "changeme" }
38
+ #
39
+ # becomes:
40
+ #
41
+ # { "username" => "test", "password" => "changeme" }
42
+ def normalize_item_data(item)
43
+ if item.size == 1 && item.values.first.is_a?(Hash)
44
+ item.values.first
45
+ else
46
+ item
47
+ end
48
+ end
49
+
50
+ def table_from_array(data)
51
+ return [] if data.empty?
52
+ table = []
53
+ items = data.map { |item| normalize_item_data(item) }
54
+ columns = items.first.keys
55
+ table << columns
56
+ items.each do |item|
57
+ row = columns.map { |c| item[c] }
58
+ table << row.map(&:to_s)
59
+ end
60
+ return table
61
+ end
62
+
63
+ def table_from_hash(data)
64
+ return [] if data.empty?
65
+ table = []
66
+ normalize_item_data(data).each do |k, v|
67
+ table << ["#{k}:",v].map(&:to_s)
68
+ end
69
+ table
70
+ end
71
+
72
+ def print_unknown(data)
73
+ say data
74
+ end
75
+
76
+ def print_big_table(table, options={})
77
+ return if table.empty?
78
+
79
+ formats, ident, colwidth = [], options[:ident].to_i, options[:colwidth]
80
+ options[:truncate] = terminal_width if options[:truncate] == true
81
+
82
+ formats << "%-#{colwidth + 2}s" if colwidth
83
+ start = colwidth ? 1 : 0
84
+
85
+ start.upto(table.first.length - 2) do |i|
86
+ maxima ||= table.max{|a,b| a[i].size <=> b[i].size }[i].size
87
+ formats << "%-#{maxima + 2}s"
88
+ end
89
+
90
+ formats << "%s"
91
+ formats[0] = formats[0].insert(0, " " * ident)
92
+
93
+ header_printed = false
94
+ table.each do |row|
95
+ sentence = ""
96
+
97
+ row.each_with_index do |column, i|
98
+ sentence << formats[i] % column.to_s
99
+ end
100
+
101
+ sentence = truncate(sentence, options[:truncate]) if options[:truncate]
102
+ $stdout.puts sentence
103
+ say(set_color("-" * sentence.size, :green)) unless header_printed
104
+ header_printed = true
105
+ end
106
+ end
107
+
108
+ end
109
+
110
+ class << self
111
+ def help(shell, subcommand = true)
112
+ list = self.printable_tasks(true, subcommand)
113
+ Thor::Util.thor_classes_in(self).each do |klass|
114
+ list += printable_tasks(false)
115
+ end
116
+ list.sort!{ |a,b| a[0] <=> b[0] }
117
+
118
+ shell.say
119
+ shell.print_table(list, :indent => 2, :truncate => true)
120
+ shell.say
121
+ Thor.send(:class_options_help, shell)
122
+ end
123
+
124
+ def banner(task, namespace = nil, subcommand = false)
125
+ task.name
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,10 @@
1
+ require '<%= name %>_client/base'
2
+
3
+ resource_files = Dir[File.expand_path('../<%= name %>_client/resources/*.rb', __FILE__)]
4
+ resource_files.each { |f| require f }
5
+
6
+ module <%= class_base %>Client
7
+ def self.client_config
8
+ @client_config ||= {:base_url => "http://localhost:3000"}
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ module <%= class_base %>Client
2
+ module Resources
3
+ class <%= resource_name.camelize %> < <%= class_base %>Client::Base
4
+ <% resource[:methods].each do |method| -%>
5
+
6
+ def <%= method[:name] %><%= "(#{ client_args(method).join(", ") })" if client_args(method).any? %>
7
+ <% if method[:params].any? -%>
8
+ validate_params!(params, <%= validation_hash(method).inspect.html_safe %>)
9
+ <% end -%>
10
+ call(:<%= api(method)[:http_method].downcase %>, "<%= substituted_url(method) %>"<%=
11
+ (api(method)[:http_method].downcase == 'get' ? ", :params => params" : ", params") if method[:params].any? %>)
12
+ end
13
+ <% end -%>
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,139 @@
1
+ # Apipie DSL functions.
2
+
3
+ module Apipie
4
+
5
+ # DSL is a module that provides #api, #error, #param, #error.
6
+ module DSL
7
+
8
+ private
9
+
10
+ # Describe whole resource
11
+ #
12
+ # Example:
13
+ # api :desc => "Show user profile", :path => "/users/", :version => '1.0 - 3.4.2012'
14
+ # param :id, Fixnum, :desc => "User ID", :required => true
15
+ # desc <<-EOS
16
+ # Long description...
17
+ # EOS
18
+ def resource_description(options = {}, &block) #:doc:
19
+ return unless Apipie.active_dsl?
20
+ Apipie.remove_resource_description(self)
21
+ Apipie.define_resource_description(self, &block) if block_given?
22
+ end
23
+
24
+ # Declare an api.
25
+ #
26
+ # Example:
27
+ # api :GET, "/resource_route", "short description",
28
+ #
29
+ def api(method, path, desc = nil) #:doc:
30
+ return unless Apipie.active_dsl?
31
+ Apipie.add_method_description_args(method, path, desc)
32
+ end
33
+
34
+ # Describe the next method.
35
+ #
36
+ # Example:
37
+ # desc "print hello world"
38
+ # def hello_world
39
+ # puts "hello world"
40
+ # end
41
+ #
42
+ def desc(description) #:doc:
43
+ return unless Apipie.active_dsl?
44
+ if Apipie.last_description
45
+ raise "Double method description."
46
+ end
47
+ Apipie.last_description = description
48
+ end
49
+ alias :description :desc
50
+
51
+ # Reference other similar method
52
+ #
53
+ # api :PUT, '/articles/:id'
54
+ # see "articles#create"
55
+ # def update; end
56
+ def see(method_key)
57
+ return unless Apipie.active_dsl?
58
+ raise "'See' method called twice." if Apipie.last_see
59
+ Apipie.last_see = method_key
60
+ end
61
+
62
+ # Show some example of what does the described
63
+ # method return.
64
+ def example(example) #:doc:
65
+ return unless Apipie.active_dsl?
66
+ Apipie.add_example(example)
67
+ end
68
+
69
+ # Describe possible errors
70
+ #
71
+ # Example:
72
+ # error :desc => "speaker is sleeping", :code => 500
73
+ # error 500, "speaker is sleeping"
74
+ # def hello_world
75
+ # return 500 if self.speaker.sleeping?
76
+ # puts "hello world"
77
+ # end
78
+ #
79
+ def error(*args) #:doc:
80
+ return unless Apipie.active_dsl?
81
+ Apipie.last_errors << Apipie::ErrorDescription.new(args)
82
+ end
83
+
84
+ # Describe method's parameter
85
+ #
86
+ # Example:
87
+ # param :greeting, String, :desc => "arbitrary text", :required => true
88
+ # def hello_world(greeting)
89
+ # puts greeting
90
+ # end
91
+ #
92
+ def param(param_name, *args, &block) #:doc:
93
+ return unless Apipie.active_dsl?
94
+ Apipie.last_params << Apipie::ParamDescription.new(param_name, *args, &block)
95
+ end
96
+
97
+ # create method api and redefine newly added method
98
+ def method_added(method_name) #:doc:
99
+ super
100
+
101
+ return unless Apipie.active_dsl?
102
+ return unless Apipie.apipie_provided?
103
+
104
+ # remove method description if exists and create new one
105
+ Apipie.remove_method_description(self, method_name)
106
+ description = Apipie.define_method_description(self, method_name)
107
+
108
+ # redefine method only if validation is turned on
109
+ if Apipie.configuration.validate == true
110
+
111
+ old_method = instance_method(method_name)
112
+
113
+ define_method(method_name) do |*args|
114
+
115
+ if Apipie.configuration.validate == true
116
+ description.params.each do |_, param|
117
+
118
+ # check if required parameters are present
119
+ if param.required && !params.has_key?(param.name)
120
+ raise ArgumentError.new("Expecting #{param.name} parameter.")
121
+ end
122
+
123
+ # params validations
124
+ if params.has_key?(param.name)
125
+ param.validate(params[:"#{param.name}"])
126
+ end
127
+
128
+ end
129
+ end
130
+
131
+ # run the original method code
132
+ old_method.bind(self).call(*args)
133
+ end
134
+
135
+ end
136
+
137
+ end # def method_added
138
+ end # module DSL
139
+ end # module Apipie