dima-restfulx 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/README.rdoc +1 -1
  2. data/Rakefile +4 -2
  3. data/VERSION.yml +1 -1
  4. data/app_generators/rx_app/rx_app_generator.rb +2 -2
  5. data/app_generators/rx_app/templates/actionscript.properties +1 -1
  6. data/app_generators/rx_app/templates/actionscriptair.properties +1 -1
  7. data/app_generators/rx_app/templates/mainapp-config.xml +1 -0
  8. data/app_generators/rx_app/templates/mainapp.mxml +19 -3
  9. data/app_generators/rx_app/templates/restfulx.yml +9 -24
  10. data/bin/rx-gen +1 -1
  11. data/lib/restfulx/active_foo.rb +5 -3
  12. data/lib/restfulx/active_record_tasks.rb +1 -2
  13. data/lib/restfulx/active_record_uuid_helper.rb +17 -0
  14. data/lib/restfulx/configuration.rb +20 -26
  15. data/lib/restfulx/{schema_to_yaml → rails/schema_to_yaml}/extensions/enumerable.rb +1 -0
  16. data/lib/restfulx/{schema_to_yaml → rails/schema_to_yaml}/settings/config.rb +1 -0
  17. data/lib/restfulx/{schema_to_yaml → rails/schema_to_yaml}/settings/core.rb +4 -1
  18. data/lib/restfulx/{schema_to_yaml.rb → rails/schema_to_yaml.rb} +2 -12
  19. data/lib/restfulx/rails/swf_helper.rb +7 -8
  20. data/lib/restfulx/tasks.rb +0 -1
  21. data/lib/restfulx.rb +2 -10
  22. data/rails_generators/rx_config/rx_config_generator.rb +20 -9
  23. data/rails_generators/rx_config/templates/actionscript.properties +1 -1
  24. data/rails_generators/rx_config/templates/actionscriptair.properties +1 -1
  25. data/rails_generators/rx_config/templates/mainapp-config.xml +1 -0
  26. data/rails_generators/rx_config/templates/restfulx.erb +46 -10
  27. data/rails_generators/rx_config/templates/restfulx.yml +9 -5
  28. data/rails_generators/rx_config/templates/session_store_flash.erb +1 -0
  29. data/rails_generators/rx_main_app/rx_main_app_generator.rb +2 -2
  30. data/rails_generators/rx_main_app/templates/mainapp.mxml +1 -1
  31. data/rails_generators/rx_scaffold/rx_scaffold_generator.rb +16 -7
  32. data/rails_generators/rx_scaffold/templates/controllers/resource_controller.rb.erb +2 -2
  33. data/rails_generators/rx_scaffold/templates/functional_test.rb +45 -0
  34. data/rails_generators/rx_scaffold/templates/helper_test.rb +4 -0
  35. data/rails_generators/rx_scaffold/templates/layouts/default.erb +7 -5
  36. data/rails_generators/rx_scaffold/templates/migration.rb.erb +4 -4
  37. data/rxgen_generators/rx_config/rx_config_generator.rb +2 -0
  38. data/rxgen_generators/rx_controller/rx_controller_generator.rb +1 -0
  39. data/rxgen_generators/rx_controller/templates/assist.py +4 -4
  40. data/rxgen_generators/rx_controller/templates/iso8601.py +92 -0
  41. data/rxgen_generators/rx_main_app/rx_main_app_generator.rb +2 -2
  42. data/rxgen_generators/rx_main_app/templates/mainapp.mxml +19 -3
  43. data/rxgen_generators/rx_scaffold/rx_scaffold_generator.rb +57 -10
  44. data/rxgen_generators/rx_scaffold/templates/{component.mxml.erb → layouts/default.erb} +56 -10
  45. data/rxgen_generators/rx_scaffold/templates/model.as.erb +33 -2
  46. data/spec/restfulx_spec.rb +1 -4
  47. data/spec/spec_helper.rb +0 -3
  48. data/tasks/restfulx.rake +2 -0
  49. data/test/rails/helpers/functional_test_helper.rb +1 -1
  50. data/test/rails/helpers/test_helper.rb +1 -8
  51. data/test/rails/helpers/unit_test_helper.rb +2 -3
  52. data/test/rails/test_active_foo.rb +1 -1
  53. data/test/rails/test_rails_integration_functional.rb +1 -1
  54. data/test/rails/test_to_fxml.rb +1 -1
  55. data/test/rails/test_to_json.rb +1 -1
  56. metadata +60 -75
  57. data/lib/restfulx/uuid_helper.rb +0 -15
  58. /data/test/rails/controllers/{application.rb → application_controller.rb} +0 -0
