fullstack-admin 0.1.20 → 0.1.23

Sign up to get free protection for your applications and to get access to all the features.
data/TODO.md ADDED
@@ -0,0 +1,38 @@
1
+ # Fullstack Admin Roadmap
2
+
3
+ * Multiple choice input with chosen
4
+
5
+ * Image inputs + detect smaller image size for thumbnails
6
+
7
+ * Inference of input type from model
8
+ eg.
9
+ field :text, :text, :markup => true
10
+ ->
11
+ input :text, :as => :markup
12
+
13
+ or
14
+ has_attached :photo, :image => true
15
+ ->
16
+ input :photo, :as => :image
17
+
18
+ ---
19
+
20
+ ## Long run goals
21
+
22
+ * Use only twitter bootstrap (drop jquery-ui)
23
+ - switch to bootstrap datepicker
24
+ - find alternative to sortable
25
+
26
+ * Multiple scopes
27
+ * Tags input with chosen
28
+ * Optional tracking of author/updaters for every model
29
+
30
+ ---
31
+
32
+ ## Completed
33
+
34
+ * Better rendering for boolean input
35
+
36
+ * Nested forms for Has Many associations
37
+ - Sorting
38
+ - Autogenerate from has_many
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.20
1
+ 0.1.23
Binary file
@@ -14,3 +14,13 @@ $(document).ready ->
14
14
  else
15
15
  links.show "fast"
16
16
  button.addClass "showing"
17
+
18
+ fixHelper = (e, ui) ->
19
+ ui.children().each ->
20
+ self = $ @
21
+ self.width self.width()
22
+ ui
23
+
24
+ $("tbody.sortable").sortable({ axis: 'y', items: 'tr', helper: fixHelper, handle: '.handle' })
25
+
26
+
@@ -3,9 +3,7 @@
3
3
  $(document).ready ->
4
4
 
5
5
  $(".modal-dialog").modal({backdrop: true, keyboard: true, show: false, static: false})
6
- $("[data-tip]").tooltip title: (e) ->
7
- $(this).data "tip"
8
-
6
+
9
7
  $(".btn.disabled").on "click", (e) ->
10
8
  e.stopPropagation()
11
9
  false
@@ -21,5 +19,6 @@ $(document).ready ->
21
19
  ($ @).closest('.modal').modal('hide')
22
20
 
23
21
  $("[data-toggle='popover']").popover()
22
+ $("[data-toggle='tooltip']").tooltip()
24
23
 
25
24
  $('.carousel').carousel()
@@ -17,6 +17,10 @@ $(document).ready ->
17
17
  label_input = $("#" + label_input_id)
18
18
  label_input.change ->
19
19
  associated_resource.find(".associated-resource-label").text(label_input.val())
20
+
21
+ update_positions = (sortable) ->
22
+ sortable.find('input.associated-resource-position').each (i, e) ->
23
+ $(@).val(i)
20
24
 
21
25
  $(".btn-add-associated-resource").click ->
22
26
  associated_resources = $(@).closest(".associated-resources")
@@ -26,4 +30,9 @@ $(document).ready ->
26
30
  template_instance = $(resource_fields_template.html().replace(/___index___/g, new_id))
27
31
  associated_resources_index.append(template_instance)
28
32
  autoupdate_labels(associated_resources_index.find('.associated-resource:last'))
29
-
33
+ update_positions associated_resources_index
34
+
35
+ autoupdate_labels($('.associated-resource'))
36
+
37
+ $('.positionable .sortable').live 'sortupdate', ->
38
+ update_positions $(@)
@@ -183,3 +183,36 @@ select,
183
183
  display: none
184
184
  }
185
185
 
