feat: add devise for user authentication

- Include 'devise' gem for user management
- Update database access URLs in configurations
- Refactor schema types from bigint to integer for resource and author IDs
- Enhance geo synchronization by updating region and subregion handling

These changes are aimed at improving the user management functionality while ensuring efficient data handling and connections for production environment.
This commit is contained in:
songtianlun 2025-02-11 11:46:15 +08:00
parent 90c491637b
commit 92ec81b152
7 changed files with 158 additions and 112 deletions

View File

@ -65,6 +65,8 @@ gem "image_processing", "~> 1.13"
# gem "ruby-vips", "~> 2.2" # gem "ruby-vips", "~> 2.2"
gem "mini_magick", "~> 4.13.2" gem "mini_magick", "~> 4.13.2"
gem 'devise', '~> 4.9'
group :development, :test do group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", platforms: %i[ mri windows ], require: "debug/prelude" gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"

View File

@ -1 +1 @@
zSnch4RlzPEpqZOE+TyBVBnHu+wyiNfNZoe1zi+cGNcgN/0BMw3xVQNIdu5ZgZzklukyimLlvkic7/G3PZE5aooqKIPeaGIgVKwdBvIGZYkTbrLTPtiA2Mx2iay4u6XI3eLDZZdy9g446GTngz9beQFR9s7dBbRBewOdIuCjA17seywsv722x97wHCv1xoGBeknK0DrEaYQcnECcy3G3esAsGeoKCQRMNHIf5/IbB6J+ZsoyOpnwtjOXdfEUnmtieLqvyw1xjgOxQTp2GMJk8zq+dGUBR3nIzplzccJbotppWfUbd9nxuT0stxWdFtKRPceynUfJ4awQNOmx+qGXi2/sYZH9lEB0nnyuKUpte6/bf1kcxcXyqTcsjaw0f9DXC3z9jx0pPbf2+O1MLZSFmo7Sr18mXjHVWjSCXnuYmWWkkuKESl4DwW5KScguWdnsf+/Y1eHds2MpE9dRLynlf5ONvFMY495iXuN4bR7UE5utAmU3imAg8vhErDqiro2L0GKMY5boMg+9NIObSlxwI6uAer8HaIKBVioiXGHFoFofR0LpmIKTbHiRF9ZZM3KCunsrQXAvRB4X7jL18JR08cDGGML2m03u9fcn9VuCYgcr+TLnsBozoG+ATP03kXc6lxkxjDvOc2z/AKqPmHUejCvsTLibFuEgw34E/NnVgcrC/Cggh/pp2Vmc6Yg2aQN7NcUmz0enGpzaTcnwiiB/8U8ig5WUovZP/iG3JH8S4KtBhDVz4hzz3FDOFBZCHd0rDrLtcyPtOcI3/WTZ0BgV5SXxA6L1gmM+aSYXtiyKZ2iUUOkMnNPvmHtimd+IWJZn2qXswX8RuE5QTqL5R5PRIdawdMSf9QrBSmDllNCo3czzClBRSNMYk2+ueWtiyvRuxP/xyNVl5e8nGOhAFWjCA+DVVA1xGWvS+R+3nxMSMtNaqTc2RPHQ3u4hY4k/0UH2TmnnzIJHS8vYScO4EzasTb/9FMeUEDnx369D7gvkXW6OapIKxwV0oQR8a/ZYUpOVyV6rSUIvEKrlpXCG8fh/6LwprYTPrnWe8iIwGqxKcuecrmtRSijAHgI2ZzMpSN91tomBBn9Qs+5svRbcXCO9KUKtjB9V82J7xqT+LwOyf1wa+v9zcO1aQxel2npRTsg7a4eIyIx1iyP7hHLAAuYIeEj1pBjiY/LiGZw+ScrXu1sIFTpt2SIKUOCVSN1y52/zXkwTRHc3qf4IdeHX6DqzWmWUiEHwm9Lva1fJ6poJ3vqWi7OLWJqyDtiN2FHJqybQ9mrgWPb0WBYi3umnfPy6ZYMQ6jQ8YarK8aFHsVdjUL0I4DKigAuer47p2rUcaFvySGCoMaSu5krSBw3VkDR6y1J8wIAm7nKZOSyoKwRjrgwXOl8yTN+zoemiaUMNJI1mTu1JQeg5rX2yUvwLEu5hu/5eOuRCkoSiwcG1+LmcnZbYRgSRvDzRcKYKZ/5hzQkPCLRkwAhh1Zp7mtc=--dyeF3fjnBpbK0tnw--nWdBu1crWT0VX9aHfnoKRw== eq4Y6iazLmvUUO+x38IQUs2iC/Ti6GXAmFGcUSIxTxqhENoqTGSWfVCu34gV7pZAnwsvc6T/csaUYutI2SaY1bS692Dm5WAWPzvAXkR5aJtmRtt8v9GMuSxcMLLkpxPGQzTWO+lVnh6ecFAQDkh2takeSSlE5OW6i4kNwqouRmvfTRVkIhmOHDJR/pRkQbk6oPYNwcx477m7KPcX1gN2k4Et5id2liFlXE7k3x4Xli9SM4dWjIV67E7wabC5+0vbz5KmvuyBqt2URW8klhQheGecOlYV37XmO0lErV1zBP2lWB9pz9QqsHNe71EMvsPwhYtYVIEyDt322pEdT3FUFOa4XBGxu2MGQFb+8Ff3tCi/DYj/kJec37sP/zcHb59AgxCUJB0VENQqiaOu2u0NTCgjODbaYnoJws8IaNybGEaEzmJkGefFG03nEEpdSpxD5QPU4MJC+byihwkA8iGzO1bW4iW+oiIPgn4RHX7UR4ADx3jGTlDsAX1pQIvzZCnf2/eRTlgcfz+QXzbvQ7wBOIHGG6kMa6rhVwYf1s9RoD7+HlYNN/egcCqj9QshibM7JADdwAQebn3An+/8gmYmRKgxQtwBf/8XSMT0m2oSfldiS3D6+kRrL4tUNjSIHqWUSQPvZomAeZrH4QtuK5dKov9OSqoSwlMUBBc9N+GvFxKIpJDOKF8JWNKoQUv+5CUWoU+AndlJBAoBnvwJbdNC2w1ergaHoVKfdDA4NU6U/TAtEbwpSbnrtYTf8Mxb/7rX30Ck1tQEuxQHUhiRozFLe4qsOhI2fQ0VOJ1HEfxK0ttdmo8i5FnpQ8iqjFE0LsAhIe5fFYqOqIpRbu/Uyt3fgThUD9p3QqBVX0ZPwNVnlqQwYfpRnrv3VNKzn0h6slyd2/hZYRVr+S2VGqxJgE0AZqTLZLSojeXHGrA3kHT1m0ncaOKEJ0YnGF9mKcbm43ViC6wgPBudbqs6mF0RTfDsAyK0JcWA4f4ssVH7QvjZgqrXz5AD4mth7U9gOSQSLQ9wxbiZTY8Csa1ZGNx3oszscFQ5Tw85tpSkk/Fuv6/7QKcVy/uFEyhWGVeJwWrLXsAyYP1lwbwyixgWflXE6APBopeVVVB7fLpEZTHyUyo/MHLe4Lp2Yw1cd1K8HOi0nIdSnEbc3HlIWwEsleZ4APFonvw7/JPlsceWUqgqbtW2KP9EtW6abplSwC9z8AuF6iFkEGYD1LFp5wE2gboaLtfUyFr0Xw1LVH3hn15ku3p7ptihvMqnHk05ulNhBAk6MVS231koMb0xIRTaP7BaUu25i0rmJgerWw2BJdjsiBab32s5u7sWnWR8fMCD55tsFMyiEZQHp1InXUVx8GBtbE9K0dTAvcIS0mKH8+DY61R9HeJ46L7AX6BADOpug9ScjiE62owl7CR95M97V+B53muKVSXT9tnqsYYJqP3fVk8X264i97T2JxOKwAxPxoPljv9Y6nwH8ZlR7s1sPCviOQxZBssEkmTxiYjX9J0+S0T/q9M6NwYZn1weEjCPFZbwHltcXBTqPuvR0iUN+itUBTnDkS0F3CkF12JmfYWJSOAh3PR+sgEcrJszySxoGMEv3smUx7MgJ+eZWIkbXbx0JWzEf/UJIparRGYVP4GCerZ7EY6Q2HG4WkyR3v2A7Ityzv0jI/u3DDXoEIB/+tW7xaeSzTJjaLjO--wzTRxzkF39xx+3YB--yfEzFDCIT3kpIEDGL5skrQ==