@@ -1,12 +1,47 @@
1
- # When you do file uploads from Flash with File.upload() that, unfortunately generates a new session id,
2
- # which will fail to authenticate if you are using restful-authentication plugin or equivalent.
3
- #
4
- # The following code is a work-around for the Flash bug that prevents file uploader
5
- # from sending correct session_id. Here, we hack the Session#initialize method and force the session_id
6
- # to load from the query string via the request URI.
7
- #
8
- # Based on the code from http://seventytwo.co.uk/posts/making-swfupload-and-rails-work-together
9
- #
1
+ # the following patches allow us to overwrite session key on file uploads from Flash,
2
+ # which ends up creating a new session for every File.upload() invocation.
3
+
4
+ <% if RAILS_GEM_VERSION =~ /^2.3/ -%>
5
+ require 'rack/utils'
6
+
7
+ class FlashSessionCookieMiddleware
8
+ def initialize(app, session_key = '_session_id')
9
+ @app = app
10
+ @session_key = session_key
11
+ @session_token = "_session_id"
12
+ end
13
+
14
+ def call(env)
15
+ if env['HTTP_USER_AGENT'] =~ /^(Adobe|Shockwave) Flash/
16
+ params = ::Rack::Utils.parse_query(env['QUERY_STRING'])
17
+ env['HTTP_COOKIE'] = [ @session_key, params[@session_token] ].join('=').freeze unless params[@session_token].nil?
18
+ end
19
+ @app.call(env)
20
+ end
21
+ end
22
+
23
+ class FlexNestedAttributeMiddleware
24
+ def initialize(app)
25
+ @app = app
26
+ end
27
+
28
+ def call(env)
29
+ req = Rack::Request.new(env)
30
+ if req && req.path_info =~ /\.fxml$/
31
+ if req.put? || req.post? || req.delete?
32
+ req.params.each do |key,value|
33
+ value.select { |k,v| k =~ /\_attributes$/ }.each do |match|
34
+ env['rack.request.form_hash'][key][match[0]] = ActiveSupport::JSON.decode(match[1])
35
+ end
36
+ end
37
+ end
38
+ end
39
+ @app.call(env)
40
+ end
41
+ end
42
+
43
+ ActionController::Dispatcher.middleware.insert_after 'ActionController::ParamsParser', FlexNestedAttributeMiddleware
44
+ <% else -%>
10
45
  class CGI::Session
11
46
  alias original_initialize initialize
12
47
 
@@ -27,6 +62,7 @@ class CGI::Session
27
62
  return option
28
63
  end
29
64
  end
65
+ <% end -%>
30
66
 
31
67
  # If you have configured your Rails/Flex/AIR application to share authenticity_token
32
68
  # comment this out to enable forgery protection. By default, this is disabled to allow
@@ -35,5 +71,5 @@ ActionController::Base.allow_forgery_protection = false
35
71
 
36
72
  <% if distributed -%>
37
73
  # If we are in distributed mode we need to make sure that the RestfulX::UUIDHelper is loaded
38
- require "restfulx/uuid_helper"
74
+ require "restfulx/active_record_uuid_helper"
39
75
  <% end -%>
@@ -2,22 +2,22 @@
2
2
 
3
3
  # This option controls what the main Flex application file will be called.
4
4
  # By default it will be equal to the name of your rails project camelized
5
- project-name: <%= project_name %>
5
+ project_name: <%= project_name %>
6
6
 
7
7
  # This options determines what the root folder for generated Flex code is.
8
8
  # By default 'app/flex'
9
- flex-root: <%= flex_root %>
9
+ flex_root: <%= flex_root %>
10
10
 
11
11
  # By default flex models, commands, controllers and components are genearated into
12
12
  # <flex-root>/<your rails project name> folder. If you'd like to customize the target folder
13
13
  # (to say append a "com" package before your rails project name) uncomment the line below
14
14
  # base-package must follow the usual flex package notation (a string separated by ".")