186
+
187
+ .help-inline
188
+ {
189
+ padding: 0px 35px 8px 14px;
190
+ margin-bottom: 18px;
191
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
192
+ background-color: #FCF8E3;
193
+ border: 1px solid #FBEED5;
194
+ -webkit-border-radius: 4px;
195
+ -moz-border-radius: 4px;
196
+ border-radius: 4px;
197
+ color: #C09853;
198
+ margin-top: 0;
199
+ }
200
+
201
+ .help-inline:before
202
+ {
203
+ content: "";
204
+ border: solid 10px transparent; /* set all borders to 10 pixels width */
205
+ border-bottom-color: #FCF8E3; /* the callout */
206
+ border-top: 0; /* we do not need the bottom border in this case */
207
+ width: 0;
208
+ height: 0;
209
+ overflow: hidden;
210
+ display: block;
211
+ position: relative;
212
+ top: -9px; /* border-width of the :after element + padding of the root element */
213
+ }
214
+
215
+ .boolean > .input label {
216
+ display: inline-block;
217
+ margin-left: 10px;
218
+ }
@@ -17,14 +17,21 @@ module AdminFormHelper
17
17
  action(@target.object.persisted? ? :update : :create, :primary => true)
18
18
  end
19
19
 
20
- def form_errors(options = {:exclude => [:slug]})
20
+ def form_errors(options = {:wrap => true})
21
+ wrap = options.delete(:wrap)
22
+ options[:exclude] ||= [:slug]
21
23
  f = @target
22
24
  unless f.object.errors.empty?
23
- @target.template.content_tag :div, :class => "alert alert-block" do
24
- @target.template.link_to "×", :class => "close", :data => {:dismiss => "alert"}
25
- @target.template.content_tag(:h4, I18n.t('fullstack.admin.form.correct_these_errors_and_retry', :default => "Correct these errors and retry"), :class => "alert-heading")
25
+ if wrap
26
+ @target.template.content_tag :div, :class => "alert alert-block" do
27
+ @target.template.link_to "×", :class => "close", :data => {:dismiss => "alert"}
28
+ @target.template.content_tag(:h4, I18n.t('fullstack.admin.form.correct_these_errors_and_retry', :default => "Correct these errors and retry"), :class => "alert-heading")
29
+ f.semantic_errors *(f.object.errors.keys - (options[:exclude] || []))
30
+ end
31
+ else
26
32
  f.semantic_errors *(f.object.errors.keys - (options[:exclude] || []))
27
33
  end
34
+
28
35
  end
29
36
  end
30
37
  alias :errors :form_errors
@@ -75,12 +82,21 @@ module AdminFormHelper
75
82
  buff << @target.input(k, :as => :select)
76
83
  end
77
84
  else
78
- buff << @target.input(k)
85
+
86
+ buff << @target.input(k)
79
87
  end
80
88
 
81
89
  }
82
90
 
83
- buff.html_safe
91
+ buff = self.inputs do
92
+ buff.html_safe
93
+ end
94
+
95
+ model.reflect_on_all_associations(:has_many).select {|m| m.options[:autosave] }.each do |assoc|
96
+ buff << association_inputs(assoc.name)
97
+ end
98
+
99
+ buff
84
100
  end
85
101
 
86
102
  def resource_submit
@@ -100,36 +116,10 @@ module AdminFormHelper
100
116
  end
101
117
 
102
118
  def association_inputs(association)
103
- @target.template.content_tag :div,:class => "well" do
104
- admin_fields_for(association) do |f|
105
-
106
- partial_name = association.to_s.singularize + "_fields"
107
-
108
- if @target.template.partial?(partial_name)
109
- @target.template.render(:partial => partial_name, :f => f)
110
- else
111
- f.resource_inputs
112
- end
113
-
114
- end
115
- end
119
+ @target.template.render :partial => "associated_resources_table", :locals => {
120
+ :association => association, :f => self }
116
121
  end
117
122
 
118
- # def link_to_remove_fields(name)
119
- # @target.hidden_field(:_destroy) + link_to_function(name, "remove_fields(this)")
120
- # end
121
- #
122
- # def link_to_add_fields(name, association)
123
- # new_object = @target.object.class.reflect_on_association(association).klass.new
124
- #
125
- # partial_name = association.to_s.singularize + "_fields"
126
- # fields = admin_fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
127
- #
128
- # end
129
- # @target.template.link_to_function(name, "add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\")")
130
- # end
131
-
132
-
133
123
  end # ~