View File

@ -11,7 +11,8 @@ default: &default
development: development:
<<: *default <<: *default
database: storage/development.sqlite3 # database: storage/development.sqlite3
url: <%= Rails.application.credentials.db_dev.url %>
# Warning: The database defined as "test" will be erased and # Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake". # re-generated from your development database when you run "rake".

View File

@ -59,7 +59,7 @@ Rails.application.configure do
# config.action_mailer.raise_delivery_errors = false # config.action_mailer.raise_delivery_errors = false
# Set host to be used by links generated in mailer templates. # Set host to be used by links generated in mailer templates.
config.action_mailer.default_url_options = { host: "example.com" } config.action_mailer.default_url_options = { host: "todayaiweather.com" }
# Specify outgoing SMTP server. Remember to add smtp/* credentials via rails credentials:edit. # Specify outgoing SMTP server. Remember to add smtp/* credentials via rails credentials:edit.
# config.action_mailer.smtp_settings = { # config.action_mailer.smtp_settings = {

24
db/schema.rb generated
View File

@ -18,9 +18,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
t.string "namespace" t.string "namespace"
t.text "body" t.text "body"
t.string "resource_type" t.string "resource_type"
t.bigint "resource_id" t.integer "resource_id"
t.string "author_type" t.string "author_type"
t.bigint "author_id" t.integer "author_id"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.index ["author_type", "author_id"], name: "index_active_admin_comments_on_author" t.index ["author_type", "author_id"], name: "index_active_admin_comments_on_author"
@ -69,8 +69,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
end end
create_table "ahoy_events", force: :cascade do |t| create_table "ahoy_events", force: :cascade do |t|
t.bigint "visit_id" t.integer "visit_id"
t.bigint "user_id" t.integer "user_id"
t.string "name" t.string "name"
t.text "properties" t.text "properties"
t.datetime "time" t.datetime "time"
@ -82,7 +82,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
create_table "ahoy_visits", force: :cascade do |t| create_table "ahoy_visits", force: :cascade do |t|
t.string "visit_token" t.string "visit_token"
t.string "visitor_token" t.string "visitor_token"
t.bigint "user_id" t.integer "user_id"
t.string "ip" t.string "ip"
t.text "user_agent" t.text "user_agent"
t.text "referrer" t.text "referrer"
@ -120,12 +120,12 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.string "slug" t.string "slug"
t.bigint "country_id", null: false t.integer "country_id", null: false
t.string "state_code" t.string "state_code"
t.string "country_code" t.string "country_code"
t.boolean "flag", default: true t.boolean "flag", default: true
t.string "wiki_data_id" t.string "wiki_data_id"
t.bigint "state_id" t.integer "state_id"
t.index ["country_id"], name: "index_cities_on_country_id" t.index ["country_id"], name: "index_cities_on_country_id"
t.index ["slug"], name: "index_cities_on_slug", unique: true t.index ["slug"], name: "index_cities_on_slug", unique: true
t.index ["state_id"], name: "index_cities_on_state_id" t.index ["state_id"], name: "index_cities_on_state_id"
@ -135,7 +135,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
t.string "name" t.string "name"
t.string "code" t.string "code"
t.string "slug" t.string "slug"
t.bigint "region_id" t.integer "region_id"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.string "iso3" t.string "iso3"
@ -158,7 +158,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
t.string "emoji_u" t.string "emoji_u"
t.boolean "flag", default: true t.boolean "flag", default: true
t.string "wiki_data_id" t.string "wiki_data_id"
t.bigint "subregion_id" t.integer "subregion_id"
t.index ["code"], name: "index_countries_on_code", unique: true t.index ["code"], name: "index_countries_on_code", unique: true
t.index ["region_id"], name: "index_countries_on_region_id" t.index ["region_id"], name: "index_countries_on_region_id"
t.index ["slug"], name: "index_countries_on_slug", unique: true t.index ["slug"], name: "index_countries_on_slug", unique: true
@ -192,7 +192,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
create_table "states", force: :cascade do |t| create_table "states", force: :cascade do |t|
t.string "name" t.string "name"
t.string "code" t.string "code"
t.bigint "country_id" t.integer "country_id"
t.string "country_code" t.string "country_code"
t.string "fips_code" t.string "fips_code"
t.string "iso2" t.string "iso2"
@ -211,7 +211,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
create_table "subregions", force: :cascade do |t| create_table "subregions", force: :cascade do |t|
t.string "name", null: false t.string "name", null: false
t.text "translations" t.text "translations"
t.bigint "region_id", null: false t.integer "region_id", null: false
t.boolean "flag", default: true t.boolean "flag", default: true
t.string "wiki_data_id" t.string "wiki_data_id"
t.datetime "created_at", null: false t.datetime "created_at", null: false
@ -220,7 +220,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_08_052634) do
end end
create_table "weather_arts", force: :cascade do |t| create_table "weather_arts", force: :cascade do |t|
t.bigint "city_id", null: false t.integer "city_id", null: false
t.date "weather_date" t.date "weather_date"
t.string "description" t.string "description"
t.decimal "temperature" t.decimal "temperature"