15
- base-package: <%= base_package %>
15
+ base_package: <%= base_package %>
16
16
 
17
17
  # Main RestfulX controller is typically named AppicationController. This controller is created in
18
18
  # <base-package>.controllers folder. You can customize the name by uncommenting the following line
19
19
  # and changing the controller name.
20
- controller-name: <%= command_controller_name %>
20
+ controller_name: <%= command_controller_name %>
21
21
 
22
22
  # If you are using Rails on the back-end and Adobe AIR as the client you can generate Rails/Flex/AIR
23
23
  # code to take advantage of synchronization (online/offline) support in RestfulX by changing the following
@@ -35,17 +35,21 @@ defaults: &defaults
35
35
  tables: [table1 table2]
36
36
  fields: [field1 field2]
37
37
 
38
+ # set up specific options for development environment
38
39
  development:
39
40
  <<: *defaults
40
41
 
42
+ # set up specific options for test environment
41
43
  test:
42
44
  <<: *defaults
43
45
 
46
+ # set up specific options for production environment
44
47
  production:
45
48
  <<: *defaults
46
49
 
47
- # Special model.yml fields for RestfulX code-generation:
50
+ # The following special model.yml fields are supported.
48
51
  #
52
+ # For example:
49
53
  # attachment_field: [avatar]
50
54
  # * arg takes Paperclip field name, or takes [uploaded_data] for Attachment_Fu
51
55
  # belongs_to: [account, profile]
@@ -0,0 +1 @@
1
+ ActionController::Dispatcher.middleware.use FlashSessionCookieMiddleware, ActionController::Base.session_options[:key]
@@ -43,8 +43,8 @@ class RxMainAppGenerator < Rails::Generator::Base
43
43
  end
44
44
 
45
45
  @component_names = []
46
- if File.exists?("#{flex_root}/#{base_folder}/components/generated")
47
- @component_names = list_mxml_files("#{flex_root}/#{base_folder}/components/generated")
46
+ if File.exists?("#{flex_root}/#{base_folder}/views/generated")
47
+ @component_names = list_mxml_files("#{flex_root}/#{base_folder}/views/generated")
48
48
  end
49
49
  end
50
50
 
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="utf-8"?>
2
2
  <mx:<%= application_tag %> xmlns:mx="http://www.adobe.com/2006/mxml"
3
- xmlns:generated="<%= base_package %>.components.generated.*"
3
+ xmlns:generated="<%= base_package %>.views.generated.*"
4
4
  paddingBottom="8" paddingLeft="8" paddingRight="8" paddingTop="8"
5
5
  layout="horizontal" styleName="plain" initialize="init()">
6
6
  <mx:Script>
@@ -125,7 +125,7 @@ class RxScaffoldGenerator < Rails::Generator::NamedBase
125
125
  end
126
126
 
127
127
  def manifest
128
- record do |m|
128
+ record do |m|
129
129
  unless options[:flex_view_only]
130
130
  m.template 'model.as.erb',
131
131
  File.join("#{@flex_root}", base_folder, "models", "#{@class_name}.as"),
@@ -139,11 +139,11 @@ class RxScaffoldGenerator < Rails::Generator::NamedBase
139
139
 
140
140
  if @layout.size > 0
141
141
  m.template "layouts/#{@layout}.erb",
142
- File.join("#{@flex_root}", base_folder, "components", "generated", "#{@class_name}Box.mxml"),
142
+ File.join("#{@flex_root}", base_folder, "views", "generated", "#{@class_name}Box.mxml"),
143
143
  :assigns => { :resource_controller_name => "#{file_name.pluralize}" }
144
144
  else
145
145
  m.template "layouts/#{RxSettings.layouts.default}.erb",
146
- File.join("#{@flex_root}", base_folder, "components", "generated", "#{@class_name}Box.mxml"),
146
+ File.join("#{@flex_root}", base_folder, "views", "generated", "#{@class_name}Box.mxml"),
147
147
  :assigns => { :resource_controller_name => "#{file_name.pluralize}" }
148
148
  end
149
149
 
@@ -155,11 +155,20 @@ class RxScaffoldGenerator < Rails::Generator::NamedBase
155
155
  unless options[:skip_migration]
