mundo-pepino 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +339 -0
- data/History.txt +4 -0
- data/Manifest.txt +21 -0
- data/README.markdown +492 -0
- data/Rakefile +54 -0
- data/cucumber.yml +1 -0
- data/init.rb +1 -0
- data/lib/mundo_pepino.rb +241 -0
- data/lib/mundo_pepino/definitions/es_ES.rb +420 -0
- data/lib/mundo_pepino/resources_history.rb +214 -0
- data/lib/mundo_pepino/version.rb +10 -0
- data/lib/mundo_pepino/visits_history.rb +13 -0
- data/lib/mundo_pepino_es_ES.rb +2 -0
- data/mundo-pepino.gemspec +34 -0
- data/rails_generators/caracteristica/USAGE +16 -0
- data/rails_generators/caracteristica/caracteristica_generator.rb +126 -0
- data/rails_generators/caracteristica/templates/caracteristica.erb +31 -0
- data/rails_generators/mundo_pepino/USAGE +13 -0
- data/rails_generators/mundo_pepino/mundo_pepino_generator.rb +21 -0
- data/rails_generators/mundo_pepino/templates/mundo_pepino.rake +7 -0
- data/rails_generators/mundo_pepino/templates/mundo_pepino_es_ES.rb +47 -0
- metadata +107 -0
@@ -0,0 +1,214 @@
|
|
1
|
+
module MundoPepino
|
2
|
+
module ResourcesHistory
|
3
|
+
module MentionedResource
|
4
|
+
def mr_instance
|
5
|
+
self.is_a?(Array) ? self.first : self
|
6
|
+
end
|
7
|
+
|
8
|
+
def mr_new_record?
|
9
|
+
self.mr_instance.new_record?
|
10
|
+
end
|
11
|
+
|
12
|
+
def mr_model
|
13
|
+
self.mr_instance.class
|
14
|
+
end
|
15
|
+
|
16
|
+
def mr_singular
|
17
|
+
self.mr_model.name.underscore
|
18
|
+
end
|
19
|
+
|
20
|
+
def mr_plural
|
21
|
+
self.mr_model.table_name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class ResourceNotFound < RuntimeError
|
26
|
+
def initialize(resource_info=nil)
|
27
|
+
@resource_info = resource_info && " (#{resource_info})"
|
28
|
+
end
|
29
|
+
def message
|
30
|
+
"Resource not found#{@resource_info}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class WithoutResources < ResourceNotFound
|
35
|
+
def initialize
|
36
|
+
super 'there is no resources'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class NotFoundInDatabase
|
41
|
+
def initialize(model, value='')
|
42
|
+
super "#{model} #{value} not found in database"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class NotMapped < RuntimeError
|
47
|
+
def initialize(type, string)
|
48
|
+
@type = type
|
49
|
+
@string = string
|
50
|
+
end
|
51
|
+
def message
|
52
|
+
"#{@type} not mapped '#{@string}'"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class ModelNotMapped < NotMapped
|
57
|
+
def initialize(string)
|
58
|
+
super('Model', string)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class FieldNotMapped < NotMapped
|
63
|
+
def initialize(string)
|
64
|
+
super('Field', string)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class CrudActionNotMapped < NotMapped
|
69
|
+
def initialize(string)
|
70
|
+
super('CRUD Action', string)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# options: :force_creation
|
75
|
+
def add_resource(model, attribs=[], options = {})
|
76
|
+
attributes = if attribs.is_a?(Hash)
|
77
|
+
[ attribs ]
|
78
|
+
else
|
79
|
+
attribs
|
80
|
+
end
|
81
|
+
res = if attributes.size == 1
|
82
|
+
find_or_create(model, attributes.first, options)
|
83
|
+
else
|
84
|
+
attributes.map do |hash|
|
85
|
+
find_or_create(model, hash, options)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
pile_up res
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_resource_from_database(modelo, nombre)
|
92
|
+
model = modelo.to_unquoted.to_model
|
93
|
+
field = field_for(model, 'nombre')
|
94
|
+
if resource = model.send("find_by_#{field}", nombre)
|
95
|
+
pile_up resource
|
96
|
+
else
|
97
|
+
NotFoundInDatabase.new(model, name)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def pile_up(mentioned)
|
102
|
+
@resources ||= []
|
103
|
+
if mentioned != last_mentioned
|
104
|
+
mentioned.class.send :include, MentionedResource
|
105
|
+
@resources.unshift mentioned
|
106
|
+
end
|
107
|
+
mentioned
|
108
|
+
end
|
109
|
+
|
110
|
+
def last_mentioned
|
111
|
+
@resources && @resources.first
|
112
|
+
end
|
113
|
+
|
114
|
+
def last_mentioned_url
|
115
|
+
if mentioned = last_mentioned
|
116
|
+
if mentioned.mr_new_record?
|
117
|
+
eval("#{mentioned.mr_plural}_path")
|
118
|
+
else
|
119
|
+
eval("#{mentioned.mr_singular}_path(mentioned.mr_instance)")
|
120
|
+
end
|
121
|
+
else
|
122
|
+
raise WithoutResources
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def last_mentioned_of(modelo, with_name = nil)
|
127
|
+
if model = modelo.to_model
|
128
|
+
resource = if with_name
|
129
|
+
detect_first @resources.flatten, [model, with_name]
|
130
|
+
elsif(last_mentioned.mr_model == model)
|
131
|
+
last_mentioned
|
132
|
+
else
|
133
|
+
if group = recursive_group_search(model, @resources[1..-1])
|
134
|
+
group
|
135
|
+
else
|
136
|
+
detect_first @resources.flatten, model
|
137
|
+
end
|
138
|
+
end
|
139
|
+
resource || raise(ResourceNotFound.new("model:#{model.name}, name:#{with_name||'nil'}"))
|
140
|
+
else
|
141
|
+
raise ModelNotMapped.new(modelo)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def last_mentioned_called(name)
|
146
|
+
detect_first @resources.flatten, name
|
147
|
+
end
|
148
|
+
|
149
|
+
def recursive_group_search(model, resources)
|
150
|
+
if lm = resources.shift
|
151
|
+
if(lm.is_a?(Array) and (lm.mr_model == model))
|
152
|
+
lm
|
153
|
+
else
|
154
|
+
recursive_group_search(model, resources)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def detect_first(arr, value, method = nil)
|
160
|
+
if value.is_a? String
|
161
|
+
method ||= :name
|
162
|
+
arr.detect { |r| r.respond_to?(method) && (r.send(method) =~ /#{value}/i) }
|
163
|
+
elsif value.is_a? Class
|
164
|
+
method ||= :is_a?
|
165
|
+
arr.detect { |r| r.respond_to?(method) && r.send(method, value) }
|
166
|
+
elsif value.is_a? Array
|
167
|
+
model, val = value # [ class, value ]
|
168
|
+
name_field = field_for(model, 'nombre')
|
169
|
+
arr.detect do |r|
|
170
|
+
r.respond_to?(:is_a?) && r.is_a?(model) && r.send(name_field) =~ /#{val}/i
|
171
|
+
end
|
172
|
+
else
|
173
|
+
method ||= :id
|
174
|
+
arr.detect { |r| r.respond_to?(method) && r.send(method) == value }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def resources_array_field_and_values(mentioned, campo, valor)
|
179
|
+
resources, valores = if mentioned.is_a?(Array)
|
180
|
+
valores = valor.split(/ ?, | y /)
|
181
|
+
if valores.size == mentioned.size
|
182
|
+
[mentioned, valores]
|
183
|
+
else
|
184
|
+
[mentioned, [ valor ] * mentioned.size]
|
185
|
+
end
|
186
|
+
else
|
187
|
+
[[ mentioned ], [ valor ]]
|
188
|
+
end
|
189
|
+
field, values = if (child_model = campo.to_model)
|
190
|
+
child_name_field = field_for(mentioned.mr_model, 'nombre')
|
191
|
+
values = add_resource(child_model,
|
192
|
+
valores.map { |val| { child_name_field => val } })
|
193
|
+
values = [ values ] unless values.is_a?(Array)
|
194
|
+
[ campo.to_field || child_model.name.underscore, values ]
|
195
|
+
else
|
196
|
+
[ field_for(mentioned.mr_model, campo), valores ]
|
197
|
+
end
|
198
|
+
[resources, field, values]
|
199
|
+
end
|
200
|
+
|
201
|
+
def method_missing(method, *args, &block)
|
202
|
+
if (method.to_s =~ /^last_mentioned_(.+)$/)
|
203
|
+
if mentioned = last_mentioned
|
204
|
+
last_mentioned.send("mr_#{$1}")
|
205
|
+
else
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
else
|
209
|
+
super
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = %q{mundo-pepino}
|
6
|
+
s.version = "0.1.0"
|
7
|
+
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
|
+
s.authors = ["Fernando García Samblas"]
|
10
|
+
s.autorequire = %q{mundo-pepino}
|
11
|
+
s.date = %q{2009-09-30}
|
12
|
+
s.email = %q{fernando.garcia@the-cocktail.com}
|
13
|
+
s.files = ["History.txt", "COPYING", "README.markdown"] + FileList['lib/**/*.rb', 'rails_generators/**/*'].to_a
|
14
|
+
s.homepage = %q{http://github.com/nando/mundo-pepino}
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.rubygems_version = %q{1.2.0}
|
17
|
+
s.summary = %q{Convention over self-implementation for 'cucumber --language=es features' writers}
|
18
|
+
|
19
|
+
if s.respond_to? :specification_version then
|
20
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
21
|
+
s.specification_version = 2
|
22
|
+
|
23
|
+
if current_version >= 3 then
|
24
|
+
s.add_runtime_dependency(%q<cucumber>, ["<= 0.3.101"])
|
25
|
+
s.add_runtime_dependency(%q<nando-string-mapper>, [">= 0.0.1"])
|
26
|
+
else
|
27
|
+
s.add_dependency(%q<cucumber>, ["<= 0.3.101"])
|
28
|
+
s.add_dependency(%q<nando-string-mapper>, [">= 0.0.1"])
|
29
|
+
end
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<cucumber>, ["<= 0.3.101"])
|
32
|
+
s.add_dependency(%q<nando-string-mapper>, [">= 0.0.1"])
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
Description:
|
2
|
+
Generates a skeleton for a new feature file written in Spanish (característica) that
|
3
|
+
describes an standard resource management (currently only C and D of the CRUD).
|
4
|
+
This generator should be used with moderation.
|
5
|
+
See http://github.com/aslakhellesoy/cucumber/wikis/feature-coupled-steps-antipattern
|
6
|
+
for details about the dangers involved.
|
7
|
+
|
8
|
+
It takes at least two arguments: the name of a model and its translation to Spanish.
|
9
|
+
|
10
|
+
This generator can take an optional list of attribute pairs similar to Rails'
|
11
|
+
built-in resource generator but adding also the field Spanish translation after the
|
12
|
+
field type (see an example below).
|
13
|
+
|
14
|
+
Examples:
|
15
|
+
`./script/generate caracteristica Post Artículo` # no attributes
|
16
|
+
`./script/generate caracteristica Post Artículo title:string:título body:string:cuerpo published:boolean:publicado`
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# Este generador crea una plantilla de caracteristica (feature) ligada a un modelo
|
2
|
+
module Rails::Generator::Commands
|
3
|
+
MUNDO_PEPINO_ENV = 'features/step_definitions/mundo_pepino_es_ES.rb'
|
4
|
+
|
5
|
+
MODEL_CLEANING = '"\n # ENTRADA AUTO-GENERADA PARA #{model}\n' +
|
6
|
+
' #{model}, # ' +
|
7
|
+
'(TODO: quitar la coma final si es el primer modelo)\n"'
|
8
|
+
|
9
|
+
MODEL_MAPPING = '"\n # MAPEO DE MODELO AUTO-GENERADO (#{model})\n' +
|
10
|
+
' /^#{regexp}$/i => #{model},' +
|
11
|
+
' # (TODO: validar RegExp para forma plural y coma final)\n"'
|
12
|
+
|
13
|
+
FIELD_MAPPING = '"\n # MAPEO DE CAMPO AUTO-GENERADO (#{field})\n' +
|
14
|
+
' /^#{regexp}$/i => :#{field},' +
|
15
|
+
' # (TODO: validar RegExp para forma plural y coma final)\n"'
|
16
|
+
|
17
|
+
class Create < Base
|
18
|
+
def mp_model_cleaning(model)
|
19
|
+
add_to_mundo_pepino_env "MundoPepino::ModelsToClean = [", eval(MODEL_CLEANING)
|
20
|
+
logger.model_cleaning "added #{model} (#{model}.destroy_all call before each scenario)"
|
21
|
+
end
|
22
|
+
|
23
|
+
def mp_model_mapping(model, regexp)
|
24
|
+
add_to_mundo_pepino_env 'String.model_mappings = {', eval(MODEL_MAPPING)
|
25
|
+
logger.model_mapping " added /^#{regexp}$/i => #{model}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def mp_field_mapping(field, regexp)
|
29
|
+
add_to_mundo_pepino_env 'String.field_mappings = {', eval(FIELD_MAPPING)
|
30
|
+
logger.field_mapping " added /^#{regexp}$/i => :#{field}"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def add_to_mundo_pepino_env(sentinel, content)
|
35
|
+
unless options[:pretend]
|
36
|
+
gsub_file MUNDO_PEPINO_ENV, /(#{Regexp.escape(sentinel)})/mi do |match|
|
37
|
+
"#{match}#{content}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Destroy < RewindBase
|
44
|
+
def mp_model_cleaning(model)
|
45
|
+
remove_from_mundo_pepino_env eval(MODEL_CLEANING)
|
46
|
+
logger.model_cleaning "removing Before { #{model}.destroy_all }"
|
47
|
+
end
|
48
|
+
|
49
|
+
def mp_model_mapping(model, regexp)
|
50
|
+
remove_from_mundo_pepino_env eval(MODEL_MAPPING)
|
51
|
+
logger.model_mapping " removing /^#{regexp}$/i => #{model}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def mp_field_mapping(field, regexp)
|
55
|
+
remove_from_mundo_pepino_env eval(FIELD_MAPPING)
|
56
|
+
logger.model_mapping " removing /^#{regexp}$/i => #{field}"
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def remove_from_mundo_pepino_env(content)
|
61
|
+
unless options[:pretend]
|
62
|
+
gsub_file MUNDO_PEPINO_ENV, /(#{Regexp.escape(content)})/mi, ''
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class CaracteristicaGenerator < Rails::Generator::NamedBase
|
69
|
+
attr_reader :modelo_en_singular, :campos
|
70
|
+
|
71
|
+
def manifest
|
72
|
+
record do |m|
|
73
|
+
if args.any?
|
74
|
+
@modelo_en_singular = args.shift
|
75
|
+
m.mp_model_cleaning class_name
|
76
|
+
m.mp_model_mapping class_name, plural_regexp(modelo_en_singular)
|
77
|
+
named_args.each do |arg|
|
78
|
+
m.mp_field_mapping arg.name, plural_regexp(arg.nombre)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
m.template 'caracteristica.erb',
|
82
|
+
"features/gestion_de_#{modelo_en_plural.downcase}.feature"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def named_args
|
87
|
+
@named_args ||= args.map{|arg| NamedArg.new(arg)}
|
88
|
+
end
|
89
|
+
|
90
|
+
def modelo_en_plural
|
91
|
+
modelo_en_singular + (modelo_en_singular =~ /[aeiou]$/i ? 's' : 'es')
|
92
|
+
end
|
93
|
+
|
94
|
+
def plural_regexp(nombre)
|
95
|
+
suffix = if nombre =~ /[aeiou]$/i
|
96
|
+
's?'
|
97
|
+
else
|
98
|
+
'(es)?'
|
99
|
+
end
|
100
|
+
nombre.downcase + suffix
|
101
|
+
end
|
102
|
+
|
103
|
+
class NamedArg
|
104
|
+
attr_reader :name, :nombre
|
105
|
+
|
106
|
+
def initialize(s)
|
107
|
+
@name, @type, @nombre = *s.split(':')
|
108
|
+
end
|
109
|
+
|
110
|
+
def value(n=0)
|
111
|
+
if @type == 'boolean'
|
112
|
+
(n % 2) == 0
|
113
|
+
elsif @type == 'integer'
|
114
|
+
n
|
115
|
+
else
|
116
|
+
"#{@nombre} #{n}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
protected
|
122
|
+
|
123
|
+
def banner
|
124
|
+
"Usage: #{$0} caracteristica ModelName NombreDelModelo [field:type:campo, field:type:campo]"
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
Característica: Gestión de <%= modelo_en_plural %>
|
2
|
+
Para [beneficio]
|
3
|
+
Como [sujeto]
|
4
|
+
Quiero [característica/comportamiento]
|
5
|
+
|
6
|
+
Escenario: Añadir un/a nuevo/a <%= modelo_en_singular %>
|
7
|
+
Dado que visito la página de nuevo/a <%= modelo_en_singular %>
|
8
|
+
<% keyword = 'Cuando' -%>
|
9
|
+
<% named_args.each do |arg| -%>
|
10
|
+
<%= keyword %> relleno <%= arg.nombre %> con "<%= arg.value %>"
|
11
|
+
<% keyword = ' Y' -%>
|
12
|
+
<% end -%>
|
13
|
+
Y pulso el botón "Crear"
|
14
|
+
<% keyword = 'Entonces' -%>
|
15
|
+
<% named_args.each do |arg| -%>
|
16
|
+
<%= keyword %> debería ver el texto "<%= arg.value %>"
|
17
|
+
<% keyword = ' Y' -%>
|
18
|
+
<% end -%>
|
19
|
+
|
20
|
+
Escenario: Borrar <%= modelo_en_singular %>
|
21
|
+
Dado que tenemos los/las siguientes <%= modelo_en_plural %>:
|
22
|
+
|<%= named_args.map(&:nombre).join('|') %>|
|
23
|
+
<% (1..4).each do |n| -%>
|
24
|
+
|<%= named_args.map{ |arg| arg.value(n) }.join('|') %>|
|
25
|
+
<% end -%>
|
26
|
+
Cuando borro el/la <%= modelo_en_singular %> en la tercera posición
|
27
|
+
Entonces debería ver una tabla con los siguientes contenidos:
|
28
|
+
|<%= named_args.map(&:nombre).join('|') %>|
|
29
|
+
<% [1,2,4].each do |n| -%>
|
30
|
+
|<%= named_args.map{ |arg| arg.value(n) }.join('|') %>|
|
31
|
+
<% end -%>
|