rlmattax-restfulx 1.2.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. data/README.rdoc +50 -0
  2. data/Rakefile +79 -0
  3. data/VERSION.yml +4 -0
  4. data/app_generators/rx_app/USAGE +22 -0
  5. data/app_generators/rx_app/rx_app_generator.rb +110 -0
  6. data/app_generators/rx_app/templates/actionscript.properties +16 -0
  7. data/app_generators/rx_app/templates/actionscriptair.properties +16 -0
  8. data/app_generators/rx_app/templates/app.yaml.erb +12 -0
  9. data/app_generators/rx_app/templates/default_tasks.rake +38 -0
  10. data/app_generators/rx_app/templates/empty.txt +0 -0
  11. data/app_generators/rx_app/templates/expressInstall.swf +0 -0
  12. data/app_generators/rx_app/templates/flex.properties +2 -0
  13. data/app_generators/rx_app/templates/generate.rb +17 -0
  14. data/app_generators/rx_app/templates/html-template/AC_OETags.js +276 -0
  15. data/app_generators/rx_app/templates/html-template/history/history.css +6 -0
  16. data/app_generators/rx_app/templates/html-template/history/history.js +645 -0
  17. data/app_generators/rx_app/templates/html-template/history/historyFrame.html +29 -0
  18. data/app_generators/rx_app/templates/html-template/index.template.html +121 -0
  19. data/app_generators/rx_app/templates/html-template/playerProductInstall.swf +0 -0
  20. data/app_generators/rx_app/templates/index.html.erb +18 -0
  21. data/app_generators/rx_app/templates/index.yaml +11 -0
  22. data/app_generators/rx_app/templates/mainair-app.xml +134 -0
  23. data/app_generators/rx_app/templates/mainapp-config.xml +22 -0
  24. data/app_generators/rx_app/templates/mainapp.mxml +152 -0
  25. data/app_generators/rx_app/templates/project-textmate.erb +72 -0
  26. data/app_generators/rx_app/templates/project.properties +18 -0
  27. data/app_generators/rx_app/templates/projectair.properties +24 -0
  28. data/app_generators/rx_app/templates/restfulx.yml +46 -0
  29. data/app_generators/rx_app/templates/swfobject.js +5 -0
  30. data/bin/rx-gen +31 -0
  31. data/lib/restfulx.rb +90 -0
  32. data/lib/restfulx/active_foo.rb +181 -0
  33. data/lib/restfulx/active_record_tasks.rb +90 -0
  34. data/lib/restfulx/active_record_uuid_helper.rb +17 -0
  35. data/lib/restfulx/configuration.rb +75 -0
  36. data/lib/restfulx/datamapper_foo.rb +81 -0
  37. data/lib/restfulx/rails/recipes.rb +60 -0
  38. data/lib/restfulx/rails/schema_to_yaml.rb +111 -0
  39. data/lib/restfulx/rails/schema_to_yaml/extensions/enumerable.rb +8 -0
  40. data/lib/restfulx/rails/schema_to_yaml/settings/config.rb +17 -0
  41. data/lib/restfulx/rails/schema_to_yaml/settings/core.rb +73 -0
  42. data/lib/restfulx/rails/swf_helper.rb +59 -0
  43. data/lib/restfulx/tasks.rb +110 -0
  44. data/rails_generators/rx_config/USAGE +19 -0
  45. data/rails_generators/rx_config/rx_config_generator.rb +151 -0
  46. data/rails_generators/rx_config/templates/actionscript.properties +16 -0
  47. data/rails_generators/rx_config/templates/actionscriptair.properties +16 -0
  48. data/rails_generators/rx_config/templates/expressInstall.swf +0 -0
  49. data/rails_generators/rx_config/templates/flex.properties +2 -0
  50. data/rails_generators/rx_config/templates/flex_controller.erb +4 -0
  51. data/rails_generators/rx_config/templates/html-template/AC_OETags.js +276 -0
  52. data/rails_generators/rx_config/templates/html-template/history/history.css +6 -0
  53. data/rails_generators/rx_config/templates/html-template/history/history.js +645 -0
  54. data/rails_generators/rx_config/templates/html-template/history/historyFrame.html +29 -0
  55. data/rails_generators/rx_config/templates/html-template/index.template.html +121 -0
  56. data/rails_generators/rx_config/templates/html-template/playerProductInstall.swf +0 -0
  57. data/rails_generators/rx_config/templates/index.erb +16 -0
  58. data/rails_generators/rx_config/templates/mainair-app.xml +134 -0
  59. data/rails_generators/rx_config/templates/mainapp-config.xml +22 -0
  60. data/rails_generators/rx_config/templates/mainapp.mxml +129 -0
  61. data/rails_generators/rx_config/templates/project-textmate.erb +72 -0
  62. data/rails_generators/rx_config/templates/project.properties +18 -0
  63. data/rails_generators/rx_config/templates/projectair.properties +24 -0
  64. data/rails_generators/rx_config/templates/restfulx.erb +75 -0
  65. data/rails_generators/rx_config/templates/restfulx.yml +65 -0
  66. data/rails_generators/rx_config/templates/restfulx_tasks.rake +9 -0
  67. data/rails_generators/rx_config/templates/routes.erb +47 -0
  68. data/rails_generators/rx_config/templates/session_store_flash.erb +1 -0
  69. data/rails_generators/rx_config/templates/swfobject.js +5 -0
  70. data/rails_generators/rx_controller/USAGE +10 -0
  71. data/rails_generators/rx_controller/rx_controller_generator.rb +31 -0
  72. data/rails_generators/rx_controller/templates/controller.as.erb +38 -0
  73. data/rails_generators/rx_main_app/USAGE +8 -0
  74. data/rails_generators/rx_main_app/rx_main_app_generator.rb +60 -0
  75. data/rails_generators/rx_main_app/templates/mainapp.mxml +129 -0
  76. data/rails_generators/rx_scaffold/USAGE +35 -0
  77. data/rails_generators/rx_scaffold/rx_scaffold_generator.rb +242 -0
  78. data/rails_generators/rx_scaffold/templates/controllers/default.rb.erb +125 -0
  79. data/rails_generators/rx_scaffold/templates/controllers/resource_controller.rb.erb +23 -0
  80. data/rails_generators/rx_scaffold/templates/fixtures.yml.erb +39 -0
  81. data/rails_generators/rx_scaffold/templates/functional_test.rb +45 -0
  82. data/rails_generators/rx_scaffold/templates/helper_test.rb +4 -0
  83. data/rails_generators/rx_scaffold/templates/layouts/default.erb +167 -0
  84. data/rails_generators/rx_scaffold/templates/migration.rb.erb +46 -0
  85. data/rails_generators/rx_scaffold/templates/model.as.erb +73 -0
  86. data/rails_generators/rx_scaffold/templates/model.rb.erb +46 -0
  87. data/rails_generators/rx_yaml_scaffold/USAGE +51 -0
  88. data/rails_generators/rx_yaml_scaffold/rx_yaml_scaffold_generator.rb +68 -0
  89. data/rxgen_generators/rx_config/USAGE +5 -0
  90. data/rxgen_generators/rx_config/rx_config_generator.rb +21 -0
  91. data/rxgen_generators/rx_controller/USAGE +10 -0
  92. data/rxgen_generators/rx_controller/rx_controller_generator.rb +41 -0
  93. data/rxgen_generators/rx_controller/templates/assist.py +65 -0
  94. data/rxgen_generators/rx_controller/templates/controller.as.erb +38 -0
  95. data/rxgen_generators/rx_controller/templates/iso8601.py +92 -0
  96. data/rxgen_generators/rx_controller/templates/restful.py +136 -0
  97. data/rxgen_generators/rx_main_app/USAGE +8 -0
  98. data/rxgen_generators/rx_main_app/rx_main_app_generator.rb +65 -0
  99. data/rxgen_generators/rx_main_app/templates/main.py.erb +29 -0
  100. data/rxgen_generators/rx_main_app/templates/mainapp.mxml +152 -0
  101. data/rxgen_generators/rx_scaffold/USAGE +29 -0
  102. data/rxgen_generators/rx_scaffold/rx_scaffold_generator.rb +194 -0
  103. data/rxgen_generators/rx_scaffold/templates/controller.py.erb +27 -0
  104. data/rxgen_generators/rx_scaffold/templates/layouts/default.erb +167 -0
  105. data/rxgen_generators/rx_scaffold/templates/model.as.erb +73 -0
  106. data/rxgen_generators/rx_scaffold/templates/model.py.erb +14 -0
  107. data/rxgen_generators/rx_yaml_scaffold/USAGE +45 -0
  108. data/rxgen_generators/rx_yaml_scaffold/rx_yaml_scaffold_generator.rb +47 -0
  109. data/spec/restfulx_spec.rb +4 -0
  110. data/spec/spec_helper.rb +13 -0
  111. data/tasks/restfulx.rake +2 -0
  112. data/test/rails/controllers/application_controller.rb +15 -0
  113. data/test/rails/controllers/locations_controller.rb +93 -0
  114. data/test/rails/controllers/notes_controller.rb +96 -0
  115. data/test/rails/controllers/projects_controller.rb +93 -0
  116. data/test/rails/controllers/tasks_controller.rb +93 -0
  117. data/test/rails/controllers/users_controller.rb +93 -0
  118. data/test/rails/database.yml +4 -0
  119. data/test/rails/fixtures/locations.yml +8 -0
  120. data/test/rails/fixtures/notes.yml +17 -0
  121. data/test/rails/fixtures/projects.yml +25 -0
  122. data/test/rails/fixtures/simple_properties.yml +19 -0
  123. data/test/rails/fixtures/tasks.yml +46 -0
  124. data/test/rails/fixtures/users.yml +13 -0
  125. data/test/rails/helpers/functional_test_helper.rb +21 -0
  126. data/test/rails/helpers/test_helper.rb +54 -0
  127. data/test/rails/helpers/unit_test_helper.rb +29 -0
  128. data/test/rails/model.yml +35 -0
  129. data/test/rails/models/location.rb +4 -0
  130. data/test/rails/models/note.rb +3 -0
  131. data/test/rails/models/project.rb +4 -0
  132. data/test/rails/models/simple_property.rb +2 -0
  133. data/test/rails/models/task.rb +18 -0
  134. data/test/rails/models/user.rb +20 -0
  135. data/test/rails/schema.rb +77 -0
  136. data/test/rails/test.swf +1 -0
  137. data/test/rails/test_active_foo.rb +36 -0
  138. data/test/rails/test_rails_integration_functional.rb +22 -0
  139. data/test/rails/test_to_fxml.rb +35 -0
  140. data/test/rails/test_to_json.rb +23 -0
  141. data/test/rails/views/notes/empty_params_action.html.erb +1 -0
  142. data/test/rails/views/notes/index.html.erb +1 -0
  143. metadata +234 -0