156
156
  FileUtils.rm Dir.glob("db/migrate/[0-9]*_create_#{file_path.gsub(/\//, '_').pluralize}.rb"), :force => true
157
157
  m.migration_template 'migration.rb.erb', 'db/migrate', :assigns => {
158
- :migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"
158
+ :migration_name => "Create#{file_path.gsub(/\//, '_').pluralize.camelcase.gsub(/::/, '')}"
159
159
  }, :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}" unless options[:flex_only]
160
160
  end
161
-
162
- m.route_resources controller_file_name
161
+
162
+ m.directory(File.join('test/functional', controller_class_path))
163
+ m.directory(File.join('test/unit', class_path))
164
+ m.directory(File.join('test/unit/helpers', class_path))
165
+
166
+ m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb"))
167
+ m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb"))
168
+
169
+ if File.open('config/routes.rb').grep(/^\s*map.resources :#{controller_file_name}/).empty?
170
+ m.route_resources controller_file_name
171
+ end
163
172
 
164
173
  m.dependency 'rx_controller', [name] + @args, :collision => :force
165
174
  end
@@ -230,4 +239,4 @@ class RxScaffoldGenerator < Rails::Generator::NamedBase
230
239
  opt.on("-fv", "--flex-view-only", "Only generate the Flex component files",
231
240
  "Default: false") { |v| options[:flex_view_only] = v }
232
241
  end
233
- end
242
+ end
@@ -10,8 +10,8 @@ class <%= controller_class_name %>Controller < ResourceController::Base
10
10
  update.failure.wants.fxml { render :fxml => @object.errors }
11
11
  destroy.wants.fxml { render :fxml => @object }
12
12
 
13
- private
14
-
13
+ private
14
+
15
15
  def collection
16
16
  @collection = end_of_association_chain.all
17
17
  end
@@ -0,0 +1,45 @@
1
+ require 'test_helper'
2
+
3
+ class <%= controller_class_name %>ControllerTest < ActionController::TestCase
4
+ test "should get index" do
5
+ get :index
6
+ assert_response :success
7
+ assert_not_nil assigns(:<%= table_name %>)
8
+ end
9
+
10
+ test "should get new" do
11
+ get :new
12
+ assert_response :success
13
+ end
14
+
15
+ test "should create <%= file_name %>" do
16
+ assert_difference('<%= class_name %>.count') do
17
+ post :create, :<%= file_name %> => { }
18
+ end
19
+
20
+ assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
21
+ end
22
+
23
+ test "should show <%= file_name %>" do
24
+ get :show, :id => <%= table_name %>(:one).id
25
+ assert_response :success
26
+ end
27
+
28
+ test "should get edit" do
29
+ get :edit, :id => <%= table_name %>(:one).id
30
+ assert_response :success
31
+ end
32
+
33
+ test "should update <%= file_name %>" do
34
+ put :update, :id => <%= table_name %>(:one).id, :<%= file_name %> => { }
35
+ assert_redirected_to <%= file_name %>_path(assigns(:<%= file_name %>))
36
+ end
37
+
38
+ test "should destroy <%= file_name %>" do
39
+ assert_difference('<%= class_name %>.count', -1) do
40
+ delete :destroy, :id => <%= table_name %>(:one).id
41
+ end
42
+
43
+ assert_redirected_to <%= table_name %>_path
44
+ end
45
+ end
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class <%= controller_class_name %>HelperTest < ActionView::TestCase
4
+ end
@@ -9,12 +9,14 @@
9
9
  import <%= base_package %>.models.<%= model.camelcase %>;
10
10
  <% end -%>
11
11
  <% if attachment_field.size > 0 -%>
12
+ import flash.net.FileReference;
12
13
  import org.restfulx.utils.RxFileReference;
13
14
  <% end -%>
14
15
 
15
16
  [Bindable]
16
17
  private var <%= class_name.dcfirst %>:<%= class_name %> = new <%= class_name %>();
17
18
  <% if attachment_field.size > 0 -%>
19
+
18
20
  [Bindable]
19
21
  private var fileName:String = "None selected";
20
22
 
@@ -83,10 +85,10 @@
83
85
 
84
86
  private function chooseFile():void {
85
87
  file = new RxFileReference("<%= attachment_field[0].camelcase(:lower) %>");
86
- file.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler, false, 0, true);
87
- file.addEventListener(Event.SELECT, selectFile, false, 0, true);
88
- file.addEventListener(Event.CANCEL, cancelBrowse, false, 0, true);
89
- file.browse();
88
+ file.reference.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler, false, 0, true);
89
+ file.reference.addEventListener(Event.SELECT, selectFile, false, 0, true);
90
+ file.reference.addEventListener(Event.CANCEL, cancelBrowse, false, 0, true);
91
+ file.reference.browse();
90
92
  }