View File

@ -10,64 +10,60 @@
# AdminUser.create!(email: 'admin@example.com', password: 'password', password_confirmation: 'password') if Rails.env.development? # AdminUser.create!(email: 'admin@example.com', password: 'password', password_confirmation: 'password') if Rails.env.development?
AdminUser.create!(email: 'admin@example.com', password: 'password', password_confirmation: 'password') AdminUser.create!(email: 'admin@example.com', password: 'password', password_confirmation: 'password')
WeatherArt.delete_all # WeatherArt.delete_all
City.delete_all # City.delete_all
Country.delete_all # Country.delete_all
Region.delete_all # Region.delete_all
# 创建区域 # 创建区域
regions = Region.create!([ regions = Region.create!([
{ {
name: 'Asia', name: 'Asia',
code: 'AS' code: 'AS'
}, # },
{ # {
name: 'South Asia', # name: 'Southeast Asia',
code: 'SA' # code: 'SEA'
}, # },
{ # {
name: 'Southeast Asia', # name: 'East Asia',
code: 'SEA' # code: 'EA'
}, # },
{ # {
name: 'East Asia', # name: 'Middle East',
code: 'EA' # code: 'ME'
}, # },
{ # {
name: 'Middle East', # name: 'Africa',
code: 'ME' # code: 'AF'
}, # },
{ # {
name: 'Africa', # name: 'North Africa',
code: 'AF' # code: 'NA'
}, # },
{ # {
name: 'North Africa', # name: 'Sub-Saharan Africa',
code: 'NA' # code: 'SSA'
}, # },
{ # {
name: 'Sub-Saharan Africa', # name: 'Europe',
code: 'SSA' # code: 'EU'
}, # },
{ # {
name: 'Europe', # name: 'North America',
code: 'EU' # code: 'NAM'
}, # },
{ # {
name: 'North America', # name: 'South America',
code: 'NAM' # code: 'SAM'
}, # },
{ # {
name: 'South America', # name: 'Central America',
code: 'SAM' # code: 'CAM'
}, # },
{ # {
name: 'Central America', # name: 'Oceania',
code: 'CAM' # code: 'OC'
},
{
name: 'Oceania',
code: 'OC'
} }
]) ])
asia = Region.find_by(code: 'AS') asia = Region.find_by(code: 'AS')
@ -80,66 +76,106 @@ oceania = Region.find_by(code: 'OC')
# 创建所有国家 # 创建所有国家
Country.create!([ Country.create!([
# East Asia # East Asia
{ name: 'China', code: 'CN', region: asia }, { name: 'China', code: 'CN', region: asia }
{ name: 'Japan', code: 'JP', region: asia }, # { name: 'Japan', code: 'JP', region: asia },
{ name: 'South Korea', code: 'KR', region: asia }, # { name: 'South Korea', code: 'KR', region: asia },
{ name: 'Taiwan', code: 'TW', region: asia }, # { name: 'Taiwan', code: 'TW', region: asia },
{ name: 'Hong Kong', code: 'HK', region: asia }, # { name: 'Hong Kong', code: 'HK', region: asia },
# South Asia # South Asia
{ name: 'India', code: 'IN', region: asia }, # { name: 'India', code: 'IN', region: asia },
{ name: 'Pakistan', code: 'PK', region: asia }, # { name: 'Pakistan', code: 'PK', region: asia },
{ name: 'Bangladesh', code: 'BD', region: asia }, # { name: 'Bangladesh', code: 'BD', region: asia },
# Southeast Asia # Southeast Asia
{ name: 'Indonesia', code: 'ID', region: asia }, # { name: 'Indonesia', code: 'ID', region: asia },
{ name: 'Vietnam', code: 'VN', region: asia }, # { name: 'Vietnam', code: 'VN', region: asia },
{ name: 'Thailand', code: 'TH', region: asia }, # { name: 'Thailand', code: 'TH', region: asia },
{ name: 'Myanmar', code: 'MM', region: asia }, # { name: 'Myanmar', code: 'MM', region: asia },
{ name: 'Singapore', code: 'SG', region: asia }, # { name: 'Singapore', code: 'SG', region: asia },
# Middle East # Middle East
{ name: 'Turkey', code: 'TR', region: asia }, # { name: 'Turkey', code: 'TR', region: asia },
{ name: 'Iran', code: 'IR', region: asia }, # { name: 'Iran', code: 'IR', region: asia },
{ name: 'Saudi Arabia', code: 'SA', region: asia }, # { name: 'Saudi Arabia', code: 'SA', region: asia },
{ name: 'Iraq', code: 'IQ', region: asia }, # { name: 'Iraq', code: 'IQ', region: asia },
# Africa # Africa
{ name: 'Nigeria', code: 'NG', region: africa }, # { name: 'Nigeria', code: 'NG', region: africa },
{ name: 'Egypt', code: 'EG', region: africa }, # { name: 'Egypt', code: 'EG', region: africa },
{ name: 'Democratic Republic of the Congo', code: 'CD', region: africa }, # { name: 'Democratic Republic of the Congo', code: 'CD', region: africa },
{ name: 'Tanzania', code: 'TZ', region: africa }, # { name: 'Tanzania', code: 'TZ', region: africa },
{ name: 'South Africa', code: 'ZA', region: africa }, # { name: 'South Africa', code: 'ZA', region: africa },
{ name: 'Kenya', code: 'KE', region: africa }, # { name: 'Kenya', code: 'KE', region: africa },
{ name: 'Angola', code: 'AO', region: africa }, # { name: 'Angola', code: 'AO', region: africa },
{ name: 'Mali', code: 'ML', region: africa }, # { name: 'Mali', code: 'ML', region: africa },
{ name: 'Ivory Coast', code: 'CI', region: africa }, # { name: 'Ivory Coast', code: 'CI', region: africa },
# Europe # Europe
{ name: 'Russia', code: 'RU', region: europe }, # { name: 'Russia', code: 'RU', region: europe },
{ name: 'United Kingdom', code: 'GB', region: europe }, # { name: 'United Kingdom', code: 'GB', region: europe },
{ name: 'Germany', code: 'DE', region: europe }, # { name: 'Germany', code: 'DE', region: europe },
{ name: 'France', code: 'FRA', region: europe }, # { name: 'France', code: 'FRA', region: europe },
# North America # North America
{ name: 'United States', code: 'US', region: north_america }, # { name: 'United States', code: 'US', region: north_america },
{ name: 'Mexico', code: 'MX', region: north_america }, # { name: 'Mexico', code: 'MX', region: north_america },
{ name: 'Canada', code: 'CA', region: north_america }, # { name: 'Canada', code: 'CA', region: north_america },
# South America # South America
{ name: 'Brazil', code: 'BR', region: south_america }, # { name: 'Brazil', code: 'BR', region: south_america },
{ name: 'Peru', code: 'PE', region: south_america }, # { name: 'Peru', code: 'PE', region: south_america },
{ name: 'Colombia', code: 'CO', region: south_america }, # { name: 'Colombia', code: 'CO', region: south_america },
{ name: 'Chile', code: 'CL', region: south_america }, # { name: 'Chile', code: 'CL', region: south_america },
# Oceania # Oceania
{ name: 'Australia', code: 'AU', region: oceania } # { name: 'Australia', code: 'AU', region: oceania }
]) ])
# 创建城市 # 创建城市
Dir[Rails.root.join('db/seeds/cities/*.rb')].sort.each do |file| # Dir[Rails.root.join('db/seeds/cities/*.rb')].sort.each do |file|
require file # require file
end # end
china = Country.find_by code: 'CN'
City.create!([
{
name: 'Shanghai',
latitude: 31.2304,
longitude: 121.4737,
country: china,
timezone: 'Asia/Shanghai',
active: true,
priority: 100
},
{
name: 'Beijing',
latitude: 39.9042,
longitude: 116.4074,
country: china,
timezone: 'Asia/Shanghai',
active: true,
priority: 100
},
{
name: 'Shenzhen',
latitude: 22.5431,
longitude: 114.0579,
country: china,
timezone: 'Asia/Shanghai',
active: true,
priority: 100
},
{
name: 'Guangzhou',
latitude: 23.1291,
longitude: 113.2644,
country: china,
timezone: 'Asia/Shanghai',
active: true,
priority: 100
}
])
guangzhou = City.find_by name: 'Guangzhou' guangzhou = City.find_by name: 'Guangzhou'
guangzhou_weather_art = WeatherArt.create!( guangzhou_weather_art = WeatherArt.create!(

View File

@ -22,11 +22,15 @@ namespace :geo do
count = 1 count = 1
regions.each do |data| regions.each do |data|
region = Region.find_or_create_by!(name: data["name"]) region = Region.find_or_create_by!(id: data["id"]) do |r|
r.name = data["name"]
r.code = data["name"]
end
puts "Sync Regions[#{count}/#{sum}]: [#{region.name}]" puts "Sync Regions[#{count}/#{sum}]: [#{region.name}]"
count += 1 count += 1
region.update!( region.update!(
name: data["name"],
code: data["name"], code: data["name"],
translations: data["translations"], translations: data["translations"],
flag: data["flag"] || true, flag: data["flag"] || true,
@ -42,10 +46,12 @@ namespace :geo do
count = 1 count = 1
subregions.each do |data| subregions.each do |data|
subregion = Subregion.find_or_create_by!(name: data["name"]) do |s| region = Region.find_by!(id: data["region_id"])
s.region_id = data["region_id"] subregion = Subregion.find_or_create_by!(id: data["id"]) do |s|
s.name = data["name"]
s.region_id = region.id
end end
puts "Sync Subregions[#{count}/#{sum}]: [#{subregion.name}]" puts "Sync Subregions[#{count}/#{sum}]: [#{subregion.name}] region:[#{region.name}]"
count += 1 count += 1
subregion.update!( subregion.update!(
@ -131,6 +137,7 @@ namespace :geo do
state = State.find_or_create_by!(name: data["name"]) do |s| state = State.find_or_create_by!(name: data["name"]) do |s|
s.country_id = country.id s.country_id = country.id
s.country_code = country.code
end end
state.update!( state.update!(