134
124
 
135
125
  def admin_form_for(record_or_name_or_array, *args)
@@ -21,7 +21,7 @@ module ScaffoldHelper
21
21
  end
22
22
 
23
23
  def labelize_attribute_name(method)
24
- label(:object, method).gsub("<label for=\"object_#{method}\">", "").gsub("</label>", "")
24
+ I18n.t("helpers.label.#{method}", :default => method.to_s.humanize)
25
25
  end
26
26
 
27
27
  def sort_link(method)
@@ -37,6 +37,11 @@ module ScaffoldHelper
37
37
  model.columns_hash["created_at"]
38
38
  end
39
39
 
40
+ def positionable?(object_or_class)
41
+ model = object_or_class.is_a?(Class) ? object_or_class : object_or_class.class
42
+ model.columns_hash["position"]
43
+ end
44
+
40
45
  def title_column(model)
41
46
  @_title_columns ||= {}
42
47
  @_title_columns[model] ||= ( model.column_names.map{ |c| c.to_s } & %W(title name label browser_title seo_title seo_name key claim email) ).first
@@ -1,112 +1,77 @@
1
1
  class BooleanInput
2
-
3
-
4
2
  include Formtastic::Inputs::Base
5
3
  include FormtasticBootstrap::Inputs::Base::Labelling
6
4
  include FormtasticBootstrap::Inputs::Base::Wrapping
7
5
  include FormtasticBootstrap::Inputs::Base::Errors
8
6
  include FormtasticBootstrap::Inputs::Base::Hints
9
7
 
8
+
9
+ def generic_input_wrapping_without_label(&block)
10
+ clearfix_div_wrapping do
11
+ input_div_wrapping do
12
+ if options[:prepend]
13
+ prepended_input_wrapping do
14
+ [template.content_tag(:span, options[:prepend], :class => 'add-on'), yield].join("\n").html_safe
15
+ end
16
+ else
17
+ yield
18
+ end
19
+ end
20
+ end
21
+ end
10
22
 
11
23
  def wrapper_html_options
12
- super.merge(:class => "mb1")
24
+ super.merge(:class => [super[:class], "mb1"].join(" "))
13
25
  end
14
26
 
15
-
16
27
  def to_html
17
-
18
- generic_input_wrapping do
19
-
20
- hidden_field_html <<
21
- check_box_html
22
- end
23
-
24
-
28
+ generic_input_wrapping_without_label do
29
+ hidden_field_html << check_box_html << label_html
30
+ end
25
31
  end
26
32
 
27
- def hidden_field_html
28
- template.hidden_field_tag(input_html_options[:name], unchecked_value, :id => nil, :disabled => input_html_options[:disabled] )
29
- end
30
-
31
- def label_with_nested_checkbox
32
- builder.label(
33
- method,
34
- label_text_with_embedded_checkbox,
35
- label_html_options
36
- )
37
- end
38
-
39
- def label_html_options
40
- prev = super
41
- prev[:class] = prev[:class] - ['label']
42
-
43
- input_html_options.merge(
44
- prev.merge(
45
- :id => nil,
46
- :name => nil,
47
- :for => input_html_options[:id]
48
- )
49
- )
50
- end
51
-
52
- def label_text_with_embedded_checkbox
53
- check_box_html << "" << label_text
54
- end
55
-
56
- def check_box_html
57
- template.check_box_tag("#{object_name}[#{method}]", checked_value, checked?, input_html_options)
58
- end
59
-
60
- def unchecked_value
61
- options[:unchecked_value] || '0'
62
- end
33
+ def hidden_field_html
34
+ template.hidden_field_tag(input_html_options[:name], unchecked_value, :id => nil, :disabled => input_html_options[:disabled] )
35
+ end
63
36
 