91
93
 
92
94
  private function selectFile(event:Event):void {
@@ -98,7 +100,7 @@
98
100
  }
99
101
 
100
102
  private function fileSelected(event:Event):void {
101
- fileName = RxFileReference(event.target).name;
103
+ fileName = FileReference(event.target).name;
102
104
  }
103
105
 
104
106
  private function ioErrorHandler(event:Event):void {
@@ -19,10 +19,10 @@ class <%= migration_name %> < ActiveRecord::Migration
19
19
  <% if attachment_field.size > 0 -%>
20
20
  <% if RxSettings.attachment_plugin == 'paperclip' -%>
21
21
  # For paperclip
22
- t.column :avatar_file_name, :string
23
- t.column :avatar_content_type, :string
24
- t.column :avatar_file_size, :integer
25
- t.column :avatar_updated_at, :datetime
22
+ t.column :<%= attachment_field[0] %>_file_name, :string
23
+ t.column :<%= attachment_field[0] %>_content_type, :string
24
+ t.column :<%= attachment_field[0] %>_file_size, :integer
25
+ t.column :<%= attachment_field[0] %>_updated_at, :datetime
26
26
  <% elsif RxSettings.attachment_plugin == 'attachment_fu' -%>
27
27
  # For attachment_fu
28
28
  t.column :parent_id, :integer
@@ -14,6 +14,8 @@ class RxConfigGenerator < RubiGen::Base
14
14
  open(framework_destination_file, "wb").write(open(framework_distribution_url).read)
15
15
  puts "done. saved to #{framework_destination_file}"
16
16
  end
17
+
18
+ m.dependency 'rx_controller', @args
17
19
  end
18
20
  end
19
21
  end
@@ -26,6 +26,7 @@ class RxControllerGenerator < RubiGen::Base
26
26
  if options[:gae]
27
27
  m.file 'restful.py', 'app/controllers/restful.py' if !File.exist?('app/controllers/restful.py')
28
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')
29
30
  end
30
31
  end
31
32
  end
@@ -23,11 +23,11 @@
23
23
  __author__ = 'Dima Berastau'
24
24
 
25
25
  from google.appengine.ext import db
26
- import datetime
26
+ import datetime, iso8601
27
27
 
28
28
  # Some useful module methods
29
29
  def all(model):
30
- items = "".join(str(item.to_xml()) for item in model.all())
30
+ items = "".join(str(item.to_xml().encode('UTF-8')) for item in model.all())
31
31
  if items == "":
32
32
  return '<entities type="array"/>'
33
33
  else:
@@ -51,13 +51,13 @@ def update_model_from_params(model, params):
51
51
  elif isinstance(getattr(model, k), int) and v != "":
52
52
  setattr(model, k, int(v))
53
53
  elif isinstance(getattr(model, k), datetime.datetime) and v != "":
54
- value = datetime.datetime.strptime(v, "%Y-%m-%dT%H:%M:%S%Z")
54
+ value = iso8601.parse_date(v)
55
55
  setattr(model, k, value)
56
56
  elif isinstance(getattr(model, k), datetime.date) and v != "":
57
57
  value = datetime.datetime.strptime(v, "%Y-%m-%d")
58
58
  setattr(model, k, datetime.date(value.year, value.month, value.day))
59
59
  elif isinstance(getattr(model, k), datetime.time) and v != "":
60
- value = datetime.datetime.strptime(v, "%Y-%m-%dT%H:%M:%S%Z")
60
+ value = iso8601.parse_date(v)
61
61
  setattr(model, k, datetime.time(value.hour, value.minute, value.second))
62
62
  else:
63
63
  setattr(model, k, v)
@@ -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)
@@ -32,8 +32,8 @@ class RxMainAppGenerator < RubiGen::Base
32
32
  end
33
33
 
34
34
  @component_names = []
