dima-restfulx 1.2.2 → 1.2.3

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 (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