motiro 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (309) hide show
  1. data/LICENSE +280 -0
  2. data/README +28 -0
  3. data/README.en +175 -0
  4. data/README.pt-br +175 -0
  5. data/Rakefile +10 -0
  6. data/app/controllers/account_controller.rb +62 -0
  7. data/app/controllers/application.rb +72 -0
  8. data/app/controllers/edition_controller.rb +13 -0
  9. data/app/controllers/javascript_controller.rb +21 -0
  10. data/app/controllers/report_controller.rb +79 -0
  11. data/app/controllers/root_controller.rb +7 -0
  12. data/app/controllers/wiki_controller.rb +102 -0
  13. data/app/core/cache_reporter.rb +53 -0
  14. data/app/core/cache_reporter_fetcher.rb +33 -0
  15. data/app/core/chief_editor.rb +69 -0
  16. data/app/core/reporter.rb +105 -0
  17. data/app/core/reporter_driver.rb +36 -0
  18. data/app/core/reporter_fetcher.rb +39 -0
  19. data/app/core/settings.rb +43 -0
  20. data/app/core/version.rb +1 -0
  21. data/app/core/wiki_page_not_found.rb +33 -0
  22. data/app/core/wiki_reporter.rb +83 -0
  23. data/app/helpers/account_helper.rb +2 -0
  24. data/app/helpers/application_helper.rb +68 -0
  25. data/app/helpers/default_page_provider.rb +28 -0
  26. data/app/helpers/report_helper.rb +2 -0
  27. data/app/helpers/root_helper.rb +2 -0
  28. data/app/helpers/wiki_helper.rb +3 -0
  29. data/app/models/change.rb +96 -0
  30. data/app/models/diff_table_builder.rb +285 -0
  31. data/app/models/event.rb +18 -0
  32. data/app/models/headline.rb +109 -0
  33. data/app/models/page.rb +114 -0
  34. data/app/models/page_sweeper.rb +26 -0
  35. data/app/models/user.rb +85 -0
  36. data/app/ports/chdir_runner.rb +36 -0
  37. data/app/ports/reporter_loader.rb +9 -0
  38. data/app/ports/runner.rb +64 -0
  39. data/app/reporters/darcs_connection.rb +54 -0
  40. data/app/reporters/darcs_reporter.rb +104 -0
  41. data/app/reporters/darcs_settings.rb +9 -0
  42. data/app/reporters/darcs_temp_repo.rb +40 -0
  43. data/app/reporters/events_reporter.rb +28 -0
  44. data/app/reporters/features_reporter.rb +24 -0
  45. data/app/reporters/subversion_reporter.rb +203 -0
  46. data/app/reporters/svn_connection.rb +62 -0
  47. data/app/reporters/svn_settings.rb +40 -0
  48. data/app/views/account/_authorization.rhtml +54 -0
  49. data/app/views/account/_availability.rhtml +5 -0
  50. data/app/views/account/availability.rhtml +1 -0
  51. data/app/views/account/login.rhtml +22 -0
  52. data/app/views/account/logout.rhtml +10 -0
  53. data/app/views/javascript/motiro.rjs +132 -0
  54. data/app/views/javascript/niftycube.rjs +300 -0
  55. data/app/views/layouts/_bottom.rhtml +5 -0
  56. data/app/views/layouts/_header.rhtml +7 -0
  57. data/app/views/layouts/_top.rhtml +25 -0
  58. data/app/views/layouts/application.rhtml +3 -0
  59. data/app/views/layouts/scaffold.rhtml +13 -0
  60. data/app/views/layouts/wiki_edit.rhtml +26 -0
  61. data/app/views/report/list.rhtml +32 -0
  62. data/app/views/report/older.rhtml +26 -0
  63. data/app/views/report/rss.rxml +29 -0
  64. data/app/views/report/show.rhtml +20 -0
  65. data/app/views/root/index.rhtml +33 -0
  66. data/app/views/wiki/_editbar.rhtml +26 -0
  67. data/app/views/wiki/_properties_edit.rhtml +5 -0
  68. data/app/views/wiki/_properties_show.rhtml +5 -0
  69. data/app/views/wiki/edit.rhtml +56 -0
  70. data/app/views/wiki/properties_edit.rhtml +1 -0
  71. data/app/views/wiki/show.rhtml +9 -0
  72. data/bin/motiro +44 -0
  73. data/config/boot.rb +19 -0
  74. data/config/database.yml +14 -0
  75. data/config/environment.rb +64 -0
  76. data/config/environments/development.rb +20 -0
  77. data/config/environments/production.rb +19 -0
  78. data/config/environments/test.rb +19 -0
  79. data/config/motiro.yml +43 -0
  80. data/config/routes.rb +79 -0
  81. data/db/migrate/005_globalize_migration.rb +11363 -0
  82. data/db/migrate/006_remove_headline_title.rb +14 -0
  83. data/db/migrate/007_stretch_rid.rb +11 -0
  84. data/db/migrate/008_add_page_editors.rb +12 -0
  85. data/db/migrate/009_add_page_original_author.rb +12 -0
  86. data/db/migrate/010_remove_empty_string_defaults_from_pages.rb +17 -0
  87. data/db/migrate/011_add_title_and_kind_to_pages.rb +13 -0
  88. data/db/migrate/012_page_modification_info.rb +13 -0
  89. data/db/migrate/013_nullify_initial_page_attributes.rb +21 -0
  90. data/db/migrate/014_events_are_wiki_pages.rb +13 -0
  91. data/db/migrate/015_migrate_previous_event_data.rb +23 -0
  92. data/db/migrate/1_initial_structure.rb +36 -0
  93. data/db/migrate/2_add_authentication.rb +12 -0
  94. data/db/migrate/3_drop_articles.rb +26 -0
  95. data/db/migrate/4_add_page_editing.rb +14 -0
  96. data/db/motirodb.sqlite.initial +0 -0
  97. data/db/schema_version +1 -0
  98. data/db/translation/pt-BR.rb +76 -0
  99. data/doc/README_FOR_APP +2 -0
  100. data/installer/rails_installer_defaults.yml +5 -0
  101. data/lib/import_translations.rb +154 -0
  102. data/lib/login_system.rb +89 -0
  103. data/lib/relative_time.rb +48 -0
  104. data/lib/string_extensions.rb +10 -0
  105. data/lib/stub_hash.rb +22 -0
  106. data/lib/tasks/packaging.rake +93 -0
  107. data/lib/tasks/testing.rake +32 -0
  108. data/lib/tick_daemon.rb +41 -0
  109. data/lib/translator.rb +67 -0
  110. data/lib/wiki_renderer.rb +42 -0
  111. data/lib/wiki_url_generator.rb +29 -0
  112. data/log/.keepdir +0 -0
  113. data/public/404.html +8 -0
  114. data/public/500.html +8 -0
  115. data/public/dispatch.cgi +10 -0
  116. data/public/dispatch.fcgi +24 -0
  117. data/public/dispatch.rb +10 -0
  118. data/public/favicon.ico +0 -0
  119. data/public/images/calendar.png +0 -0
  120. data/public/images/rss.png +0 -0
  121. data/public/javascripts/controls.js +750 -0
  122. data/public/javascripts/dragdrop.js +584 -0
  123. data/public/javascripts/effects.js +854 -0
  124. data/public/javascripts/prototype.js +1785 -0
  125. data/public/robots.txt +1 -0
  126. data/public/stylesheets/motiro.css +269 -0
  127. data/public/stylesheets/niftyCorners.css +35 -0
  128. data/public/stylesheets/scaffold.css +74 -0
  129. data/script/about +3 -0
  130. data/script/breakpointer +3 -0
  131. data/script/console +3 -0
  132. data/script/destroy +3 -0
  133. data/script/generate +3 -0
  134. data/script/performance/benchmarker +3 -0
  135. data/script/performance/profiler +3 -0
  136. data/script/plugin +3 -0
  137. data/script/process/reaper +3 -0
  138. data/script/process/spawner +3 -0
  139. data/script/process/spinner +3 -0
  140. data/script/runner +3 -0
  141. data/script/server +3 -0
  142. data/script/ticker +29 -0
  143. data/test/acceptance/account_test.rb +186 -0
  144. data/test/acceptance/darcs_test.rb +62 -0
  145. data/test/acceptance/events_test.rb +47 -0
  146. data/test/acceptance/main_page_test.rb +92 -0
  147. data/test/acceptance/subversion_test.rb +319 -0
  148. data/test/acceptance/ts_all_suites.rb +27 -0
  149. data/test/acceptance/wiki_test.rb +202 -0
  150. data/test/contract/darcs_test.rb +51 -0
  151. data/test/contract/remote_darcs_test.rb +61 -0
  152. data/test/contract/svn_test.rb +53 -0
  153. data/test/fixtures/changes.yml +25 -0
  154. data/test/fixtures/headlines.yml +45 -0
  155. data/test/fixtures/pages.yml +98 -0
  156. data/test/fixtures/users.yml +31 -0
  157. data/test/functional/account_controller_test.rb +96 -0
  158. data/test/functional/report_controller_test.rb +113 -0
  159. data/test/functional/report_features_test.rb +38 -0
  160. data/test/functional/report_subversion_test.rb +79 -0
  161. data/test/functional/root_controller_test.rb +127 -0
  162. data/test/functional/wiki_controller_test.rb +280 -0
  163. data/test/lib/acceptance_test_case.rb +43 -0
  164. data/test/lib/configuration.rb +53 -0
  165. data/test/lib/darcs_excerpts.rb +181 -0
  166. data/test/lib/darcs_repo.rb +77 -0
  167. data/test/lib/live_mode_test.rb +51 -0
  168. data/test/lib/local_svn.rb +157 -0
  169. data/test/lib/netutils.rb +42 -0
  170. data/test/lib/platform_thread.rb +62 -0
  171. data/test/lib/repoutils.rb +23 -0
  172. data/test/lib/selenium_extensions.rb +32 -0
  173. data/test/lib/stubio.rb +37 -0
  174. data/test/lib/svn_excerpts.rb +288 -0
  175. data/test/lib/test_configuration.rb +14 -0
  176. data/test/lib/webserver.rb +71 -0
  177. data/test/meta/configuration_test.rb +72 -0
  178. data/test/meta/darcs_repo_test.rb +118 -0
  179. data/test/meta/local_svn_test.rb +125 -0
  180. data/test/meta/platform_thread_test.rb +46 -0
  181. data/test/meta/stubio_test.rb +44 -0
  182. data/test/mocks/headline.rb +34 -0
  183. data/test/mocks/svn_reporter.rb +29 -0
  184. data/test/stubs/svn_settings.rb +19 -0
  185. data/test/stubs/url_generator.rb +24 -0
  186. data/test/test_helper.rb +36 -0
  187. data/test/unit/cache_reporter_fetcher_test.rb +46 -0
  188. data/test/unit/cache_reporter_test.rb +97 -0
  189. data/test/unit/caching_test.rb +78 -0
  190. data/test/unit/change_test.rb +152 -0
  191. data/test/unit/chdir_runner_test.rb +77 -0
  192. data/test/unit/chief_editor_test.rb +234 -0
  193. data/test/unit/darcs_connection_test.rb +109 -0
  194. data/test/unit/darcs_reporter_test.rb +146 -0
  195. data/test/unit/darcs_settings_test.rb +37 -0
  196. data/test/unit/darcs_temp_repo_test.rb +51 -0
  197. data/test/unit/default_page_provider_test.rb +46 -0
  198. data/test/unit/diff_table_builder_test.rb +602 -0
  199. data/test/unit/headline_test.rb +259 -0
  200. data/test/unit/page_test.rb +145 -0
  201. data/test/unit/relative_time_test.rb +56 -0
  202. data/test/unit/reporter_driver_test.rb +85 -0
  203. data/test/unit/reporter_fetcher_test.rb +31 -0
  204. data/test/unit/reporter_test.rb +81 -0
  205. data/test/unit/runner_test.rb +93 -0
  206. data/test/unit/settings_test.rb +55 -0
  207. data/test/unit/string_extensions_test.rb +10 -0
  208. data/test/unit/svn_connection_test.rb +183 -0
  209. data/test/unit/svn_reporter_interaction_test.rb +38 -0
  210. data/test/unit/svn_reporter_test.rb +286 -0
  211. data/test/unit/svn_settings_test.rb +86 -0
  212. data/test/unit/translator_test.rb +96 -0
  213. data/test/unit/user_test.rb +125 -0
  214. data/test/unit/wiki_renderer_test.rb +87 -0
  215. data/test/unit/wiki_reporter_test.rb +94 -0
  216. data/test/unit/wiki_url_generator_test.rb +31 -0
  217. data/vendor/motiro-installer.rb +159 -0
  218. data/vendor/plugins/globalize/LICENSE +9 -0
  219. data/vendor/plugins/globalize/README +49 -0
  220. data/vendor/plugins/globalize/data/country_data.csv +240 -0
  221. data/vendor/plugins/globalize/data/language_data.csv +188 -0
  222. data/vendor/plugins/globalize/data/translation_data.csv +3421 -0
  223. data/vendor/plugins/globalize/generators/globalize/USAGE +10 -0
  224. data/vendor/plugins/globalize/generators/globalize/globalize_generator.rb +42 -0
  225. data/vendor/plugins/globalize/generators/globalize/templates/migration.rb.gz +0 -0
  226. data/vendor/plugins/globalize/generators/globalize/templates/tiny_migration.rb.gz +0 -0
  227. data/vendor/plugins/globalize/init.rb +30 -0
  228. data/vendor/plugins/globalize/lib/globalize/localization/core_ext.rb +170 -0
  229. data/vendor/plugins/globalize/lib/globalize/localization/core_ext_hooks.rb +33 -0
  230. data/vendor/plugins/globalize/lib/globalize/localization/db_translate.rb +494 -0
  231. data/vendor/plugins/globalize/lib/globalize/localization/db_view_translator.rb +152 -0
  232. data/vendor/plugins/globalize/lib/globalize/localization/locale.rb +173 -0
  233. data/vendor/plugins/globalize/lib/globalize/localization/rfc_3066.rb +40 -0
  234. data/vendor/plugins/globalize/lib/globalize/models/country.rb +24 -0
  235. data/vendor/plugins/globalize/lib/globalize/models/currency.rb +188 -0
  236. data/vendor/plugins/globalize/lib/globalize/models/language.rb +84 -0
  237. data/vendor/plugins/globalize/lib/globalize/models/model_translation.rb +4 -0
  238. data/vendor/plugins/globalize/lib/globalize/models/translation.rb +9 -0
  239. data/vendor/plugins/globalize/lib/globalize/models/view_translation.rb +14 -0
  240. data/vendor/plugins/globalize/lib/globalize/rails/action_mailer.rb +125 -0
  241. data/vendor/plugins/globalize/lib/globalize/rails/action_view.rb +79 -0
  242. data/vendor/plugins/globalize/lib/globalize/rails/active_record.rb +129 -0
  243. data/vendor/plugins/globalize/lib/globalize/rails/active_record_helper.rb +33 -0
  244. data/vendor/plugins/globalize/populators/pop_dates.rb +81 -0
  245. data/vendor/plugins/globalize/populators/pop_migration.rb +18 -0
  246. data/vendor/plugins/globalize/populators/pop_pluralization.rb +26 -0
  247. data/vendor/plugins/globalize/populators/pop_seps.rb +32 -0
  248. data/vendor/plugins/globalize/tasks/data.rake +130 -0
  249. data/vendor/plugins/globalize/test/action_mailer_test/globalize_mailer/test.en-US.plain.text.rhtml +1 -0
  250. data/vendor/plugins/globalize/test/action_mailer_test/globalize_mailer/test.en.plain.text.rhtml +1 -0
  251. data/vendor/plugins/globalize/test/action_mailer_test/globalize_mailer/test.he.plain.text.rhtml +1 -0
  252. data/vendor/plugins/globalize/test/action_mailer_test/globalize_mailer/test.plain.text.rhtml +1 -0
  253. data/vendor/plugins/globalize/test/action_mailer_test.rb +54 -0
  254. data/vendor/plugins/globalize/test/config/database.yml.default +16 -0
  255. data/vendor/plugins/globalize/test/config/database.yml.example +22 -0
  256. data/vendor/plugins/globalize/test/core_ext_test.rb +61 -0
  257. data/vendor/plugins/globalize/test/currency_test.rb +141 -0
  258. data/vendor/plugins/globalize/test/date_helper_test.rb +634 -0
  259. data/vendor/plugins/globalize/test/db/schema.rb +90 -0
  260. data/vendor/plugins/globalize/test/db_translation_test.rb +374 -0
  261. data/vendor/plugins/globalize/test/fixtures/globalize_categories.yml +7 -0
  262. data/vendor/plugins/globalize/test/fixtures/globalize_categories_products.yml +7 -0
  263. data/vendor/plugins/globalize/test/fixtures/globalize_countries.yml +41 -0
  264. data/vendor/plugins/globalize/test/fixtures/globalize_languages.yml +64 -0
  265. data/vendor/plugins/globalize/test/fixtures/globalize_manufacturers.yml +5 -0
  266. data/vendor/plugins/globalize/test/fixtures/globalize_products.yml +29 -0
  267. data/vendor/plugins/globalize/test/fixtures/globalize_simples.yml +5 -0
  268. data/vendor/plugins/globalize/test/fixtures/globalize_translations.yml +354 -0
  269. data/vendor/plugins/globalize/test/locale_test.rb +27 -0
  270. data/vendor/plugins/globalize/test/mime_responds_test.rb +358 -0
  271. data/vendor/plugins/globalize/test/model_test.rb +17 -0
  272. data/vendor/plugins/globalize/test/standard_data_test_helper.rb +33 -0
  273. data/vendor/plugins/globalize/test/test_helper.rb +19 -0
  274. data/vendor/plugins/globalize/test/test_standard_data.rb +54 -0
  275. data/vendor/plugins/globalize/test/validation_test.rb +29 -0
  276. data/vendor/plugins/globalize/test/view_picking_test.rb +49 -0
  277. data/vendor/plugins/globalize/test/view_translation_test.rb +237 -0
  278. data/vendor/plugins/globalize/test/views/layouts/standard.rhtml +1 -0
  279. data/vendor/plugins/globalize/test/views/respond_to/all_types_with_layout.rhtml +1 -0
  280. data/vendor/plugins/globalize/test/views/respond_to/all_types_with_layout.rjs +1 -0
  281. data/vendor/plugins/globalize/test/views/respond_to/using_defaults.en.rhtml +1 -0
  282. data/vendor/plugins/globalize/test/views/respond_to/using_defaults.en.rjs +1 -0
  283. data/vendor/plugins/globalize/test/views/respond_to/using_defaults.en.rxml +1 -0
  284. data/vendor/plugins/globalize/test/views/respond_to/using_defaults.fr.rhtml +1 -0
  285. data/vendor/plugins/globalize/test/views/respond_to/using_defaults.fr.rjs +1 -0
  286. data/vendor/plugins/globalize/test/views/respond_to/using_defaults.fr.rxml +1 -0
  287. data/vendor/plugins/globalize/test/views/respond_to/using_defaults.rhtml +1 -0
  288. data/vendor/plugins/globalize/test/views/respond_to/using_defaults.rjs +1 -0
  289. data/vendor/plugins/globalize/test/views/respond_to/using_defaults.rxml +1 -0
  290. data/vendor/plugins/globalize/test/views/respond_to/using_defaults_with_type_list.en.rhtml +1 -0
  291. data/vendor/plugins/globalize/test/views/respond_to/using_defaults_with_type_list.en.rjs +1 -0
  292. data/vendor/plugins/globalize/test/views/respond_to/using_defaults_with_type_list.en.rxml +1 -0
  293. data/vendor/plugins/globalize/test/views/respond_to/using_defaults_with_type_list.fr.rhtml +1 -0
  294. data/vendor/plugins/globalize/test/views/respond_to/using_defaults_with_type_list.fr.rjs +1 -0
  295. data/vendor/plugins/globalize/test/views/respond_to/using_defaults_with_type_list.fr.rxml +1 -0
  296. data/vendor/plugins/globalize/test/views/respond_to/using_defaults_with_type_list.rhtml +1 -0
  297. data/vendor/plugins/globalize/test/views/respond_to/using_defaults_with_type_list.rjs +1 -0
  298. data/vendor/plugins/globalize/test/views/respond_to/using_defaults_with_type_list.rxml +1 -0
  299. data/vendor/plugins/globalize/test/views/test.he-IL.rhtml +1 -0
  300. data/vendor/plugins/globalize/test/views/test.rhtml +1 -0
  301. data/vendor/plugins/globalize/test/views/test2.he.rhtml +1 -0
  302. data/vendor/plugins/globalize/test/views/test2.rhtml +1 -0
  303. data/vendor/plugins/test_xml/MIT-LICENSE +20 -0
  304. data/vendor/plugins/test_xml/README +20 -0
  305. data/vendor/plugins/test_xml/Rakefile +22 -0
  306. data/vendor/plugins/test_xml/init.rb +7 -0
  307. data/vendor/plugins/test_xml/lib/xml_assertions.rb +22 -0
  308. data/vendor/plugins/test_xml/test/test_xml_test.rb +40 -0
  309. metadata +505 -0
