рдПрдЪрдЯреАрдПрдордПрд▓ рд░реВрдкреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрдХрд╛рд▓рд╛ рдкрд░ рдбреАрдПрд╕рдПрд▓



рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдЖрдк рдореЗрдВ рд╕реЗ рдмрд╣реБрдд рд╕реЗ HTML рдлреЙрд░реНрдо рдмрдирд╛рдиреЗ рдФрд░ рдкреНрд░реЛрд╕реЗрд╕ рдХрд░рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реИрдВред рдпрд╣ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рд╡реЗрдм рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдХреЗ рд▓рд┐рдП рддреБрдЪреНрдЫ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрджрд┐ рдЖрдк рдХреЙрд░реНрдкреЛрд░реЗрдЯ рдХреНрд╖реЗрддреНрд░ рдореЗрдВ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рд╕реНрдерд┐рддрд┐ рдереЛрдбрд╝реА рдЕрд▓рдЧ рд╣реИред рдХреНрд▓рд╛рдЗрдВрдЯреНрд╕, рдбреЙрдХреНрдпреВрдореЗрдВрдЯреНрд╕ рдмрдирд╛рдиреЗ рдФрд░ рд╕рдВрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХреЗ рд░реВрдк рдФрд░ рдмрд╣реБрдд рдХреБрдЫ рджреИрдирд┐рдХ рджрд┐рдирдЪрд░реНрдпрд╛ рдмрди рдЬрд╛рддреЗ рд╣реИрдВред рдЬрд╛рд╡рд╛ рдлреНрд░реЗрдорд╡рд░реНрдХ, рд╡рд┐рдХрд╛рд╕, рдЙрдирдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдзрд┐рдХ рд╕реЗ рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдПрдкреАрдЖрдИ рдФрд░ рдШрдЯрдХреЛрдВ рдХреА рдкреЗрд╢рдХрд╢ рдХрд░рддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдЗрд╕рдХреЗ рдмрд╛рд╡рдЬреВрдж, рдХрдИ рд▓реЛрдЧ рдЖрд╢реНрдЪрд░реНрдпрдЪрдХрд┐рдд рдереЗ рдХрд┐ рдХреНрдпрд╛ рд░реВрдкреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рдереЛрдбрд╝рд╛ рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реЛрдЧрд╛ред
рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рдореИрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдпрдерд╛рд╕рдВрднрд╡ рдЖрд╕рд╛рди рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд░реВрдкрд░реЗрдЦрд╛ рдЪрд╛рд╣реВрдВрдЧрд╛:

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрд╣ рд╡рд╛рдВрдЫрдиреАрдп рд╣реИ рдХрд┐ рд╕рдВрдХрд▓рди рдЪрд░рдг рдореЗрдВ рдХрдИ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рд╕реНрдХрд╛рд▓рд╛ рдореЗрдВ рдЕрдкрдирд╛ рд╕реНрд╡рдпрдВ рдХрд╛ рдбреАрдПрд╕рдПрд▓ рдмрдирд╛рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд╛ рд╡рд░реНрдгрди рдХрд░реВрдВрдЧрд╛, рдФрд░ рдлрд┐рд░ рджрд┐рдЦрд╛рдКрдВрдЧрд╛ рдХрд┐ рдкреНрд▓реЗ рдлреНрд░реЗрдорд╡рд░реНрдХ 2 рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ рд░реВрдкреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдиреЗ рдХрд╛ рдирдпрд╛ рддрд░реАрдХрд╛ рдХреИрд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рдПред

рд╢рдмреНрджрд╛рд╡рд▓реА рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдереЛрдбрд╝рд╛ред

рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ рдЖрдВрддрд░рд┐рдХ DSL рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░ рд░рд╣рд╛ рд╣реВрдБред рдЖрдВрддрд░рд┐рдХ рдбреАрдПрд╕рдПрд▓ рдПрдХ рдирдИ рднрд╛рд╖рд╛ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рд╣реЛрд╕реНрдЯ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рднрд╛рд╖рд╛ рдХреЗ рд╕рд┐рдВрдЯреИрдХреНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдбреЛрдореЗрди рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдиреЗ рдХрд╛ рдХреЗрд╡рд▓ рдПрдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рддрд░реАрдХрд╛ рд╣реИред рд╕рдЪ рд╣реИ, рдпрджрд┐ рд╣реЛрд╕реНрдЯ рднрд╛рд╖рд╛ рдХрд╛ рд╕рд┐рдВрдЯреИрдХреНрд╕ рдкрд░реНрдпрд╛рдкреНрдд рд▓рдЪреАрд▓рд╛ рд╣реИ, рддреЛ рдЖрдВрддрд░рд┐рдХ рдбреАрдПрд╕рдПрд▓ рдРрд╕рд╛ рд▓рдЧ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдЗрд╕ рдХреНрд╖реЗрддреНрд░ рдХреЗ рд▓рд┐рдП рдбрд┐рдЬрд╝рд╛рдЗрди рдХреА рдЧрдИ рдирдИ рднрд╛рд╖рд╛ рд╣реИред рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рдХреЗ рдлрд╛рдпрджреЛрдВ рдореЗрдВ рдпрд╣ рддрдереНрдп рд╢рд╛рдорд┐рд▓ рд╣реИ рдХрд┐ рд╡рд┐рдХрд╛рд╕ рдХреЗ рд╡рд╛рддрд╛рд╡рд░рдг рдЖрдВрддрд░рд┐рдХ рдбреАрдПрд╕рдПрд▓ рдХреЛ рд╕рдордЭрддреЗ рд╣реИрдВ, рд╡рд╛рдХреНрдпрд╡рд┐рдиреНрдпрд╛рд╕ рдХреЛ рдЙрдЬрд╛рдЧрд░ рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рд╕реНрд╡рдд: рдкреВрд░реНрдг рд╣реЛрдиреЗ рд╡рд╛рд▓реЗ рд╡рд┐рдХрд▓реНрдкреЛрдВ рдХреА рдкреЗрд╢рдХрд╢ рдХрд░рддреЗ рд╣реИрдВред рддреБрд▓рдирд╛ рдХреЗ рд▓рд┐рдП, рдмрд╛рд╣рд░реА рдбреАрдПрд╕рдПрд▓ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдПрдХ рдирдИ рднрд╛рд╖рд╛ рд╣реИ рдЬрд┐рд╕реЗ рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдкрд╛рд░реНрд╕рд░ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред

рдЖрд▓реЗрдЦ рдореЗрдВ рд╡рд░реНрдгрд┐рдд рдбреАрдПрд╕рдПрд▓ рдореВрд▓ рд░реВрдк рд╕реЗ рдкреНрд▓реЗ рдлреНрд░реЗрдорд╡рд░реНрдХ 2 рдореЗрдВ рдлреЙрд░реНрдо рдЗрдВрдЬрди рдХреА рдХреБрдЫ рд╕реАрдорд╛рдУрдВ рдХреЗ рдХрд╛рд░рдг рдЙрддреНрдкрдиреНрди рд╣реБрдИ рд╕рдорд╕реНрдпрд╛рдУрдВ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рдерд╛ред рд▓реЗрдХрд┐рди рдЕрдм рдпрд╣ рдкреНрд▓реЗ рд╕реЗ рд╕реНрд╡рддрдВрддреНрд░ рд╣реИ рдФрд░ рдЙрдкрдпреБрдХреНрдд рдПрдбреЗрдкреНрдЯрд░ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдкрд░ рдХрд┐рд╕реА рднреА рдЬреЗрд╡реАрдПрдо рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЗ рд╕рд╛рде рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

DSL рд╡рд┐рдХрд╛рд╕ рдХрд╣рд╛рдБ рд╕реЗ рд╢реБрд░реВ рдХрд░реЗрдВ?

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рддрдп рдХрд░реЗрдВ рдХрд┐ рдЖрдк рдкрд░рд┐рдгрд╛рдо рдХреЗ рд░реВрдк рдореЗрдВ рдХреНрдпрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рд╣рдореЗрдВ рдЗрд╕ рд╡рд┐рд╖рдп рдкрд░ рдХрд▓реНрдкрдирд╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ: "рдПрдХ рдЖрджрд░реНрд╢ рдбреАрдПрд╕рдПрд▓ рдХреЛ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреИрд╕рд╛ рджрд┐рдЦрдирд╛ рдЪрд╛рд╣рд┐рдП", рдереЛрдбрд╝реА рджреЗрд░ рдХреЗ рд▓рд┐рдП рднреВрд▓ рдЬрд╛рддреЗ рд╣реИрдВ рдХрд┐ рд╣рдо рдореЗрдЬрдмрд╛рди рднрд╛рд╖рд╛ рд╕рд┐рдВрдЯреИрдХреНрд╕ рджреНрд╡рд╛рд░рд╛ рд╕реАрдорд┐рдд рд╣реИрдВред
рдПрдХ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд░реВрдк рдореЗрдВ, рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдлрд╝реАрд▓реНрдб рдХреЗ рд╕рд╛рде рдкрдВрдЬреАрдХрд░рдг рдлреЙрд░реНрдо рд▓реЗрдВ:

рдЗрд╕рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдк рдЫрджреНрдордХреЛрдб рдореЗрдВ рдРрд╕реА рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
form( string(email, required, validate(EmailAddress)) string(name, required) date(birthDate) ) 

рдЕрдм рдпрд╣ рд╕реЛрдЪрдиреЗ рдХрд╛ рд╕рдордп рд╣реИ рдХрд┐ рдЗрд╕реЗ рдХреИрд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рдПред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдкреНрд░рдкрддреНрд░ рдХреЗ рд╕рд╛рдорд╛рдиреНрдп рд╡рд┐рд╡рд░рдг рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ, рдЕрд░реНрдерд╛рддреН рдлрд╝реАрд▓реНрдб рдФрд░ рдЙрдирдХреЗ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдПрдХ рд╕реЗрдЯред рд╕реНрдХрд╛рд▓рд╛ рд╣рдореЗрдВ рдРрд╕рд╛ рд╡рд┐рд╡рд░рдг рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдИ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ:

1. рдмрд┐рд▓реНрдбрд░ рдкреИрдЯрд░реНрди рдФрд░ рд╡рд┐рдзрд┐ рдЬрдВрдЬреАрд░

рдлрд╛рд░реНрдо рдХреЗ рд▓рд┐рдП рдлреИрдХреНрдЯрд░реА рд╡рд┐рдзрд┐ рд╣рд╕реНрддрд╛рдХреНрд╖рд░:
 def form(builderFoo: FormBuilder => FormBuilder) 

рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ:
 form(_ .string(...) .string(...) .extend(commonFields) .date(...) ) 

рдпрд╣рд╛рдВ рд╣рдо рдмрд┐рд▓реНрдбрд░ рдФрд░ рдореЗрдердб рдЪреЗрдирд┐рдВрдЧ рдкреИрдЯрд░реНрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕рд╕реЗ рд╣рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд▓рд╛рдн рдорд┐рд▓рддреЗ рд╣реИрдВ:

рдЖрдЧреЗ рджреЗрдЦрддреЗ рд╣реБрдП, рдореИрдВ рдХрд╣реВрдВрдЧрд╛ рдХрд┐ рдпрд╣ рд╡рд┐рдХрд▓реНрдк рдЪреБрдирд╛ рдЧрдпрд╛ рдерд╛ ред

2. рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рдПрдХ рдЪрд░ рд╕рдВрдЦреНрдпрд╛ рдХреЗ рд╕рд╛рде рдПрдХ рд╕рдорд╛рд░реЛрд╣ рдХреЗ рд░реВрдк рдореЗрдВ

рдлреИрдХреНрдЯрд░реА рд╡рд┐рдзрд┐:
 def form(fields: FormBuilder => FormBuilder*) 

рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ:
 form( _.string(...), _.string(...), _.date(...), commonFields:_* ) 

рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рдХреЗ рдХрдИ рдиреБрдХрд╕рд╛рди рд╣реИрдВ:

3. рдХреЙрд▓ рдЕрдиреБрдХреНрд░рдо рдХреЗ рд╕рд╛рде рдХреЛрдб рдмреНрд▓реЙрдХ

рдлреИрдХреНрдЯрд░реА рд╡рд┐рдзрд┐:
 def form(fields: => ()) 

 : form{ string(...) date(...) commonFields() } 

рдпрд╣ рддрд░реАрдХрд╛ рдмреБрд░рд╛ рд╣реИ:


рддреЛ, рдПрдХ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рд╕реЗ рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ рдкреНрд░рд╕рд┐рджреНрдз рдбрд┐рдЬрд╛рдЗрди рдкреИрдЯрд░реНрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рд╡рд┐рдХрд▓реНрдк рд╕рдмрд╕реЗ рдЙрдкрдпреБрдХреНрдд рд╣реИред рдЖрд╢реНрдЪрд░реНрдпред

рдЦреЗрддреЛрдВ рдХрд╛ рд╡рд░реНрдгрди