64
- def checked_value
65
- options[:checked_value] || '1'
66
- end
37
+ def check_box_html
38
+ template.check_box_tag("#{object_name}[#{method}]", checked_value, checked?, input_html_options)
39
+ end
67
40
 
68
- def responds_to_global_required?
69
- false
70
- end
41
+ def unchecked_value
42
+ options[:unchecked_value] || '0'
43
+ end
71
44
 
72
- def input_html_options
73
- {:name => input_html_options_name}.merge(super)
74
- end
75
-
76
- def input_html_options_name
77
- if builder.options.key?(:index)
78
- "#{object_name}[#{builder.options[:index]}][#{method}]"
79
- else
80
- "#{object_name}[#{method}]"
81
- end
82
- end
45
+ def checked_value
46
+ options[:checked_value] || '1'
47
+ end
83
48
 
84
- def checked?
85
- if defined? ActionView::Helpers::InstanceTag
86
- object && ActionView::Helpers::InstanceTag.check_box_checked?(object.send(method), checked_value)
87
- else
88
- object && boolean_checked?(object.send(method), checked_value)
89
- end
90
- end
49
+ def checked?
50
+ if defined? ActionView::Helpers::InstanceTag
51
+ object && ActionView::Helpers::InstanceTag.check_box_checked?(object.send(method), checked_value)
52
+ else
53
+ object && boolean_checked?(object.send(method), checked_value)
54
+ end
55
+ end
91
56
 
92
- private
57
+ private
93
58
 
94
- def boolean_checked?(value, checked_value)
95
- case value
96
- when TrueClass, FalseClass
97
- value
98
- when NilClass
99
- false
100
- when Integer
101
- value != 0
102
- when String
103
- value == checked_value
104
- when Array
105
- value.include?(checked_value)
106
- else
107
- value.to_i != 0
108
- end
109
- end
59
+ def boolean_checked?(value, checked_value)
60
+ case value
61
+ when TrueClass, FalseClass
62
+ value
63
+ when NilClass
64
+ false
65
+ when Integer
66
+ value != 0
67
+ when String
68
+ value == checked_value
69
+ when Array
70
+ value.include?(checked_value)
71
+ else
72
+ value.to_i != 0
73
+ end
74
+ end
110
75
 
111
76
 
112
77
  end
