motiro 0.6.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 (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