рдЦреЗрддреЛрдВ рдХреЗ рд╡рд┐рд╕реНрддреГрдд рд╡рд┐рд╡рд░рдг рдХреЗ рд▓рд┐рдП, рд╡рд┐рдзрд┐ рдЬрдВрдЬреАрд░ рдФрд░ рдмрд┐рд▓реНрдбрд░ рдХрд╛ рдЪрдпрди рднреА рдЙрдиреНрд╣реАрдВ рдХрд╛рд░рдгреЛрдВ рд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рдлрд╝реАрд▓реНрдб рдкрд░рд┐рднрд╛рд╖рд╛ рд╡рд┐рдзрд┐ рд╣рд╕реНрддрд╛рдХреНрд╖рд░:
 def string(fieldFoo: FieldBuilder => FieldBuilder): FormBuilder 

рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ:
 .string(_.name("email").required.validate(EmailAddress)) 

рдирд┐рд░реНрдорд╛рдг рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХрд╛ рд╕реНрдХреЗрдЪ

рд╕реНрдХреЗрд▓рд╛ рдореЗрдВ рдПрдХ рдмрд┐рд▓реНрдбрд░ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕рдХреА рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдкрд░рд┐рднрд╛рд╖рд┐рдд copy рд╡рд┐рдзрд┐ рдХреЗ рд╕рд╛рде рдПрдХ case class рдорд╣рд╛рди рд╣реИред рдпрд╣ рд╡рд┐рдзрд┐ рд╡рд░реНрддрдорд╛рди рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ рдХреЙрдкреА рдХрд░рддреА рд╣реИ, рдЖрд╡рд╢реНрдпрдХ рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рдХреЛ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд░рддреА рд╣реИред рдПрдХ рд╡реНрдпрдХреНрддрд┐рдЧрдд рдХреНрд╖реЗрддреНрд░ рдХреЗ рд▓рд┐рдП рдПрдХ рдмрд┐рд▓реНрдбрд░ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦ рд╕рдХрддрд╛ рд╣реИ:
 case class FieldBuilderImpl(fieldType: String, label: String, isRequired: Boolean, validators: Seq[Validator]) extends FieldBuilder { def fieldType(t: String): FieldBuilder = copy(fieldType = t) def label(l: String): FieldBuilder = copy(label = l) def required: FieldBuilder = copy(isRequired = true) def validate(vs: Validator*): FieldBuilder = copy(validators = validators ++ vs) def build: FieldDescription = ??? } 

рдореБрдЦреНрдп рд░реВрдк рдкреИрд░рд╛рдореАрдЯрд░ рдЗрд╕рдХреЗ рдЦреЗрддреЛрдВ рдХрд╛ рдПрдХ рд╕реЗрдЯ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЗрд╕рдХреЗ рд▓рд┐рдП рдмрд┐рд▓реНрдбрд░ рдереЛрдбрд╝рд╛ рдЕрд▓рдЧ рджрд┐рдЦреЗрдЧрд╛:
 case class FormBuilderImpl(fields: Map[String, FieldDescription]) extends FormBuilder { def field[F](name: String)(foo: FieldBuilder[F] => FieldBuilder[F]) = copy(fields = fields ++ Map(name -> foo(newFieldBuilder[F]).asInstanceOf[FieldBuilderImpl[F]].build)) def string(name: String)(foo: FieldBuilder[String] => FieldBuilder[String]) = field[String](name)(foo andThen (_.fieldType("string"))) def newFieldBuilder[F]: FieldBuilder[F] = ??? def build: FormDescription = ??? } 

рдпрд╣ рдзреНрдпрд╛рди рджреЗрдиреЗ рдпреЛрдЧреНрдп рд╣реИ рдХрд┐ рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рд╣рдореЗрдВ рдЕрднреА рднреА рдлрд╝реАрд▓реНрдб рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛ рдХреЛ рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЙрд╕реА рд╕рдордп, рдпрджрд┐ рд╣рдо рдЧрд▓рддреА рд╕реЗ рдЧрд▓рдд рдкрд╣рдЪрд╛рдирдХрд░реНрддрд╛ рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░рддреЗ рд╣реИрдВ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЗрд╕реЗ рд╕реАрд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИ), рддреЛ рд╣рдо рдХреЗрд╡рд▓ рдкреНрд░реЛрдЧреНрд░рд╛рдо рдирд┐рд╖реНрдкрд╛рджрди рдХреЗ рджреМрд░рд╛рди рддреНрд░реБрдЯрд┐ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдиреЗрдВрдЧреЗред

рдкреНрд░рдкрддреНрд░ рдбреЗрдЯрд╛ рдХреА рдкреНрд░рд╕реНрддреБрддрд┐

рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рдлреЙрд░реНрдо рдЬрдорд╛ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╡реЗрдм рдврд╛рдВрдЪрд╛ рдЕрдиреБрд░реЛрдз рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдлреНрд░реЗрдорд╡рд░реНрдХ-рд╡рд┐рд╢рд┐рд╖реНрдЯ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рддрд╛ рд╣реИред рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЗ рд╕рд╛рде рдПрдХреАрдХрд░рдг рдХреЗ рдХрд╛рд░реНрдпреЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рд╣рдорд╛рд░реА рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рд░реВрдк рдХреЗ рдЖрдВрддрд░рд┐рдХ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд╛ рдкрд░рд┐рд╡рд░реНрддрди рд╣реИ (рдЗрд╕ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ рдкреНрд▓реЗ рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ рд▓реЗрдЦ рдХреЗ рдЕрдВрдд рдореЗрдВ рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ)

рдкреНрд░рдкрддреНрд░ рдбреЗрдЯрд╛ рдкреНрд░рд╕реНрддреБрдд рдХрд░рдирд╛ case class рдХреА рдПрдХ рдорд┐рд╕рд╛рд▓ рдХреЗ рд░реВрдк рдореЗрдВ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИред рд╕рдВрдХреНрд╖рд┐рдкреНрддрддрд╛ рдХреЗ рд▓рд┐рдП, рд╣рдо рдЗрд╕реЗ "рдкреНрд░рдкрддреНрд░ рдбреЗрдЯрд╛ рдСрдмреНрдЬреЗрдХреНрдЯ" рдпрд╛ рдХреЗрд╡рд▓ "рдлрд╝реЙрд░реНрдо рдбреЗрдЯрд╛" рдХрд╣реЗрдВрдЧреЗред рдЗрд╕ рдХреНрд╖реЗрддреНрд░ рдХреЗ рдЯрд╛рдЗрдк рдХрд┐рдП рдЧрдП рдлрд╝реАрд▓реНрдб рджреНрд╡рд╛рд░рд╛ рдлреЙрд░реНрдо рдлрд╝реАрд▓реНрдб рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдореЗрдВ, рд╣рдо рдлреЙрд░реНрдо рдлрд╝реАрд▓реНрдбреНрд╕ рдХреЛ рдПрдХ рдХреНрд▓реЛрдЬрд░ рд╕реЗ рдкрд╣рдЪрд╛рди рд╕рдХрддреЗ рд╣реИрдВ - рдлреЙрд░реНрдо рдбреЗрдЯрд╛ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЗ рдЗрдЪреНрдЫрд┐рдд рдлрд╝реАрд▓реНрдб рдХреЗ (_.someField) рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдХреНрд▓реЛрдЬрд░ (_.someField) "someField" рдирд╛рдордХ рдлреЙрд░реНрдо рдлрд╝реАрд▓реНрдб рдХреЗ рдЕрдиреБрд░реВрдк рд╣реЛрдЧрд╛ред

рдПрдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЗ рдмрдЬрд╛рдп рдПрдХ рдХреНрд▓реЛрдЬрд░ рдкрд╛рд╕ рдХрд░рдирд╛ рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд▓рд╛рдн рд╣реИ - рдпрд╣ рд╣рдореЗрдВ рдлреЙрд░реНрдо рдлрд╝реАрд▓реНрдб рдХреЗ рдкреНрд░рдХрд╛рд░ рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╣рдо рдХреНрд▓реЛрдЬрд░ рдХреЗ рдкреНрд░рдХрд╛рд░ рдХреЛ рдЬрд╛рдирддреЗ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, UserFormData => String рдХреЗ рдкреНрд░рдХрд╛рд░ рд╕реЗ UserFormData => String рд╣рдо рдпрд╣ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдлреЙрд░реНрдо рдлрд╝реАрд▓реНрдб рдХреЗрд╡рд▓ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдПред

рдкреНрд░рддрд┐рдмрд┐рдВрдм рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдлрд╝реАрд▓реНрдб рдирд╛рдо рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛

рд╕реНрдХрд╛рд▓рд╛ рдореЗрдВ рдлрд╝реАрд▓реНрдб рдирд╛рдо рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдо рд╕реЗ рдХрдо рджреЛ рддрд░реАрдХреЗ рд╣реИрдВ: рдкрд╣рд▓рд╛, рдкреНрд░рддрд┐рдмрд┐рдВрдм рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдкрд╛рд░рдВрдкрд░рд┐рдХ, рджреВрд╕рд░рд╛ рдореИрдХреНрд░реЛрдЬрд╝ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ред рдкреНрд░рддрд┐рдмрд┐рдВрдм рдЗрд╕ рдмрд╛рдд рдореЗрдВ рдЕрдЪреНрдЫрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рд▓рдВрдмреЗ рд╕рдордп рд╕реЗ рд╕реНрдерд╛рдкрд┐рдд, рдмрд▓реНрдХрд┐ рдЕрдкреНрд░рдорд╛рдгрд┐рдд рддрдВрддреНрд░ рд╣реИ, рдЬрдмрдХрд┐ рдореИрдХреНрд░реЛрдЬрд╝ рдПрдХ рдирдИ рд╡рд┐рд╢реЗрд╖рддрд╛ рд╣реИ рдЬреЛ рдПрдХ рдкреНрд░рдпреЛрдЧ рдХреА рд╕реНрдерд┐рддрд┐ рдореЗрдВ рд╣реИ, рдФрд░ рдЗрд╕рдХреЗ рдЙрдкрдпреЛрдЧ рдХреА рдХреБрдЫ рд╕реАрдорд╛рдПрдВ рд╣реИрдВред рд▓реЗрдХрд┐рди рд╡реЗ рдЖрдкрдХреЛ рд╕рдВрдХрд▓рди рдХреЗ рд╕рдордп рдХреНрд╖реЗрддреНрд░ рдХрд╛ рдирд╛рдо рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддреЗ рд╣реИрдВ, рдЬреЛ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдкреНрд▓рд╕ рд╣реИред рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рд╣рдо рдЦреБрдж рдХреЛ рдкреНрд░рддрд┐рдмрд┐рдВрдм рдХреЗ рд▓рд┐рдП рдкреНрд░рддрд┐рдмрдВрдзрд┐рдд рдХрд░рддреЗ рд╣реИрдВ (рд╣рдо рдЕрдЧрд▓реА рдмрд╛рд░ рдореИрдХреНрд░реЛрдЬрд╝ рдХреЛ рдорд┐рд▓реЗрдВрдЧреЗ)ред
рддреЛ, рд╣рдо рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рддрд░реАрдХреЗ рд╕реЗ рдПрдХ рдХреНрд╖реЗрддреНрд░ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рдореЗрдВ рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВ:
 form[FormData](_.string( _.someField )(_.someProperty)) 

