fullstack-admin 0.1.20 → 0.1.23

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