35
- if File.exists?("#{flex_root}/#{base_folder}/components/generated")
36
- @component_names = list_mxml_files("#{flex_root}/#{base_folder}/components/generated")
35
+ if File.exists?("#{flex_root}/#{base_folder}/views/generated")
36
+ @component_names = list_mxml_files("#{flex_root}/#{base_folder}/views/generated")
37
37
  end
38
38
 
39
39
  @controller_names = []
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="utf-8"?>
2
2
  <mx:<%= application_tag %> xmlns:mx="http://www.adobe.com/2006/mxml"
3
- xmlns:generated="<%= base_package %>.components.generated.*"
3
+ xmlns:generated="<%= base_package %>.views.generated.*"
4
4
  paddingBottom="8" paddingLeft="8" paddingRight="8" paddingTop="8"
5
5
  layout="horizontal" styleName="plain" initialize="init()">
6
6
  <mx:Script>
@@ -14,12 +14,13 @@
14
14
  import org.restfulx.events.PushStartEvent;
15
15
  import org.restfulx.controllers.ChangeController;
16
16
  import org.restfulx.services.ISyncingServiceProvider;
17
- import org.restfulx.services.http.XMLHTTPServiceProvider;
18
17
  <% end -%>
19
18
  import org.restfulx.services.air.AIRServiceProvider;
20
19
  <% end -%>
21
20
  <% if use_gae -%>
22
21
  import org.restfulx.services.http.GAEHTTPServiceProvider;
22
+ <% else -%>
23
+ import org.restfulx.services.http.XMLHTTPServiceProvider;
23
24
  <% end -%>
24
25
  import org.restfulx.Rx;
25
26
  import <%= base_package %>.controllers.<%= command_controller_name %>;
@@ -53,9 +54,15 @@
53
54
  AIRServiceProvider.ID, "<%= base_package %>");
54
55
  <% if distributed -%>
55
56
 
57
+ <% if use_gae -%>
58
+ Rx.changes.setSyncProviders(
59
+ ISyncingServiceProvider(Rx.services.getServiceProvider(AIRServiceProvider.ID)),
60
+ Rx.services.getServiceProvider(GAEHTTPServiceProvider.ID));
61
+ <% else -%>
56
62
  Rx.changes.setSyncProviders(
57
63
  ISyncingServiceProvider(Rx.services.getServiceProvider(AIRServiceProvider.ID)),
58
64
  Rx.services.getServiceProvider(XMLHTTPServiceProvider.ID));
65
+ <% end -%>
59
66
 
60
67
  Rx.changes.addEventListener(PushStartEvent.ID, onPushStart);
61
68
  Rx.changes.addEventListener(PushEndEvent.ID, onPushEnd);
@@ -93,7 +100,11 @@
93
100
  online = (socketMonitor.available) ? true : false;
94
101
 
95
102
  if (online) {
103
+ <% if use_gae -%>
104
+ Rx.defaultServiceId = GAEHTTPServiceProvider.ID;
105
+ <% else -%>
96
106
  Rx.defaultServiceId = XMLHTTPServiceProvider.ID;
107
+ <% end -%>
97
108
  } else {
98
109
  Rx.defaultServiceId = AIRServiceProvider.ID;
99
110
  }
@@ -101,8 +112,13 @@
101
112
 