рдпрд╛рдиреА _.someField рдХреЛ рдмрдВрдж _.someField "someField" рдирд╛рдо рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВред
рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдорд╛рд░реЛрд╣ рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ:
fieldName[T:Manifest](fieldFoo: T => Any): String
рд╕рдВрдХреЗрддрди [T:Manifest] рдЕрд░реНрде рд╣реИ рдХрд┐ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рдЕрддрд┐рд░рд┐рдХреНрдд, рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд рд╕реВрдЪреА рдХреЛ рд╡рд┐рдзрд┐ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рдПрдЧрд╛, рдЬреЛ рдХрдВрдкрд╛рдЗрд▓рд░ рд╕реЗ рдореИрдирд┐рдлреЗрд╕реНрдЯ [рдЯреА] рдкреНрд░рдХрд╛рд░ рдХреЗ рддрд░реНрдХ рдХреА рдЙрдореНрдореАрдж рдХрд░рддрд╛ рд╣реИред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдпрд╣ JVM рдореЗрдВ рдЯрд╛рдЗрдк рдПрд░рд╛рд╕реБрд░реЗ рдХреЗ рд░реВрдк рдореЗрдВ рдРрд╕реА рдШрдЯрдирд╛ рдкрд░ рдХрд╛рдмреВ рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдмреИрд╕рд╛рдЦреА рд╕реЗ рдЬреНрдпрд╛рджрд╛ рдХреБрдЫ рдирд╣реАрдВ рд╣реИред рдореИрдирд┐рдлрд╝реЗрд╕реНрдЯ рдХреЗ рд╕рд╛рде, рд╣рдореЗрдВ рд░рди рдЯрд╛рдЗрдо рдХреЗ рджреМрд░рд╛рди рдПрдХ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдкреИрд░рд╛рдореАрдЯрд░ (рдЬрд╛рд╡рд╛ рдореЗрдВ рдЬреЗрдиреЗрд░рд┐рдХ рдХреНрд▓рд╛рд╕ рдХреЗ рдЯрд╛рдЗрдк рд╡реЗрд░рд┐рдПрдмрд▓ рдХрд╛ рдПрдирд╛рд▓реЙрдЧ) рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХрд╛ рдЕрд╡рд╕рд░ рдорд┐рд▓рддрд╛ рд╣реИред
рдкреНрд░рдХрдЯ рд╕реЗ рдПрдХ рд╡рд░реНрдЧ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рд╣рдо рдЗрд╕реЗ рдПрдХ рдЧрддрд┐рд╢реАрд▓ рдкреНрд░реЙрдХреНрд╕реА рдкреНрд░реЙрдХреНрд╕реАрдмреЙрдЬ рд╕реЗ proxyObject : рдПрдХ рд╡рд╕реНрддреБ рдЬреЛ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рджрд┐рдП рдЧрдП рдкреНрд░рдХрд╛рд░ рдХреЛ рд╕рдВрддреБрд╖реНрдЯ рдХрд░рддреА рд╣реИ, рдЬрд┐рд╕рдХрд╛ рдПрдХрдорд╛рддреНрд░ рдХрд╛рд░реНрдп рдкрд╣рд▓реЗ рд╡рд┐рдзрд┐ рдХреЗ рдирд╛рдо рдХреЛ рд░рд┐рдкреЛрд░реНрдЯ рдХрд░рдирд╛ рд╣реИред рдпрджрд┐ рдХреНрд▓реЛрдЬрд░ (_: T).someField рдХреЛ fieldFoo: T => Any рд░реВрдк рдореЗрдВ рдкрд╛рд╕ рдХрд┐рдпрд╛ fieldFoo: T => Any , рддреЛ fieldFoo(proxyObject) рдХреЙрд▓ рдХрд░рдиреЗ рд╕реЗ рд╣рдореЗрдВ рд╕реНрдЯреНрд░рд┐рдВрдЧ "someField" ред рдпреЗ рдХрд╛рд░реНрд░рд╡рд╛рдИ рд╕реНрдХрд╛рд▓рд╛-рд░рд┐рдлреНрд▓реЗрдХреНрдЯрд┐рд╡-рдЯреВрд▓реНрд╕ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдореЗрдВ рдХреА рдЧрдИред
рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:
 case class MyClass(fieldA: String) import FieldNameGetter._ assertTrue $[MyClass](_.fieldA) == "fieldA" 

рдЕрдм рд╣рдо рдлреЙрд░реНрдо рдмрд┐рд▓реНрдбрд░ рдХреЛ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдлрд┐рд░ рд╕реЗ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ:
 case class FormBuilderImpl[T:Manifest](fields: Map[String, FieldDescription]) extends FormBuilder[T] with FieldNameGetter { def field[F](fieldFoo: T => F)(foo: FieldBuilder[F] => FieldBuilder[F]) = copy(fields = fields ++ Map($[T](fieldFoo) -> foo(newFieldBuilder[F]).asInstanceOf[FieldBuilderImpl[F]].build)) def string(fieldFoo: T => String)(foo: FieldBuilder[String] => FieldBuilder[String]) = field[String](fieldFoo)(foo andThen (_.fieldType("string"))) ... } 


рдкреНрд░рдкрддреНрд░ рдлрд╝реАрд▓реНрдб рдЖрд╡рд╢реНрдпрдХ рдФрд░ рд╡реИрдХрд▓реНрдкрд┐рдХ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред рд╣рдо рдорд╛рдорд▓реЗ рдХреА рд╢реНрд░реЗрдгреА рдореЗрдВ рдХреНрд╖реЗрддреНрд░ рдХреА рд╡реИрдХрд▓реНрдкрд┐рдХрддрд╛ рдХреА рд╕рдВрдкрддреНрддрд┐ рдХреЛ рджрд░реНрд╢рд╛ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрд╕реА рдХреНрд╖реЗрддреНрд░ рдХреЛ рдЯрд╛рдЗрдк рд╡рд┐рдХрд▓реНрдк рджреЗ [тАж]ред
рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╣рдорд╛рд░реЗ рдлреЙрд░реНрдо рдХреЗ рд▓рд┐рдП, рдПрдХ рдХреЗрд╕ рдХреНрд▓рд╛рд╕ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦ рд╕рдХрддрд╛ рд╣реИ:
 case class RegistrationFormData( name: String, surname: Option[String], email: String, birthDate: Option[Date] ) 