@@ -0,0 +1,10 @@
1
+ class CountryInput < FormtasticBootstrap::Inputs::SelectInput
2
+
3
+ def to_html
4
+ builder.input(method, :as => :select, :collection => Iso3166.codes.map {|code| Iso3166.localize(code)})
5
+ end
6
+
7
+
8
+
9
+
10
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+
3
+ module Iso3166
4
+ ALPHA_3_CODES = ["AFG", "ALA", "ALB", "DZA", "ASM", "AND", "AGO", "AIA", "ATA", "ATG", "ARG", "ARM", "ABW", "AUS", "AUT", "AZE", "BHS", "BHR", "BGD", "BRB", "BLR", "BEL", "BLZ", "BEN", "BMU", "BTN", "BOL", "BES", "BIH", "BWA", "BVT", "BRA", "IOT", "BRN", "BGR", "BFA", "BDI", "KHM", "CMR", "CAN", "CPV", "CYM", "CAF", "TCD", "CHL", "CHN", "CXR", "CCK", "COL", "COM", "COG", "COD", "COK", "CRI", "CIV", "HRV", "CUB", "CUW", "CYP", "CZE", "DNK", "DJI", "DMA", "DOM", "ECU", "EGY", "SLV", "GNQ", "ERI", "EST", "ETH", "FLK", "FRO", "FJI", "FIN", "FRA", "GUF", "PYF", "ATF", "GAB", "GMB", "GEO", "DEU", "GHA", "GIB", "GRC", "GRL", "GRD", "GLP", "GUM", "GTM", "GGY", "GIN", "GNB", "GUY", "HTI", "HMD", "VAT", "HND", "HKG", "HUN", "ISL", "IND", "IDN", "IRN", "IRQ", "IRL", "IMN", "ISR", "ITA", "JAM", "JPN", "JEY", "JOR", "KAZ", "KEN", "KIR", "PRK", "KOR", "KWT", "KGZ", "LAO", "LVA", "LBN", "LSO", "LBR", "LBY", "LIE", "LTU", "LUX", "MAC", "MKD", "MDG", "MWI", "MYS", "MDV", "MLI", "MLT", "MHL", "MTQ", "MRT", "MUS", "MYT", "MEX", "FSM", "MDA", "MCO", "MNG", "MNE", "MSR", "MAR", "MOZ", "MMR", "NAM", "NRU", "NPL", "NLD", "NCL", "NZL", "NIC", "NER", "NGA", "NIU", "NFK", "MNP", "NOR", "OMN", "PAK", "PLW", "PSE", "PAN", "PNG", "PRY", "PER", "PHL", "PCN", "POL", "PRT", "PRI", "QAT", "REU", "ROU", "RUS", "RWA", "BLM", "SHN", "KNA", "LCA", "MAF", "SPM", "VCT", "WSM", "SMR", "STP", "SAU", "SEN", "SRB", "SYC", "SLE", "SGP", "SXM", "SVK", "SVN", "SLB", "SOM", "ZAF", "SGS", "SSD", "ESP", "LKA", "SDN", "SUR", "SJM", "SWZ", "SWE", "CHE", "SYR", "TWN", "TJK", "TZA", "THA", "TLS", "TGO", "TKL", "TON", "TTO", "TUN", "TUR", "TKM", "TCA", "TUV", "UGA", "UKR", "ARE", "GBR", "USA", "UMI", "URY", "UZB", "VUT", "VEN", "VNM", "VGB", "VIR", "WLF", "ESH", "YEM", "ZMB", "ZWE"]
5
+ DEFAULTS = ActiveSupport::HashWithIndifferentAccess.new("AFG"=>"Afghanistan", "ALA"=>"Aland Islands", "ALB"=>"Albania", "DZA"=>"Algeria", "ASM"=>"American Samoa", "AND"=>"Andorra", "AGO"=>"Angola", "AIA"=>"Anguilla", "ATA"=>"Antarctica", "ATG"=>"Antigua and Barbuda", "ARG"=>"Argentina", "ARM"=>"Armenia", "ABW"=>"Aruba", "AUS"=>"Australia", "AUT"=>"Austria", "AZE"=>"Azerbaijan", "BHS"=>"Bahamas", "BHR"=>"Bahrain", "BGD"=>"Bangladesh", "BRB"=>"Barbados", "BLR"=>"Belarus", "BEL"=>"Belgium", "BLZ"=>"Belize", "BEN"=>"Benin", "BMU"=>"Bermuda", "BTN"=>"Bhutan", "BOL"=>"Bolivia, Plurinational State of", "BES"=>"Bonaire, Sint Eustatius and Saba", "BIH"=>"Bosnia and Herzegovina", "BWA"=>"Botswana", "BVT"=>"Bouvet Island", "BRA"=>"Brazil", "IOT"=>"British Indian Ocean Territory", "BRN"=>"Brunei Darussalam", "BGR"=>"Bulgaria", "BFA"=>"Burkina Faso", "BDI"=>"Burundi", "KHM"=>"Cambodia", "CMR"=>"Cameroon", "CAN"=>"Canada", "CPV"=>"Cape Verde", "CYM"=>"Cayman Islands", "CAF"=>"Central African Republic", "TCD"=>"Chad", "CHL"=>"Chile", "CHN"=>"China", "CXR"=>"Christmas Island", "CCK"=>"Cocos (Keeling) Islands", "COL"=>"Colombia", "COM"=>"Comoros", "COG"=>"Congo", "COD"=>"Congo, the Democratic Republic of the", "COK"=>"Cook Islands", "CRI"=>"Costa Rica", "CIV"=>"Cote d'Ivoire", "HRV"=>"Croatia", "CUB"=>"Cuba", "CUW"=>"Curaçao", "CYP"=>"Cyprus", "CZE"=>"Czech Republic", "DNK"=>"Denmark", "DJI"=>"Djibouti", "DMA"=>"Dominica", "DOM"=>"Dominican Republic", "ECU"=>"Ecuador", "EGY"=>"Egypt", "SLV"=>"El Salvador", "GNQ"=>"Equatorial Guinea", "ERI"=>"Eritrea", "EST"=>"Estonia", "ETH"=>"Ethiopia", "FLK"=>"Falkland Islands (Malvinas)", "FRO"=>"Faroe Islands", "FJI"=>"Fiji", "FIN"=>"Finland", "FRA"=>"France", "GUF"=>"French Guiana", "PYF"=>"French Polynesia", "ATF"=>"French Southern Territories", "GAB"=>"Gabon", "GMB"=>"Gambia", "GEO"=>"Georgia", "DEU"=>"Germany", "GHA"=>"Ghana", "GIB"=>"Gibraltar", "GRC"=>"Greece", "GRL"=>"Greenland", "GRD"=>"Grenada", "GLP"=>"Guadeloupe", "GUM"=>"Guam", "GTM"=>"Guatemala", "GGY"=>"Guernsey", "GIN"=>"Guinea", "GNB"=>"Guinea-Bissau", "GUY"=>"Guyana", "HTI"=>"Haiti", "HMD"=>"Heard Island and McDonald Islands", "VAT"=>"Holy See (Vatican City State)", "HND"=>"Honduras", "HKG"=>"Hong Kong", "HUN"=>"Hungary", "ISL"=>"Iceland", "IND"=>"India", "IDN"=>"Indonesia", "IRN"=>"Iran, Islamic Republic of", "IRQ"=>"Iraq", "IRL"=>"Ireland", "IMN"=>"Isle of Man", "ISR"=>"Israel", "ITA"=>"Italy", "JAM"=>"Jamaica", "JPN"=>"Japan", "JEY"=>"Jersey", "JOR"=>"Jordan", "KAZ"=>"Kazakhstan", "KEN"=>"Kenya", "KIR"=>"Kiribati", "PRK"=>"Korea, Democratic People's Republic of", "KOR"=>"Korea, Republic of", "KWT"=>"Kuwait", "KGZ"=>"Kyrgyzstan", "LAO"=>"Lao People's Democratic Republic", "LVA"=>"Latvia", "LBN"=>"Lebanon", "LSO"=>"Lesotho", "LBR"=>"Liberia", "LBY"=>"Libya", "LIE"=>"Liechtenstein", "LTU"=>"Lithuania", "LUX"=>"Luxembourg", "MAC"=>"Macao", "MKD"=>"Macedonia, the former Yugoslav Republic of", "MDG"=>"Madagascar", "MWI"=>"Malawi", "MYS"=>"Malaysia", "MDV"=>"Maldives", "MLI"=>"Mali", "MLT"=>"Malta", "MHL"=>"Marshall Islands", "MTQ"=>"Martinique", "MRT"=>"Mauritania", "MUS"=>"Mauritius", "MYT"=>"Mayotte", "MEX"=>"Mexico", "FSM"=>"Micronesia, Federated States of", "MDA"=>"Moldova, Republic of", "MCO"=>"Monaco", "MNG"=>"Mongolia", "MNE"=>"Montenegro", "MSR"=>"Montserrat", "MAR"=>"Morocco", "MOZ"=>"Mozambique", "MMR"=>"Myanmar", "NAM"=>"Namibia", "NRU"=>"Nauru", "NPL"=>"Nepal", "NLD"=>"Netherlands", "NCL"=>"New Caledonia", "NZL"=>"New Zealand", "NIC"=>"Nicaragua", "NER"=>"Niger", "NGA"=>"Nigeria", "NIU"=>"Niue", "NFK"=>"Norfolk Island", "MNP"=>"Northern Mariana Islands", "NOR"=>"Norway", "OMN"=>"Oman", "PAK"=>"Pakistan", "PLW"=>"Palau", "PSE"=>"Palestinian Territory, Occupied", "PAN"=>"Panama", "PNG"=>"Papua New Guinea", "PRY"=>"Paraguay", "PER"=>"Peru", "PHL"=>"Philippines", "PCN"=>"Pitcairn", "POL"=>"Poland", "PRT"=>"Portugal", "PRI"=>"Puerto Rico", "QAT"=>"Qatar", "REU"=>"Reunion", "ROU"=>"Romania", "RUS"=>"Russian Federation", "RWA"=>"Rwanda", "BLM"=>"Saint Barthélemy", "SHN"=>"Saint Helena, Ascension and Tristan da Cunha", "KNA"=>"Saint Kitts and Nevis", "LCA"=>"Saint Lucia", "MAF"=>"Saint Martin (French part)", "SPM"=>"Saint Pierre and Miquelon", "VCT"=>"Saint Vincent and the Grenadines", "WSM"=>"Samoa", "SMR"=>"San Marino", "STP"=>"Sao Tome and Principe", "SAU"=>"Saudi Arabia", "SEN"=>"Senegal", "SRB"=>"Serbia", "SYC"=>"Seychelles", "SLE"=>"Sierra Leone", "SGP"=>"Singapore", "SXM"=>"Sint Maarten (Dutch part)", "SVK"=>"Slovakia", "SVN"=>"Slovenia", "SLB"=>"Solomon Islands", "SOM"=>"Somalia", "ZAF"=>"South Africa", "SGS"=>"South Georgia and the South Sandwich Islands", "SSD"=>"South Sudan", "ESP"=>"Spain", "LKA"=>"Sri Lanka", "SDN"=>"Sudan", "SUR"=>"Suriname", "SJM"=>"Svalbard and Jan Mayen", "SWZ"=>"Swaziland", "SWE"=>"Sweden", "CHE"=>"Switzerland", "SYR"=>"Syrian Arab Republic", "TWN"=>"Taiwan, Province of China", "TJK"=>"Tajikistan", "TZA"=>"Tanzania, United Republic of", "THA"=>"Thailand", "TLS"=>"Timor-Leste", "TGO"=>"Togo", "TKL"=>"Tokelau", "TON"=>"Tonga", "TTO"=>"Trinidad and Tobago", "TUN"=>"Tunisia", "TUR"=>"Turkey", "TKM"=>"Turkmenistan", "TCA"=>"Turks and Caicos Islands", "TUV"=>"Tuvalu", "UGA"=>"Uganda", "UKR"=>"Ukraine", "ARE"=>"United Arab Emirates", "GBR"=>"United Kingdom", "USA"=>"United States", "UMI"=>"United States Minor Outlying Islands", "URY"=>"Uruguay", "UZB"=>"Uzbekistan", "VUT"=>"Vanuatu", "VEN"=>"Venezuela, Bolivarian Republic of", "VNM"=>"Viet Nam", "VGB"=>"Virgin Islands, British", "VIR"=>"Virgin Islands, U.S.", "WLF"=>"Wallis and Futuna", "ESH"=>"Western Sahara", "YEM"=>"Yemen", "ZMB"=>"Zambia", "ZWE"=>"Zimbabwe")
6
+
7
+ def default(code)
8
+ DEFAULTS[code] || code
9
+ end
10
+ module_function :default
11
+
12
+ def localize(code)
13
+ I18n.t(code, :scope => "iso3166", :default => default(code))
14
+ end
15
+ module_function :localize
16
+
17
+ def codes
18
+ ALPHA_3_CODES
19
+ end
20
+ module_function :codes
21
+ end