102
113
  private function getCurrentProviderName(id:int):String {
103
114
  switch (id) {
115
+ <% if use_gae -%>
116
+ case GAEHTTPServiceProvider.ID:
117
+ return "GAE";
118
+ <% else -%>
104
119
  case XMLHTTPServiceProvider.ID:
105
- return "Rails";
120
+ return "XML/HTTP";
121
+ <% end -%>
106
122
  case AIRServiceProvider.ID:
107
123
  return "AIR (SQLite)";
108
124
  default :
@@ -70,7 +70,14 @@ class RxScaffoldGenerator < RubiGen::Base
70
70
 
71
71
  attr_reader :belongs_tos,
72
72
  :has_manies,
73
- :has_ones
73
+ :has_ones,
74
+ :attachment_field,
75
+ :has_many_through,
76
+ :polymorphic,
77
+ :tree_model,
78
+ :layout,
79
+ :ignored_fields,
80
+ :args_for_generation
74
81
 
75
82
  attr_reader :name,
76
83
  :class_name,
@@ -93,14 +100,20 @@ class RxScaffoldGenerator < RubiGen::Base
93
100
  end
94
101
 
95
102
  def manifest
96
- record do |m|
103
+ record do |m|
97
104
  m.template 'model.as.erb',
98
105
  File.join("#{flex_root}", base_folder, "models", "#{@class_name}.as"),
99
106
  :assigns => { :resource_controller_name => "#{file_name.pluralize}" }
100
107
 
101
- m.template 'component.mxml.erb',
102
- File.join("#{flex_root}", base_folder, "components", "generated", "#{@class_name}Box.mxml"),
103
- :assigns => { :resource_controller_name => "#{file_name.pluralize}" }
108
+ if @layout.size > 0
109
+ m.template "layouts/#{@layout}.erb",
110
+ File.join("#{@flex_root}", base_folder, "views", "generated", "#{@class_name}Box.mxml"),
111
+ :assigns => { :resource_controller_name => "#{file_name.pluralize}" }
112
+ else
113
+ m.template "layouts/#{RxSettings.layouts.default}.erb",
114
+ File.join("#{@flex_root}", base_folder, "views", "generated", "#{@class_name}Box.mxml"),
115
+ :assigns => { :resource_controller_name => "#{file_name.pluralize}" }
116
+ end
104
117
 
105
118
  if options[:gae]
106
119
  m.template 'controller.py.erb', "app/controllers/#{file_name.pluralize}.py"
@@ -113,23 +126,57 @@ class RxScaffoldGenerator < RubiGen::Base
113
126
 
114
127
  protected
115
128
  def extract_relationships
129
+ # arrays
116
130
  @belongs_tos = []
117
131
  @has_ones = []
118
132
  @has_manies = []
119
- # Figure out has_one, has_many and belongs_to based on args
133
+ @attachment_field = []
134
+ @polymorphic = []
135
+ @tree_model = []
136
+ @layout = []
137
+ @ignored_fields = []
138
+
139
+ # hashes
140
+ @has_many_through = {}
141
+
120
142
  @args.each do |arg|
143
+ # arrays
121
144
  if arg =~ /^has_one:/
122
- # arg = "has_one:arg1,arg2", so all the has_one are together
123
145
  @has_ones = arg.split(':')[1].split(',')
124
146
  elsif arg =~ /^has_many:/
125
- # arg = "has_many:arg1,arg2", so all the has_many are together
126
147
  @has_manies = arg.split(":")[1].split(",")
127
- elsif arg =~ /^belongs_to:/ # belongs_to:arg1,arg2
148
+ elsif arg =~ /^belongs_to:/
128
149
  @belongs_tos = arg.split(":")[1].split(',')
150
+ elsif arg =~ /^attachment_field:/
151
+ @attachment_field = arg.split(":")[1].split(',')
152
+ elsif arg =~ /^polymorphic:/
153
+ @polymorphic = arg.split(":")[1].split(',')
154
+ elsif arg =~ /^tree_model:/
155
+ @tree_model = arg.split(":")[1].split(',')
156
+ elsif arg =~ /^layout:/
157
+ @layout = arg.split(":")[1].split(',')
158
+ elsif arg =~ /^ignored_fields:/
159
+ @ignored_fields = arg.split(":")[1].split(',')
160
+ # hashes
161
+ elsif arg =~ /^has_many_through:/
162
+ hmt_arr = arg.split(":")[1].split(',')
163
+ @has_many_through[hmt_arr.first] = hmt_arr.last
129
164
  end
130
165
  end
131
166
 
132
- @args.delete_if { |elt| elt =~ /^(has_one|has_many|belongs_to):/ }
167
+ # delete special fields from @args ivar
168
+ %w(has_one has_many belongs_to attachment_field has_many_through
169
+ polymorphic tree_model layout ignored_fields).each do |special_field|
170
+ @args.delete_if { |f| f =~ /^(#{special_field}):/ }
171
+ end
172
+
173
+ @args_for_generation = @args.clone
174
+
175
+ # delete ignored_fields from @args ivar
176
+ @ignored_fields.each do |ignored|
177
+ @args.delete_if { |f| f =~ /^(#{ignored}):/ }
178
+ end
179
+
133
180
  end
134
181
 
135
182
  def attributes