name рдлрд╝реАрд▓реНрдб рдХрд╛ рдЧреЗрдЯреНрдЯрд░ рдкреНрд░рдХрд╛рд░ рд╣реИ RegistrationFormData => String , рдФрд░ surname рдЧреЗрдЯреНрдЯрд░ рдкреНрд░рдХрд╛рд░ рд╣реИ RegistrationFormData => Option[String] ред рддрджрдиреБрд╕рд╛рд░, рдЦреЗрддреЛрдВ рдХреЛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рджреЛ рддрд░реАрдХреЗ рд╣реЛрдиреЗ рдЪрд╛рд╣рд┐рдПред
рдЖрд╡рд╢реНрдпрдХ рдХреЗ рд▓рд┐рдП:
(def string(fieldFoo: T => String)(foo: FieldBuilder[String] => FieldBuilder[String])
рд╡реИрдХрд▓реНрдкрд┐рдХ рдХреЗ рд▓рд┐рдП:
def stringOpt(fieldFoo: T => Option[String])(foo: FieldBuilder[String] => FieldBuilder[String])
рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рдХрд╛рд░ рдХреЗ рдХреНрд╖реЗрддреНрд░ (рдмреВрд▓рд┐рдпрди рдХреЛ рдЫреЛрдбрд╝рдХрд░) рдХреЗ рд▓рд┐рдП, рджреЛрдиреЛрдВ рд╡рд┐рдХрд▓реНрдкреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рд╣рдо string рд╡рд┐рдзрд┐ рдХреЛ рдЕрдиреБрдХреВрд▓рд┐рдд рдХрд░рддреЗ рд╣реИрдВ, рдЗрд╕рдореЗрдВ required рд╕рдВрдкрддреНрддрд┐ рдХреА рдШреЛрд╖рдгрд╛ рдХрд░рддреЗ рд╣реИрдВред рдХрд░реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдореИрдВ рдПрдХ рдХрд╛рдлреА рдХреЙрдореНрдкреИрдХреНрдЯ рдХреЛрдб рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдореЗрдВ рдХрд╛рдордпрд╛рдм рд░рд╣рд╛:

 case class FormBuilderImpl[T: Manifest](fields: Map[String, FieldDescription]) extends FormBuilder[T] with FieldNameGetter { //    : type FieldFoo[F] = FieldBuilder[F] => FieldBuilder[F] type FormField[F] = FieldFoo[F] => FormBuilder[T] //    def string(fieldFoo: T => String): FormField[String] = fieldBase[String](fieldFoo)(_.required) //    def stringOpt(fieldFoo: T => Option[String]): FormField[String] = fieldBase[String](fieldFoo)(identity) def field[F](fieldName: String)(foo: FieldFoo[F]) = copy(fields = fields ++ Map(fieldName -> foo(newFieldBuilder[F]).asInstanceOf[FieldBuilderImpl[F]].build)) def fieldBase[F: Manifest](fieldFoo: T => Any) (innerConfigFoo: FieldFoo[F]) (userConfigFoo: FieldFoo[F]) = field($[T](fieldFoo))(innerConfigFoo andThen (_.fieldType(fieldTypeBy[F])) andThen userConfigFoo) ... } 


рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрд╣рд╛рдВ рд╣рдо рдЯрд╛рдЗрдк рдкреИрд░рд╛рдореАрдЯрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ fieldType рдЯрд╛рдЗрдк рд╕рдВрдкрддреНрддрд┐ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВред

DSL рд╡рд┐рд╕реНрддрд╛рд░

рдлрд┐рд▓рд╣рд╛рд▓, рд╣рдорд╛рд░реЗ FieldBuilder рдХреА рдорджрдж рд╕реЗ рд╣рдо рдХреЗрд╡рд▓ label рдФрд░ required рдЧреБрдгреЛрдВ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдФрд░ рдпрд╣ рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИред рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреЛ рдлрд╝реАрд▓реНрдб рдЧреБрдгреЛрдВ рдХреЗ рд╕реЗрдЯ рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдпрджрд┐ рдХрд╛рд░реНрдп рдХреЛ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╡рд┐рднрд┐рдиреНрди рдкреНрд░рдХрд╛рд░ рдХреЗ рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЛ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдЧреБрдгреЛрдВ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред
рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдирд┐рд╣рд┐рдд рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдФрд░ рдкрд┐рдВрдк рдорд╛рдИ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдкреИрдЯрд░реНрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред

рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╡рд┐рдзрд┐ рдХреЛ FieldBuilder рдЬреЛрдбрд╝реЗрдВ:
def addProperty(key: String, value: Any): FieldBuilder[T]
рдЕрдЧрд▓рд╛, рд╣рдо рдЗрд╕рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕рдВрдкреВрд░реНрдг рдлрд╝реАрд▓реНрдб рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рд╕реЗрдЯ рдХрд░реЗрдВрдЧреЗред рд╣рдорд╛рд░реЗ рдбреАрдПрд╕рдПрд▓ рдХреЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЗрд╕реЗ рд╕реАрдзреЗ рдПрдХреНрд╕реЗрд╕ рдирд╣реАрдВ рдХрд░реЗрдВрдЧреЗ, рдпреВрдЬрд░ рдПрдкреАрдЖрдИ рдлреЙрд░реНрдо рдХреЗ рдирд┐рд╣рд┐рддрд╛рд░реНрде- рдкреНрд░рдХрд╛рд░ рдХрдиреНрд╡рд░реНрдЯрд░реНрд╕ рдмрдирд╛ рджреЗрдЧрд╛:
 object FieldDslExtenders { import FieldAttributes._ implicit class StringFieldBuilderExtender(val fb: FieldBuilder[String]) extends AnyVal { def minLength(length: Int) = fb.addProperty(MinLength, length) def maxLength(length: Int) = fb.addProperty(MaxLength, length) } implicit class SeqFieldBuilderExtender[A](val fb: FieldBuilder[Seq[A]]) extends AnyVal { ... } implicit class DateFieldBuilderExtender(val fb: FieldBuilder[Date]) extends AnyVal { def format(datePattern: String) = fb.addProperty(DatePattern, datePattern) } } 

рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ рдирд┐рд╣рд┐рдд рд╡рд░реНрдЧ AnyVal рд╕реЗ рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ AnyVal ред рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рд╣реИ рддрд╛рдХрд┐ рд░рди рдЯрд╛рдЗрдо рдкрд░ рдЗрд╕ рд░реИрдкрд░ рдХреНрд▓рд╛рд╕ рдХреА рд╡рд╕реНрддреБ рдХреЛ рддрддреНрдХрд╛рд▓ рди рд▓рдЧрд╛рдпрд╛ рдЬрд╛рдП, рд▓реЗрдХрд┐рди рдЗрд╕рдХреЗ рдмрдЬрд╛рдп рд╕реНрдереИрддрд┐рдХ рд╡рд┐рдзрд┐ рдХреЛ рд╕рд╛рдереА рдСрдмреНрдЬреЗрдХреНрдЯ рдкрд░ рдХреЙрд▓ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рд╕рдВрдХрд▓рдХ рджреНрд╡рд╛рд░рд╛ рдмрдирд╛рдпрд╛ рдЬрд╛рдПрдЧрд╛ред

рд╕рдм рдХреБрдЫ рд╣рдорд╛рд░реЗ FormBuilder рд╕рд╛рде рдкрд░рд┐рдкреВрд░реНрдг рдирд╣реАрдВ рд╣реИ: рдЗрд╕рдХреА рдорджрдж рд╕реЗ, рдЖрдк рдЕрднреА рдХреЗ рд▓рд┐рдП рдлрд╝реАрд▓реНрдб рдкреНрд░рдХрд╛рд░реЛрдВ рдХрд╛ рдПрдХ рдмрд╣реБрдд рд╕реАрдорд┐рдд рд╕реЗрдЯ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╣ рд╕реНрдкрд╖реНрдЯ рд╣реИ рдХрд┐ рд╕рднреА рд╕рдмрд╕реЗ рдЖрдо рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЛ рдЬреЛрдбрд╝рдирд╛ рдореБрд╢реНрдХрд┐рд▓ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдХреНрдпрд╛ рд╣реЛрдЧрд╛ рдпрджрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдПрдХ рдирдП рдкреНрд░рдХрд╛рд░ рдХреЗ рдлрд╝реАрд▓реНрдб рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ рдЬреЛ FormBuilder рдореЗрдВ рдирд╣реАрдВ рд╣реИ? рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рд╕рднреА рд╕рдорд╛рди рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд рд╡реЗрдВ рдХрдиреНрд╡рд░реНрдЯрд░реНрд╕ рдХреА рдорджрдж рд╕реЗ рд╣рд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдЬреЛ рдмреЗрд╕ fieldBase рд╡рд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рдЙрдирдХреА рдорджрдж рд╕реЗ, рд╣рдо FormBuilder рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ - рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╣рдо рдПрдХ рддрд┐рдерд┐ рдХреЗ рд╕рд╛рде рдПрдХ рдлрд╝реАрд▓реНрдб рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╡рд┐рдзрд┐ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВ:
 object FormDslExtensions extends FieldNameGetter { val defaultDateFormat = new SimpleDateFormat("dd.MM.yyyy").toPattern import FieldDslExtenders._ implicit class DateFormExtension[T: Manifest](val fb: FormBuilder[T]) extends AnyVal { def dateOpt(fieldFoo: T => Option[Date]) = fb.fieldBase[Date](fieldFoo)(_.format(defaultDateFormat)) _ def date(fieldFoo: T => Date) = fb.fieldBase[Date](fieldFoo)(_.required.format(defaultDateFormat)) _ } } 


рдлреЙрд░реНрдо рдХрд╛ рдЖрдВрддрд░рд┐рдХ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡

рд╣рдорд╛рд░реЗ рдмрд┐рд▓реНрдбрд░реЛрдВ рдХреЗ рдХрд╛рдо рдХреЗ рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк, рд╣рдореЗрдВ рдлреЙрд░реНрдордбреЗрд╕рдХреНрд░рд┐рдкреНрд╢рди рдлреЙрд░реНрдо рдХрд╛ рд╡рд┐рд╡рд░рдг рдСрдмреНрдЬреЗрдХреНрдЯ рдорд┐рд▓рддрд╛ рд╣реИ рдЬрд┐рд╕рдореЗрдВ String -> FieldDescription ред рдмрджрд▓реЗ рдореЗрдВ, FieldDescription рдореЗрдВ рдПрдХ String -> Any рдирдХреНрд╢рд╛ рд╣реЛрддрд╛ рд╣реИред рдЗрд╕ рдкреНрд░рдХрд╛рд░, рд╣рдо рдЙрди рдлрд╝реАрд▓реНрдбреНрд╕ рдХреЛ рдХрд┐рд╕реА рднреА рд╡рд┐рд╢реЗрд╖рддрд╛ рдкрд░ рд╕реЗрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд┐рдирдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛ рд╕рдХрддреА рд╣реИред рдлреЙрд░реНрдо рдХрд╛ рдкрд░рд┐рдгрд╛рдореА рд╡рд┐рд╡рд░рдг, рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдлреНрд░реЗрдорд╡рд░реНрдХ-рдЕрдЬреНрдЮреЗрдпрд╡рд╛рджреА рд╣реИ, рдЕрд░реНрдерд╛рддреНред рд╡рд┐рднрд┐рдиреНрди рдЪреМрдЦрдЯреЗ рдХреЗ рд╕рд╛рде рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдЙрдкрдпреЛрдЧ рдХреА рдЬрд╛рдиреЗ рд╡рд╛рд▓реА рд░реВрдкрд░реЗрдЦрд╛ рдХреЗ рд╕реНрд╡рд░реВрдк рдореЗрдВ рд░реВрдкрд╛рдВрддрд░рдг рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рд╣реИред рдЗрд╕рдХреЗ рдмрд╛рдж, рд╣рдо рджреЗрдЦреЗрдВрдЧреЗ рдХрд┐ рдпрд╣ рдкреНрд▓реЗ рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЗ рд▓рд┐рдП рдХреИрд╕реЗ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред

рдПрдХреАрдХрд░рдг рдЦреЗрд▓реЗрдВ

рдкреНрд▓реЗ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдПрдХ рддрдВрддреНрд░ рд╣реИ рдЬреЛ рдмрд╣реБрдд рдЬрдЯрд┐рд▓ рд░реВрдкреЛрдВ рдХреЗ рд▓рд┐рдП рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдирд╣реАрдВ рд╣реИред рд▓реЗрдХрд┐рди рдЗрд╕рдХреА рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╕реАрдорд╛рдПрдБ рд╣реИрдВ:

рдлрд╝реЙрд░реНрдо рд╕рдмрдорд┐рдЯ рдХрд░рддреЗ рд╕рдордп, Play рдПрдХ HTTP рдЕрдиреБрд░реЛрдз рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИ, рдЗрд╕реЗ deserializes рдФрд░ Map[String, Seq[String]] рд░реВрдк рдореЗрдВ рдЕрдиреБрд░реЛрдз рдкреИрд░рд╛рдореАрдЯрд░ рдкреНрд░рд╕реНрддреБрдд рдХрд░рддрд╛ рд╣реИред рддрдм рдЙрдиреНрд╣реЗрдВ рдорд╛рдиреНрдп рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ рдФрд░ рдХрд╕реНрдЯрдо рдХреЛрдб рдХреЗ рд▓рд┐рдП рдПрдХ рдЙрдкрдпреБрдХреНрдд рджреГрд╢реНрдп рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд рдкреНрд▓реЗ рдЯреВрд▓ рдЖрдкрдХреЛ рдЗрд╕ рдбреЗрдЯрд╛ рдХреЛ рдЯрдкрд▓ рдореЗрдВ рдпрд╛ рдПрдХ рдордирдорд╛рдиреА рд╡рд╕реНрддреБ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддреЗ рд╣реИрдВ - рдЗрд╕рдХреЗ рд▓рд┐рдП, рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЖрдкрдХреЛ рдЗрд╕реЗ рдмрдирд╛рдиреЗ рдХрд╛ рдХрд╛рд░реНрдп рдкреНрд░рджрд╛рди рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдпрджрд┐ рдкреНрд░рдкрддреНрд░ рдореЗрдВ рдкрд░реНрдпрд╛рдкреНрдд рдлрд╝реАрд▓реНрдб рд╣реИрдВ, рддреЛ рдЗрд╕рд╕реЗ рд╕рдВрднрд╛рд╡рд┐рдд рддреНрд░реБрдЯрд┐-рд╕рдореГрджреНрдз рдХреЛрдб рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рдХрд▓реНрдкрдирд╛ рдХрд░реЗрдВ: рдЖрдкрдХреЛ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдХрд┐ 18 рдлрд╝рдВрдХреНрд╢рди рддрд░реНрдХ рд╕рд╣реА рдХреНрд░рдо рдореЗрдВ рд╣реИрдВред

рдХрдЪреНрдЪреЗ рдбреЗрдЯрд╛ рдХреЛ рдорд╛рдиреНрдп рдФрд░ рд░реВрдкрд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдкреНрд▓реЗ рдореИрдкрд┐рдВрдЧ рдХреНрд▓рд╛рд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред
рдкреНрд▓реЗ рдореЗрдВ рдирд┐рд░реНрдорд┐рдд рдкреНрд░рдкрддреНрд░реЛрдВ рдХреА рдореИрдкрд┐рдВрдЧ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рддрд░реНрдХреЛрдВ рдХреЗ рд░реВрдк рдореЗрдВ рдлрд╝реАрд▓реНрдбреНрд╕ рдХреЗ рдореИрдк рдореИрдкрд┐рдВрдЧ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреА рд╣реИ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП: ObjectMapping9 ), рдЗрд╕рд▓рд┐рдП рдПрдХ рдлреЙрд░реНрдо рдореЗрдВ рдЗрд╕рдХреА рдкрд░рд┐рднрд╛рд╖рд╛ рдХреЗ рд╕рдордп рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдлрд╝реАрд▓реНрдб рдХрд╛ рдХреЗрд╡рд▓ рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рд╕реЗрдЯ рд╣реЛ рд╕рдХрддрд╛ рд╣реИред рд╣рдорд╛рд░реА рдХрдХреНрд╖рд╛, рдЬрд┐рд╕реЗ рд╣рдо рдлреЙрд░реНрдордореИрдкрд┐рдВрдЧ рдХрд╣реЗрдВрдЧреЗ , рдПрдХ рдЕрдирд┐рдпрдВрддреНрд░рд┐рдд рдХреНрд╖реЗрддреНрд░ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░ рд╕рдХрддреА рд╣реИред рджреВрд╕рд░реА рдУрд░, рдЗрд╕реЗ рдкрд╛рд╕ рдХрд┐рдП рдЧрдП рдлрд╝реАрд▓реНрдб рдЯрд╛рдЗрдкрд┐рдВрдЧ рдЦреЛ рджреЗрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдпрд╣ рдбрд░рд╛рд╡рдирд╛ рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ FormMapping рдЙрджреНрджреЗрд╢реНрдп рдореИрдиреНрдпреБрдЕрд▓ рд░реВрдк рд╕реЗ рдлрд╝реАрд▓реНрдб рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рдирд╣реАрдВ рд╣реИред рдлрд╝реАрд▓реНрдбреНрд╕ рдХреА рдЯрд╛рдЗрдкрд┐рдВрдЧ рдбреАрдПрд╕рдПрд▓ рджреНрд╡рд╛рд░рд╛ рдЧрд╛рд░рдВрдЯреАрдХреГрдд рд╣реИ, рдФрд░ рдлреЙрд░реНрдо рдбреЗрдЯрд╛ рдСрдмреНрдЬреЗрдХреНрдЯ рдФрд░ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд рдореЗрдВ рд░реВрдкрд╛рдВрддрд░рдг рдкреНрд░рддрд┐рдмрд┐рдВрдм рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд╣реИред

рдкреНрд▓реЗ рдкрд░ рдлреЙрд░реНрдо рдХреЗрд╕ рдХреНрд▓рд╛рд╕ play.api.data.Form рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рджрд░реНрд╢рд╛рдП рдЬрд╛рддреЗ рд╣реИрдВ , рдФрд░ рдлрд╝реАрд▓реНрдб play.api.data.Field рд╣реИрдВ ред рдкреНрд░рдкрддреНрд░реЛрдВ рдФрд░ рдХреНрд╖реЗрддреНрд░реЛрдВ рдХрд╛ рд╣рдорд╛рд░рд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЗрди рд╡рд░реНрдЧреЛрдВ рд╕реЗ рд╡рд┐рд░рд╛рд╕рдд рдореЗрдВ рдорд┐рд▓рд╛ рд╣реЛрдЧрд╛, рдХреНрдпреЛрдВрдХрд┐ рд╣рдореЗрдВ рдкреБрд░рд╛рдиреЗ рдПрдкреАрдЖрдИ рдХреЗ рд╕рд╛рде рд╕рдВрдЧрддрддрд╛ рд╣рд╛рд╕рд┐рд▓ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдПрдХ рдирдпрд╛ attributes рдлрд╝реАрд▓реНрдб рдкреНрд░рдкрддреНрд░ рдлрд╝реАрд▓реНрдб рдореЗрдВ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛ - рдЗрд╕рдХреА рд╕рд╣рд╛рдпрддрд╛ рд╕реЗ рдЕрддрд┐рд░рд┐рдХреНрдд рдкреИрд░рд╛рдореАрдЯрд░ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗред

рдЙрджрд╛рд╣рд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ

рдЖрдХрд╛рд░ рдкрд░рд┐рднрд╛рд╖рд╛:
  PlayFormFactory.form[FormData](_.string(_.name)(_.label("").someAttribute(42)) 


рдлреЙрд░реНрдо рдХреЗ рд╕рд╛рде рдЯреЗрдореНрдкреНрд▓реЗрдЯ:
 @(form: com.naumen.scala.forms.play.ExtendedForm[RegistrationFormData]) <div> .... @myComponent(form(_.name)) .... </div> 

MyComponent рдШрдЯрдХ рдЯреЗрдореНрдкрд▓реЗрдЯ:
 @(customField: com.naumen.scala.forms.play.ExtendedField) <div> .... @customField.ext.attrs("someAttribute") .... </div> 

рдкреНрд░рдкрддреНрд░ рдХрд╛ рдирд┐рд░реНрдзрд╛рд░рдг рдХрд░рдиреЗ рдХреЗ рдЪрд░рдг рдореЗрдВ рдордирдорд╛рдирд╛ рдХреНрд╖реЗрддреНрд░ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рдЖрдкрдХреЛ рдЯреЗрдореНрдкрд▓реЗрдЯ рдореЗрдВ рд▓реЗрдЖрдЙрдЯ рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд░рдиреЗ рдФрд░ рдЧреИрд░-рд▓реЗрдЖрдЙрдЯ рдорд╛рдкрджрдВрдбреЛрдВ рдХреА рдкрд░рд┐рднрд╛рд╖рд╛ рдирд┐рдпрдВрддреНрд░рдХ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддреА рд╣реИред

Source: https://habr.com/ru/post/In206996/


All Articles