@@ -0,0 +1,173 @@
1
+ module Globalize
2
+
3
+ class NoBaseLanguageError < StandardError; end
4
+
5
+ =begin rdoc
6
+ Locale defines the currenctly active _locale_. You'll mostly use it like this:
7
+ Locale.set("en-US")
8
+
9
+ +en+ is the code for English, and +US+ is the country code. The country code is
10
+ optional, but you'll need to define it to get a lot of the localization features.
11
+ =end
12
+ class Locale
13
+ attr_reader :language, :country, :code
14
+ attr_accessor :date_format, :currency_format, :currency_code,
15
+ :thousands_sep, :decimal_sep, :currency_decimal_sep,
16
+ :number_grouping_scheme
17
+
18
+ @@cache = {}
19
+ @@translator_class = DbViewTranslator
20
+ @@translator = {}
21
+ @@active = nil
22
+ @@base_language = nil
23
+ @@base_language_code = nil
24
+ @@translator = @@translator_class.instance
25
+
26
+ # Is there an active locale?
27
+ def self.active?; !@@active.nil? end
28
+
29
+ # This is the focal point of the class. Sets the locale in the familiar
30
+ # RFC 3066 format (see: http://www.faqs.org/rfcs/rfc3066.html). It can
31
+ # also take a Locale object. Set it to the +nil+ object, to deactivate
32
+ # the locale.
33
+ def self.set(locale)
34
+ if locale.kind_of? Locale
35
+ @@active = locale
36
+ elsif locale.nil?
37
+ @@active = nil
38
+ else
39
+ @@active = ( @@cache[locale] ||= Locale.new(locale) )
40
+ end
41
+ end
42
+
43
+ # Clears the locale cache -- used mostly for testing.
44
+ def self.clear_cache
45
+ @@cache.clear
46
+ end
47
+
48
+ # Returns the active locale.
49
+ def self.active; @@active end
50
+
51
+ # Sets the base language. The base language is the language that has
52
+ # complete coverage in the database. For instance, if you have a +Category+
53
+ # model with a +name+ field, the base language is the language in which names
54
+ # are stored in the model itself, and not in the translations table.
55
+ #
56
+ # Takes either a language code (valid RFC 3066 code like +en+ or <tt>en-US</tt>)
57
+ # or a language object.
58
+ #
59
+ # May be set with a language code in environment.rb, without accessing the db.
60
+ def self.set_base_language(lang)
61
+ if lang.kind_of? Language
62
+ @@base_language = lang
63
+ else
64
+ @@base_language_code = RFC_3066.parse lang
65
+ end
66
+ end
67
+
68
+ # Returns the base language. Raises an exception if none is set.
69
+ def self.base_language
70
+ @@base_language ? @@base_language :
71
+ (@@base_language_code ?
72
+ (@@base_language = Language.pick(@@base_language_code)) :
73
+ raise(NoBaseLanguageError, "base language must be defined"))
74
+ end
75
+
76
+ # Is the currently active language the base language?
77
+ def self.base?
78
+ active ? active.language == base_language : true
79
+ end
80
+
81
+ # Returns the currently active language model or +nil+.
82
+ def self.language
83
+ active? ? active.language : nil
84
+ end
85
+
86
+ # Returns the currently active language code or +nil+.
87
+ def self.language_code
88
+ active? ? language.code : nil
89
+ end
90
+
91
+ # Returns the currently active country model or +nil+.
92
+ def self.country
93
+ active? ? active.country : nil
94
+ end
95
+
96
+ # Creates a new locale object by looking up an RFC 3066 code in the database.
97
+ def initialize(code)
98
+ if code.nil?
99
+ return
100
+ end
101
+
102
+ rfc = RFC_3066.parse(code)
103
+ @code = rfc.locale
104
+
105
+ @language = Language.pick(rfc)
106
+ @country = Country.pick(rfc)
107
+
108
+ setup_fields
109
+ end
110
+
111
+ # Sets the translation for +key+.
112
+ #
113
+ # :call-seq:
114
+ # Locale.set_translation(key, language, *translations)
115
+ # Locale.set_translation(key, *translations)
116
+ #
117
+ # If +language+ is given, define a translation using that language
118
+ # model, otherwise use the active language.
119
+ #
120
+ # Multiple translation strings may be given, in order to define plural forms.
121
+ # In English, there are only two plural forms, singular and plural, so you
122
+ # would provide two strings at the most. The order is determined by the
123
+ # formula in the languages database. For English, the order is: singular form,
124
+ # then plural.
125
+ #
126
+ # Example:
127
+ # Locale.set_translation("There are %d items in your cart",
128
+ # "There is one item in your cart", "There are %d items in your cart")
129
+ def self.set_translation(key, *options)
130
+ key = key.to_s.gsub('_', ' ') if key.kind_of? Symbol
131
+ if options.first.kind_of? Language
132
+ language = options.shift
133
+ else
134
+ language = self.language
135
+ end
136
+
137
+ raise ArgumentError, "No translations given" if options.empty?
138
+ translator.set(key, language, *options)
139
+ end
140
+
141
+ def self.set_pluralized_translation(key, *options)
142
+ key = key.to_s.gsub('_', ' ') if key.kind_of? Symbol
143
+ if options.first.kind_of? Language
144
+ language = options.shift
145
+ else
146
+ language = self.language
147
+ end
148
+
149
+ raise ArgumentError, "No translations given" if options.empty?
150
+ translator.set_pluralized(key, language, *options)
151
+ end
152
+
153
+ def self.translate(key, default = nil, arg = nil) # :nodoc:
154
+ key = key.to_s.gsub('_', ' ') if key.kind_of? Symbol
155
+
156
+ translator.fetch(key, self.language, default, arg)
157
+ end
158
+
159
+ # Returns the translator object -- mostly for testing and adjusting the cache.
160
+ def self.translator; @@translator end
161
+
162
+ private
163
+
164
+ def setup_fields
165
+ return if !@country
166
+
167
+ [:date_format, :currency_format, :currency_code, :thousands_sep,
168
+ :decimal_sep, :currency_decimal_sep, :number_grouping_scheme
169
+ ].each {|f| instance_variable_set "@#{f}", @country.send(f) }
170
+ end
171
+ end
172
+ end
173
+
@@ -0,0 +1,40 @@
1
+ module Globalize
2
+ class RFC_3066 # :nodoc:
3
+ attr_accessor :country, :language, :locale
4
+ def self.parse(locale)
5
+ # check for validity
6
+ raise ArgumentError, "bad format for #{locale}, not rfc-3066 compliant" if
7
+ locale !~ /^[a-zA-Z0-9\-]+$/
8
+
9
+ # split/assign
10
+ lang, country, remain = locale.split('-', 3)
11
+ raise ArgumentError, "bad format for #{locale}, not rfc-3066 compliant" if
12
+ lang !~ /^[a-zA-Z]{1,8}$/
13
+ new_language = lang if lang.size == 2 || lang.size == 3
14
+ raise ArgumentError, "bad format for #{locale}, not rfc-3066 compliant" if
15
+ !new_language && lang != 'i' && lang != 'x'
16
+
17
+ raise ArgumentError, "bad format for #{locale}, not rfc-3066 compliant" if
18
+ country && (country.size < 2 || country.size > 8)
19
+ new_country = country if country && country.size == 2
20
+
21
+ rfc = self.new
22
+ rfc.country = new_country
23
+ rfc.language = new_language
24
+ rfc.locale = locale
25
+
26
+ return rfc
27
+ end
28
+
29
+ def self.valid?(locale)
30
+ begin
31
+ parse(locale)
32
+ rescue ArgumentError
33
+ return false
34
+ else
35
+ return true
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ module Globalize
2
+ class Country < ActiveRecord::Base # :nodoc:
3
+ set_table_name "globalize_countries"
4
+
5
+ def self.reloadable?; false end
6
+
7
+ def self.pick(rfc)
8
+
9
+ # assume it's a country code string
10
+ if rfc.kind_of?(String)
11
+ find_by_code(rfc)
12
+ elsif rfc.kind_of?(RFC_3066)
13
+ rfc.country ? find_by_code(rfc.country) : nil
14
+ else
15
+ raise ArgumentError, "argument must be String or RFC_3066 object"
16
+ end
17
+ end
18
+
19
+ def number_grouping_scheme
20
+ attr = read_attribute(:number_grouping_scheme)
21
+ attr ? attr.intern : nil
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,188 @@
1
+ module Globalize
2
+
3
+ =begin rdoc
4
+ This is what you use for representing money in your ActiveRecord models.
5
+ It stores values as integers internally and in the database, to safeguard
6
+ precision and rounding.
7
+
8
+ More importantly for globalization freaks, it prints out the amount correctly
9
+ in the current locale, via the handy #format method. Try it!
10
+
11
+ Example:
12
+
13
+ class Product < ActiveRecord::Base
14
+ composed_of :price, :class_name => "Globalize::Currency",
15
+ :mapping => [ %w(price cents) ]
16
+ end
17
+
18
+ product.price -> "SFr. 483'232.43"
19
+ =end
20
+ class Currency
21
+ include Comparable
22
+
23
+ attr_reader :cents
24
+
25
+ class CurrencyError < StandardError# :nodoc:
26
+ end
27
+
28
+ # Creates a new Currency object, given cents. This is used to tie into
29
+ # the ActiveRecord aggregation stuff.
30
+ def initialize(cents)
31
+ @cents = cents.nil? ? nil : cents.to_i
32
+ end
33
+
34
+ @@no_cents = false
35
+ def self.no_cents; @@no_cents end
36
+ def self.no_cents=(val); @@no_cents = val end
37
+
38
+ @@free = Currency.new(0)
39
+ @@free.freeze
40
+
41
+ # Returns the Currency object representing a price of 0.
42
+ def self.free
43
+ @@free
44
+ end
45
+
46
+ @@na = Currency.new(nil)
47
+ @@na.freeze
48
+
49
+ # Returns the Currency object representing an unknown price.
50
+ def self.na
51
+ @@na
52
+ end
53
+
54
+ def <=>(other)
55
+ if other.respond_to? :cents
56
+ if na?
57
+ other.na? ? 0 : 1
58
+ else
59
+ other.na? ? -1 : cents <=> other.cents
60
+ end
61
+ elsif other.kind_of? Integer
62
+ na? ? 1 : cents <=> other
63
+ else
64
+ raise "can only compare with money or integer"
65
+ end
66
+ end
67
+
68
+ def +(other_money)
69
+ raise TypeError, "parameter to Currency#+ must be Currency object" unless
70
+ other_money.kind_of? Currency
71
+ (na? || other_money.na?) ? Currency.na :
72
+ Currency.new(cents + other_money.cents)
73
+ end
74
+
75
+ def -(other_money)
76
+ raise TypeError, "parameter to Currency#+ must be Currency object" unless
77
+ other_money.kind_of? Currency
78
+ (na? || other_money.na?) ? Currency.na :
79
+ Currency.new(cents - other_money.cents)
80
+ end
81
+
82
+ # Multiply money by amount
83
+ def *(amount)
84
+ return Currency.new(nil) if na?
85
+ new_cents = amount * cents;
86
+ new_cents = new_cents.round if new_cents.respond_to? :round
87
+ Currency.new(new_cents)
88
+ end
89
+
90
+ # Divide money by amount
91
+ def /(amount)
92
+ return Currency.new(nil) if na?
93
+ new_cents = cents / amount;
94
+ new_cents = new_cents.round if new_cents.respond_to? :round
95
+ Currency.new(new_cents)
96
+ end
97
+
98
+ # Returns the formatted version of the amount in local currency.
99
+ # If <tt>:code => true</tt> is specified, format using international
100
+ # 3-letter currency code. If <tt>:country</tt> is specified as well,
101
+ # use that country's currency code for formatting.
102
+ def format(options = {})
103
+ return :no_price_available.t("call for price") if na?
104
+
105
+ force_cents = options[:force_cents]
106
+ if options[:code]
107
+ currency_code = options[:country] ?
108
+ options[:country].currency_code :
109
+ ( Locale.active? ? Locale.active.currency_code : nil )
110
+ currency_code ?
111
+ self.amount(false, force_cents) + " " + currency_code :
112
+ self.amount(false, force_cents)
113
+ else
114
+ if Locale.active?
115
+ fmt = Locale.active.currency_format || '$%n'
116
+ fmt.sub('%n', self.amount(false, force_cents))
117
+ else
118
+ self.amount false, force_cents
119
+ end
120
+ end
121
+ end
122
+
123
+ # Returns the formatted version of the amount, but without the currency symbol.
124
+ # If +unlocalized+ is true, do not format the number according to the current locale,
125
+ # so <tt>Currency.new(1234567) -> 12345.67</tt>. This is useful for sending the
126
+ # amount to payment gateways.
127
+ def amount(unlocalized = false, force_cents = false)
128
+ return nil if na?
129
+ decimal_sep = unlocalized ?
130
+ '.' :
131
+ (Locale.active? ?
132
+ (Locale.active.currency_decimal_sep || Locale.active.decimal_sep || '.') :
133
+ '.')
134
+ dollar_str = unlocalized ? dollar_part.to_s : dollar_part.localize
135
+ result = dollar_str
136
+ result << decimal_sep + sprintf("%02d", cent_part) unless
137
+ self.class.no_cents && !force_cents
138
+ return result
139
+ end
140
+
141
+ # Same as #format with no arguments.
142
+ def to_s
143
+ self.format
144
+ end
145
+
146
+ # Parse a string or number into a currency object. Easier to use than #new.
147
+ def self.parse(num)
148
+ case
149
+ when num.is_a?(String)
150
+ raise ArgumentError, "Not an amount (#{num})" if num.delete("^0-9").empty?
151
+ _dollars, _cents = num.delete("^0-9.").split('.', 2)
152
+ _cents = _cents ? _cents[0,2] : 0
153
+ Currency.new(_dollars.to_i * 100 + _cents.to_i)
154
+ when num.is_a?(Numeric)
155
+ Currency.new(num * 100)
156
+ when num.is_a?(NilClass)
157
+ Currency.na
158
+ else
159
+ raise ArgumentError, "Unrecognized object #{num.class.name} for Currency"
160
+ end
161
+ end
162
+
163
+ # Conversion to self
164
+ def to_currency
165
+ self
166
+ end
167
+
168
+ # Is the value 0? Is it free?
169
+ def empty?
170
+ cents == 0
171
+ end
172
+
173
+ # Is the value unknown?
174
+ def na?
175
+ cents.nil?
176
+ end
177
+
178
+ private
179
+ def dollar_part
180
+ na? ? nil : cents / 100
181
+ end
182
+
183
+ def cent_part
184
+ na? ? nil : cents % 100
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,84 @@
1
+ module Globalize
2
+ class Language < ActiveRecord::Base # :nodoc:
3
+ set_table_name "globalize_languages"
4
+
5
+ validates_presence_of :english_name
6
+
7
+ validates_uniqueness_of :iso_639_1, :if => :iso_639_1
8
+ validates_uniqueness_of :iso_639_2, :if => :iso_639_2
9
+ validates_uniqueness_of :iso_639_3, :if => :iso_639_3
10
+ validates_uniqueness_of :rfc_3066, :if => :rfc_3066
11
+
12
+ validates_length_of :pluralization, :maximum => 200, :if => :pluralization
13
+ validates_format_of :pluralization, :with => /^[c=\d?:%!<>&|() ]+$/, :if => :pluralization,
14
+ :message => " has invalid characters. Allowed characters are: " +
15
+ "'c', '=', 0-9, '?', ':', '%', '!', '<', '>', '&', '|', '(', ')', ' '."
16
+
17
+ def self.reloadable?; false end
18
+
19
+ def after_initialize
20
+ if !pluralization.nil? && pluralization.size > 200
21
+ raise SecurityError, "Pluralization field for #{self.english_name} language " +
22
+ "contains potentially harmful code. " +
23
+ "Must be less than 200 characters in length. Was #{pluralization.size} characters."
24
+ end
25
+
26
+ if !pluralization.nil? && pluralization !~ /^[c=\d?:%!<>&|() ]+$/
27
+ raise SecurityError, "Pluralization field ('#{pluralization}') for #{self.english_name} language " +
28
+ "contains potentially harmful code. " +
29
+ "Must only use the characters: 'c', '=', 0-9, '?', ':', " +
30
+ "'%', '!', '<', '>', '&', '|', '(', ')', ' '."
31
+ end
32
+ end
33
+
34
+ def self.pick(rfc)
35
+ if rfc.kind_of? String then rfc = RFC_3066.parse(rfc) end
36
+ if rfc.locale.include? '-'
37
+ lang = find_by_rfc_3066(rfc.locale)
38
+ return lang if lang
39
+ end
40
+
41
+ code = rfc.language
42
+ if code.size == 2
43
+ lang = find_by_iso_639_1(code)
44
+ elsif code.size == 3
45
+ lang = find_by_iso_639_3(code)
46
+ end
47
+
48
+ lang
49
+ end
50
+
51
+ def code; iso_639_1 || iso_639_3 || rfc_3066; end
52
+
53
+ def code=(new_code)
54
+ if new_code =~ /-/
55
+ self.rfc_3066 = new_code
56
+ else
57
+ raise ArgumentError,
58
+ "code must be in rfc_3066 format, with a hyphen character; was #{new_code}"
59
+ end
60
+ end
61
+
62
+ def native_name; self['native_name'] || self['english_name'] end
63
+
64
+ def ==(other)
65
+ return false if !other.kind_of? Language
66
+ self.code == other.code
67
+ end
68
+
69
+ def plural_index(num)
70
+
71
+ # number is not defined, so we assume no pluralization
72
+ return 1 if num.nil?
73
+
74
+ c = num
75
+ expr = pluralization || 'c == 1 ? 2 : 1'
76
+
77
+ instance_eval(expr)
78
+ end
79
+
80
+ def to_s; english_name end
81
+ def inspect; english_name end
82
+
83
+ end
84
+ end
@@ -0,0 +1,4 @@
1
+ module Globalize
2
+ class ModelTranslation < Translation # :nodoc:
3
+ end
4
+ end
@@ -0,0 +1,9 @@
1
+ module Globalize # :nodoc:
2
+ class Translation < ActiveRecord::Base # :nodoc:
3
+ set_table_name "globalize_translations"
4
+
5
+ def self.reloadable?; false end
6
+
7
+ belongs_to :language
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ module Globalize
2
+ # Represents a view translation in the DB, which is a translation of a string
3
+ # in the rails application itself. This includes error messages, controllers,
4
+ # views, and flashes, but not database content.
5
+ class ViewTranslation < Translation # :nodoc:
6
+
7
+ def self.pick(key, language, idx)
8
+ find(:first, :conditions => [
9
+ 'tr_key = ? AND language_id = ? AND pluralization_index = ?',
10
+ key, language.id, idx ])
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,125 @@
1
+ # Search for translated templates or fall back to the default one
2
+ module ActionMailer # :nodoc:
3
+
4
+ # Globalize overrides the create! method to support multiple templates
5
+ # for different locales. For example, for English it will select the template:
6
+ # signup_notification.en-US.text.html.rhtml
7
+ #
8
+ # It will look for the currently active locale code (en-US) first,
9
+ # then the language code (en).
10
+ #
11
+ # If neither of those are found, it will use the regular name:
12
+ # signup_notification.text.html.rhtml
13
+ #
14
+ # It is fully backwards compatible with the original Rails version.
15
+ class Base
16
+
17
+ # Initialize the mailer via the given +method_name+. The body will be
18
+ # rendered and a new TMail::Mail object created.
19
+ #
20
+ # This method is overriden by Globalize to support multiple templates
21
+ # for different locales. For example, use:
22
+ # signup_notification.en-US.text.html.rhtml
23
+ # It is fully backwards compatible with the original rails version.
24
+ def create!(method_name, *parameters) #:nodoc:
25
+ initialize_defaults(method_name)
26
+ send(method_name, *parameters)
27
+
28
+ # If an explicit, textual body has not been set, we check assumptions.
29
+ unless String === @body
30
+ # First, we look to see if there are any likely templates that match,
31
+ # which include the content-type in their file name (i.e.,
32
+ # "the_template_file.text.html.rhtml", etc.). Only do this if parts
33
+ # have not already been specified manually.
34
+ if @parts.empty?
35
+ append_localized_parts
36
+ unless @parts.empty?
37
+ @content_type = "multipart/alternative"
38
+ @charset = nil
39
+ @parts = sort_parts(@parts, @implicit_parts_order)
40
+ end
41
+ end
42
+
43
+ # Then, if there were such templates, we check to see if we ought to
44
+ # also render a "normal" template (without the content type). If a
45
+ # normal template exists (or if there were no implicit parts) we render
46
+ # it.
47
+ render_localized_normal_template
48
+
49
+ # Finally, if there are other message parts and a textual body exists,
50
+ # we shift it onto the front of the parts and set the body to nil (so
51
+ # that create_mail doesn't try to render it in addition to the parts).
52
+ if !@parts.empty? && String === @body
53
+ @parts.unshift Part.new(:charset => charset, :body => @body)
54
+ @body = nil
55
+ end
56
+ end
57
+
58
+ # If this is a multipart e-mail add the mime_version if it is not
59
+ # already set.
60
+ @mime_version ||= "1.0" if !@parts.empty?
61
+
62
+ # build the mail object itself
63
+ @mail = create_mail
64
+
65
+ end
66
+
67
+ private
68
+ def append_localized_parts
69
+ codes = locale_codes
70
+ codes.each do |code|
71
+ if code
72
+ templates = Dir.glob("#{template_path}/#{@template}.#{code}.*")
73
+ else
74
+ templates = Dir.glob("#{template_path}/#{@template}.*")
75
+ end
76
+ templates.each do |path|
77
+ sections = File.basename(path).split(".")[0..-2] || []
78
+
79
+ # skip if this is some other language
80
+ next if !code && Globalize::RFC_3066.valid?(sections[1])
81
+
82
+ # skip either template name and locale, or just template name
83
+ type_sections = code ? sections[2..-1] : sections[1..-1]
84
+ type = type_sections.join("/")
85
+
86
+ next if type.empty?
87
+ @parts << Part.new(:content_type => type,
88
+ :disposition => "inline", :charset => charset,
89
+ :body => render_message(sections.join('.'), @body))
90
+ end
91
+
92
+
93
+ # if we found templates at this stage, no need to continue to defaults
94
+ break if !templates.empty?
95
+ end
96
+ end
97
+
98
+ def render_localized_normal_template
99
+ template_exists = @parts.empty?
100
+ codes = locale_codes
101
+ codes.each do |code|
102
+ localized_name = @template
103
+ if !template_exists
104
+ if code
105
+ localized_name = [ @template, code ].join(".")
106
+ template_exists ||=
107
+ Dir.glob("#{template_path}/#{localized_name}.*").any? { |i| i.split(".").length == 3 }
108
+ else
109
+ template_exists ||=
110
+ Dir.glob("#{template_path}/#{@template}.*").any? { |i| i.split(".").length == 2 }
111
+ end
112
+ end
113
+ @body = render_message(localized_name, @body) if template_exists
114
+ break if template_exists
115
+ end
116
+ end
117
+
118
+ def locale_codes
119
+ loc = Globalize::Locale.active
120
+ lang = Globalize::Locale.language
121
+ codes = [ loc, lang ].compact.map {|o| o.code }.uniq
122
+ codes << nil # look for default path, with no localization
123
+ end
124
+ end
125
+ end