mundo-pepino 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,10 @@
1
+ module MundoPepino #:nodoc:
2
+ class VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+ PATCH = nil # Set to nil for official release
7
+
8
+ STRING = [MAJOR, MINOR, TINY, PATCH].compact.join('.')
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module MundoPepino
2
+ module VisitsHistory
3
+ def do_visit(url)
4
+ @visits ||= []
5
+ @visits << url
6
+ visit url
7
+ end
8
+
9
+ def last_visited
10
+ @visits.last
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,2 @@
1
+ require 'mundo_pepino'
2
+ require 'mundo_pepino/definitions/es_ES'
@@ -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 -%>