@@ -0,0 +1,51 @@
1
+ Description:
2
+ Scaffolds an entire application based on db/model.yml file. This generator
3
+ transforms entries in db/model.yml into command line calls to
4
+ "rx_scaffold".
5
+
6
+ rx_scaffold delegates the underlying rails code generation to "scaffold"
7
+ and extends it in a number of ways:
8
+ 1. Generates all required Flex code.
9
+ 2. You can pass special belongs_to, has_one and has_many attributes
10
+ to generate *all* appropriate relationships. No more manual code
11
+ editing.
12
+
13
+ Examples:
14
+ `./script/generate rx_yaml_scaffold`
15
+
16
+ Sample Model File:
17
+ project:
18
+ - name: string
19
+ - notes: text
20
+ - start_date: date
21
+ - end_date: date
22
+ - completed: boolean
23
+ - belongs_to: [user]
24
+ - has_many: [tasks]
25
+
26
+ location:
27
+ - name: string
28
+ - notes: text
29
+ - belongs_to: [user]
30
+ - has_many: [tasks]
31
+
32
+ task:
33
+ - name: string
34
+ - notes: text
35
+ - start_time: datetime
36
+ - end_time: datetime
37
+ - completed: boolean
38
+ - next_action: boolean
39
+ - belongs_to: [project, location, user]
40
+
41
+ note:
42
+ - content: text
43
+ - belongs_to: [user]
44
+
45
+ user:
46
+ - login: string
47
+ - first_name: string
48
+ - last_name: string
49
+ - email: string
50
+ - has_many: [tasks, projects, locations]
51
+ - has_one: [note]
@@ -0,0 +1,68 @@
1
+ require 'yaml'
2
+
3
+ class RxYamlScaffoldGenerator < Rails::Generator::Base
4
+ def extract_attrs(line, attrs)
5
+ attrs.each do |key,value|
6
+ if value.class == Array
7
+ line << " #{key}:#{value.join(',')}"
8
+ else
9
+ line << " #{key}:#{value}"
10
+ end
11
+ end
12
+ line
13
+ end
14
+
15
+ def manifest
16
+ record do |m|
17
+ models = YAML.load(File.open(File.join(RAILS_ROOT, 'db/model.yml'), 'r'))
18
+ models.each do |model|
19
+ line = ""
20
+ attrs = model[1]
21
+ if attrs.class == Array
22
+ attrs.each do |elm|
23
+ line = extract_attrs(line, elm)
24
+ end
25
+ else
26
+ line = extract_attrs(line, attrs)
27
+ end
28
+ line = model[0].camelcase + " " + line
29
+
30
+ if ARGV.size > 0
31
+ ARGV.each do |arg|
32
+ if model[0].camelcase == arg
33
+ puts 'running: rx_scaffold ' + line
34
+ Rails::Generator::Scripts::Generate.new.run(["rx_scaffold"] + line.split,
35
+ :flex_only => options[:flex_only],
36
+ :flex_view_only => options[:flex_view_only],
37
+ :rails_only => options[:rails_only])
38
+ puts 'done ...'
39
+ sleep 1
40
+ end
41
+ end
42
+ else
43
+ puts 'running: rx_scaffold ' + line
44
+ Rails::Generator::Scripts::Generate.new.run(["rx_scaffold"] + line.split,
45
+ :flex_only => options[:flex_only],
46
+ :flex_view_only => options[:flex_view_only],
47
+ :rails_only => options[:rails_only])
48
+ puts 'done ...'
49
+ sleep 1
50
+ end
51
+ end
52
+
53
+ Rails::Generator::Scripts::Generate.new.run(["rx_main_app"])
54
+ end
55
+ end
56
+
57
+ protected
58
+ def add_options!(opt)
59
+ opt.separator ''
60
+ opt.separator 'Options:'
61
+ opt.on("-f", "--flex-only", "Only generate the Flex/AIR files",
62
+ "Default: false") { |v| options[:flex_only] = v }
63
+ opt.on("-r", "--rails-only", "Only generate the Rails files",
64
+ "Default: false") { |v| options[:rails_only] = v }
65
+ opt.on("-fv", "--flex-view-only", "Only generate the Flex component files",
66
+ "Default: false") { |v| options[:flex_view_only] = v }
67
+ end
68
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Fetches the latest published RestfulX Framework SWC file.
3
+
4
+ Examples:
5
+ `./script/generate rx_config`
@@ -0,0 +1,21 @@
1
+ require 'open-uri'
2
+
3
+ class RxConfigGenerator < RubiGen::Base
4
+ include RestfulX::Configuration
5
+
6
+ def manifest
7
+ record do |m|
8
+ framework_release = RestfulX::FRAMEWORK_VERSION
9
+ framework_distribution_url = "http://restfulx.github.com/releases/restfulx-#{framework_release}.swc"
10
+ framework_destination_file = "lib/restfulx-#{framework_release}.swc"
11
+
12
+ if !options[:skip_framework] && !File.exist?(framework_destination_file)
13
+ puts "fetching #{framework_release} framework binary from: #{framework_distribution_url} ..."
14
+ open(framework_destination_file, "wb").write(open(framework_distribution_url).read)
15
+ puts "done. saved to #{framework_destination_file}"
16
+ end
17
+
18
+ m.dependency 'rx_controller', @args
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ Description:
2
+ Generates/updates the main Flex application controller, typically
3
+ app/flex/<yourappname>/controllers/ApplicationController.as
4
+
5
+ It pulls out all available models and commands from respective
6
+ folders and makes sure they'll be pulled into the Flex application
7
+ at runtime. Doesn't require any arguments or options.
8
+
9
+ Examples:
10
+ `./script/generate rx_controller`
@@ -0,0 +1,41 @@
1
+ class RxControllerGenerator < RubiGen::Base
2
+ include RestfulX::Configuration
3
+
4
+ attr_reader :project_name,
5
+ :flex_project_name,
6
+ :base_package,
7
+ :base_folder,
8
+ :command_controller_name,
9
+ :model_names,
10
+ :command_names,
11
+ :flex_root
12
+
13
+ def initialize(runtime_args, runtime_options = {})
14
+ super
15
+ @project_name, @flex_project_name, @command_controller_name,
16
+ @base_package, @base_folder, @flex_root = extract_names
17
+
18
+ @model_names = list_as_files("#{flex_root}/#{base_folder}/models")
19
+ @command_names = list_as_files("#{flex_root}/#{base_folder}/commands")
20
+ end
21
+
22
+ def manifest
23
+ record do |m|
24
+ m.template 'controller.as.erb', File.join("#{flex_root}/#{base_folder}/controllers",
25
+ "#{command_controller_name}.as")
26
+ if options[:gae]
27
+ m.file 'restful.py', 'app/controllers/restful.py' if !File.exist?('app/controllers/restful.py')
28
+ m.file 'assist.py', 'app/models/assist.py' if !File.exist?('app/models/assist.py')
29
+ m.file 'iso8601.py', 'app/models/iso8601.py' if !File.exist?('app/models/iso8601.py')
30
+ end
31
+ end
32
+ end
33
+
34
+ protected
35
+ def add_options!(opt)
36
+ opt.separator ''
37
+ opt.separator 'Options:'
38
+ opt.on("--gae", "Generate Google App Engine Python classes in addition to RestfulX Flex resources.",
39
+ "Default: false") { |v| options[:gae] = v }
40
+ end
41
+ end
@@ -0,0 +1,65 @@
1
+ # The MIT License
2
+ #
3
+ # Copyright (c) 2008 Dima Berastau
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to
7
+ # deal in the Software without restriction, including without limitation
8
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+ # and/or sell copies of the Software, and to permit persons to whom the
10
+ # Software is furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+ # DEALINGS IN THE SOFTWARE.
22
+
23
+ __author__ = 'Dima Berastau'
24
+
25
+ from google.appengine.ext import db
26
+ import datetime, iso8601
27
+
28
+ # Some useful module methods
29
+ def all(model):
30
+ items = "".join(str(item.to_xml().encode('UTF-8')) for item in model.all())
31
+ if items == "":
32
+ return '<entities type="array"/>'
33
+ else:
34
+ return '<entities kind="%s" type="array">%s</entities>' % (model.kind(), items)
35
+
36
+ def update_model_from_params(model, params):
37
+ for k, v in params.items():
38
+ if k.endswith("_id"):
39
+ if v == "":
40
+ setattr(model, k.replace("_id", ""), None)
41
+ else:
42
+ setattr(model, k.replace("_id", ""), db.Key(v))
43
+ elif hasattr(model, k):
44
+ if isinstance(getattr(model, k), bool):
45
+ if v == "false" or v == "":
46
+ setattr(model, k, False)
47
+ else:
48
+ setattr(model, k, True)
49
+ elif isinstance(getattr(model, k), float) and v != "":
50
+ setattr(model, k, float(v))
51
+ elif isinstance(getattr(model, k), int) and v != "":
52
+ setattr(model, k, int(v))
53
+ elif isinstance(getattr(model, k), datetime.datetime) and v != "":
54
+ value = iso8601.parse_date(v)
55
+ setattr(model, k, value)
56
+ elif isinstance(getattr(model, k), datetime.date) and v != "":
57
+ value = datetime.datetime.strptime(v, "%Y-%m-%d")
58
+ setattr(model, k, datetime.date(value.year, value.month, value.day))
59
+ elif isinstance(getattr(model, k), datetime.time) and v != "":
60
+ value = iso8601.parse_date(v)
61
+ setattr(model, k, datetime.time(value.hour, value.minute, value.second))
62
+ else:
63
+ setattr(model, k, v)
64
+
65
+ model.put()
@@ -0,0 +1,38 @@
1
+ package <%= base_package %>.controllers {
2
+ import <%= base_package %>.models.*;
3
+ import <%= base_package %>.commands.*;
4
+
5
+ import mx.core.Application;
6
+ import org.restfulx.Rx;
7
+ import org.restfulx.controllers.RxApplicationController;
8
+ import org.restfulx.utils.RxUtils;
9
+
10
+ public class <%= command_controller_name %> extends RxApplicationController {
11
+ private static var controller:<%= command_controller_name %>;
12
+
13
+ public static var models:Array = [<%= model_names %>]; /* Models */
14
+
15
+ public static var commands:Array = [<%= command_names %>]; /* Commands */
16
+
17
+ public function <%= command_controller_name %>(enforcer:SingletonEnforcer,
18
+ extraServices:Array, defaultServiceId:int = -1) {
19
+ super(commands, models, extraServices, defaultServiceId);
20
+ }
21
+
22
+ public static function get instance():<%= command_controller_name %> {
23
+ if (controller == null) initialize();
24
+ return controller;
25
+ }
26
+
27
+ public static function initialize(extraServices:Array = null,
28
+ defaultServiceId:int = -1, airDatabaseName:String = null):void {
29
+ if (!RxUtils.isEmpty(airDatabaseName)) Rx.airDatabaseName = airDatabaseName;
30
+ controller = new <%= command_controller_name %>(new SingletonEnforcer,
31
+ extraServices, defaultServiceId);
32
+ Rx.sessionToken = Application.application.parameters.session_token;
33
+ Rx.authenticityToken = Application.application.parameters.authenticity_token;
34
+ }
35
+ }
36
+ }
37
+
38
+ class SingletonEnforcer {}
@@ -0,0 +1,92 @@
1
+ from datetime import datetime, timedelta, tzinfo
2
+ import re
3
+
4
+ __all__ = ["parse_date", "ParseError"]
5
+
6
+ # Adapted from http://delete.me.uk/2005/03/iso8601.html
7
+ ISO8601_REGEX = re.compile(r"(?P<year>[0-9]{4})(-(?P<month>[0-9]{1,2})(-(?P<day>[0-9]{1,2})"
8
+ r"((?P<separator>.)(?P<hour>[0-9]{2}):(?P<minute>[0-9]{2})(:(?P<second>[0-9]{2})(\.(?P<fraction>[0-9]+))?)?"
9
+ r"(?P<timezone>Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?"
10
+ )
11
+ TIMEZONE_REGEX = re.compile("(?P<prefix>[+-])(?P<hours>[0-9]{2}).(?P<minutes>[0-9]{2})")
12
+
13
+ class ParseError(Exception):
14
+ """Raised when there is a problem parsing a date string"""
15
+
16
+ # Yoinked from python docs
17
+ ZERO = timedelta(0)
18
+ class Utc(tzinfo):
19
+ """UTC
20
+
21
+ """
22
+ def utcoffset(self, dt):
23
+ return ZERO
24
+
25
+ def tzname(self, dt):
26
+ return "UTC"
27
+
28
+ def dst(self, dt):
29
+ return ZERO
30
+ UTC = Utc()
31
+
32
+ class FixedOffset(tzinfo):
33
+ """Fixed offset in hours and minutes from UTC
34
+
35
+ """
36
+ def __init__(self, offset_hours, offset_minutes, name):
37
+ self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes)
38
+ self.__name = name
39
+
40
+ def utcoffset(self, dt):
41
+ return self.__offset
42
+
43
+ def tzname(self, dt):
44
+ return self.__name
45
+
46
+ def dst(self, dt):
47
+ return ZERO
48
+
49
+ def __repr__(self):
50
+ return "<FixedOffset %r>" % self.__name
51
+
52
+ def parse_timezone(tzstring, default_timezone=UTC):
53
+ """Parses ISO 8601 time zone specs into tzinfo offsets
54
+
55
+ """
56
+ if tzstring == "Z":
57
+ return default_timezone
58
+ # This isn't strictly correct, but it's common to encounter dates without
59
+ # timezones so I'll assume the default (which defaults to UTC).
60
+ # Addresses issue 4.
61
+ if tzstring is None:
62
+ return default_timezone
63
+ m = TIMEZONE_REGEX.match(tzstring)
64
+ prefix, hours, minutes = m.groups()
65
+ hours, minutes = int(hours), int(minutes)
66
+ if prefix == "-":
67
+ hours = -hours
68
+ minutes = -minutes
69
+ return FixedOffset(hours, minutes, tzstring)
70
+
71
+ def parse_date(datestring, default_timezone=UTC):
72
+ """Parses ISO 8601 dates into datetime objects
73
+
74
+ The timezone is parsed from the date string. However it is quite common to
75
+ have dates without a timezone (not strictly correct). In this case the
76
+ default timezone specified in default_timezone is used. This is UTC by
77
+ default.
78
+ """
79
+ if not isinstance(datestring, basestring):
80
+ raise ParseError("Expecting a string %r" % datestring)
81
+ m = ISO8601_REGEX.match(datestring)
82
+ if not m:
83
+ raise ParseError("Unable to parse date string %r" % datestring)
84
+ groups = m.groupdict()
85
+ tz = parse_timezone(groups["timezone"], default_timezone=default_timezone)
86
+ if groups["fraction"] is None:
87
+ groups["fraction"] = 0
88
+ else:
89
+ groups["fraction"] = int(float("0.%s" % groups["fraction"]) * 1e6)
90
+ return datetime(int(groups["year"]), int(groups["month"]), int(groups["day"]),
91
+ int(groups["hour"]), int(groups["minute"]), int(groups["second"]),
92
+ int(groups["fraction"]), tz)
@@ -0,0 +1,136 @@
1
+ # The MIT License
2
+ #
3
+ # Copyright (c) 2008 William T. Katz
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to
7
+ # deal in the Software without restriction, including without limitation
8
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+ # and/or sell copies of the Software, and to permit persons to whom the
10
+ # Software is furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+ # DEALINGS IN THE SOFTWARE.
22
+
23
+ """
24
+ RESTful Controller
25
+
26
+ We want our RESTful controllers to simply throw up their hands if they get
27
+ an unhandled HTTP verb. This is better for rich clients and server load
28
+ than throwing back lots of useless HTML.
29
+
30
+ These inherited methods should be overridden if there's a chance a human
31
+ browser is involved.
32
+
33
+ TODO: Return more information HTTP status codes that won't autotrip
34
+ browser login forms. For example, return status 405 (Method not allowed)
35
+ with an Allow header containing the list of valid methods.
36
+ """
37
+ __author__ = 'William T. Katz'
38
+
39
+ from google.appengine.ext import webapp
40
+
41
+ import logging
42
+
43
+ # Some useful module methods
44
+ def send_successful_response(handler, response):
45
+ logging.debug("Sending successful response: %s", response)
46
+ handler.response.headers["Content-Type"] = "application/xml"
47
+ handler.response.out.write('<?xml version="1.0" encoding="UTF-8"?>')
48
+ handler.response.out.write(response)
49
+
50
+ def get_model_key(handler):
51
+ return handler.request.path_info.split("/").pop().replace(".xml", "")
52
+
53
+ def get_sent_properties(request_func, propname_list):
54
+ """
55
+ This maps request strings to values in a hash, optionally run through
56
+ a function with previous request values as parameters to the func.
57
+ 1) key -> just read in the corresponding request value
58
+ 2) tuple (key, func) -> Read the request value for the string key
59
+ and pass it through func
60
+ 3) tuple (key, func, additional keys...) -> Get the request
61
+ values for the additional keys and pass them through func
62
+ before setting the key's value with the output.
63
+ If a key is not present in the request, then we do not insert a key
64
+ with None or empty string. The key is simply absent, therefore allowing
65
+ you to use the returned hash to initial a Model instance.
66
+ """
67
+ prop_hash = {}
68
+ for item in propname_list:
69
+ if isinstance(item, basestring):
70
+ key = item
71
+ value = request_func(item)
72
+ elif isinstance(item, tuple):
73
+ key = item[0]
74
+ prop_func = item[1]
75
+ if len(item) <= 2:
76
+ value = prop_func(request_func(key))
77
+ else:
78
+ try:
79
+ addl_keys = map(prop_hash.get, item[2:])
80
+ value = prop_func(*addl_keys)
81
+ except:
82
+ return None
83
+ if value:
84
+ prop_hash[key] = value
85
+ return prop_hash
86
+
87
+ def methods_via_query_allowed(handler_method):
88
+ """
89
+ A decorator to automatically re-route overloaded POSTs
90
+ that specify the real HTTP method in a _method query string.
91
+
92
+ To use it, decorate your post method like this:
93
+
94
+ import restful
95
+ ...
96
+ @restful.methods_via_query_allowed
97
+ def post(self):
98
+ pass
99
+
100
+ The decorator will check for a _method query string or POST argument,
101
+ and if present, will redirect to delete(), put(), etc.
102
+ """
103
+ def redirect_if_needed(self, *args, **kwargs):
104
+ real_verb = self.request.get('_method', None)
105
+ if not real_verb and 'X-HTTP-Method-Override' in self.request.environ:
106
+ real_verb = self.request.environ['X-HTTP-Method-Override']
107
+ if real_verb:
108
+ logging.debug("Redirected from POST. Detected method override = %s", real_verb)
109
+ method = real_verb.upper()
110
+ if method == 'HEAD':
111
+ self.head(*args, **kwargs)
112
+ elif method == 'PUT':
113
+ self.put(*args, **kwargs)
114
+ elif method == 'DELETE':
115
+ self.delete(*args, **kwargs)
116
+ elif method == 'TRACE':
117
+ self.trace(*args, **kwargs)
118
+ elif method == 'OPTIONS':
119
+ self.head(*args, **kwargs)
120
+ # POST and GET included for completeness
121
+ elif method == 'POST':
122
+ self.post(*args, **kwargs)
123
+ elif method == 'GET':
124
+ self.get(*args, **kwargs)
125
+ else:
126
+ self.error(405)
127
+ else:
128
+ handler_method(self, *args, **kwargs)
129
+ return redirect_if_needed
130
+
131
+ class Controller(webapp.RequestHandler):
132
+ def get(self, *params):
133
+ self.redirect("/403.html")
134
+
135
+ def head(self, *params):
136
+ pass