smartdown 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -127,6 +127,21 @@ country slugs/names.
127
127
  [date: baby_due_date]
128
128
  ```
129
129
 
130
+ To control the range of years selected you can supply 2 optional arguments to date questions: `from` and `to`.
131
+ These can take the form of absolute values, eg.
132
+
133
+ ```markdown
134
+ [date: baby_due_date, from: 2010, to: 2015]
135
+ ```
136
+
137
+ Or relative values (from the current year), eg.
138
+
139
+ ```markdown
140
+ [date: baby_due_date, from: -4, to: 1]
141
+ ```
142
+
143
+ The default values for `from` and `to` are relative years: `-1` and `3` respectively.
144
+
130
145
  ### Text
131
146
 
132
147
  ```markdown
@@ -3,6 +3,39 @@ require 'smartdown/api/question'
3
3
  module Smartdown
4
4
  module Api
5
5
  class DateQuestion < Question
6
+ extend Forwardable
7
+ DEFAULT_START_YEAR = Time.now.year - 1
8
+ DEFAULT_END_YEAR = Time.now.year + 3
9
+
10
+ def start_year
11
+ parse_date(from) || DEFAULT_START_YEAR
12
+ end
13
+
14
+ def end_year
15
+ parse_date(to) || DEFAULT_END_YEAR
16
+ end
17
+
18
+ private
19
+
20
+ delegate [:to, :from] => :question_element
21
+
22
+ def question_element
23
+ @question_element ||= elements.find{|element| element.is_a? Smartdown::Model::Element::Question::Date}
24
+ end
25
+
26
+ def parse_date(date_string)
27
+ return nil unless date_string
28
+ year = date_string.to_i
29
+ if is_fixed_year?(year)
30
+ year
31
+ else
32
+ Time.now.year + year
33
+ end
34
+ end
35
+
36
+ def is_fixed_year?(int)
37
+ int.abs >= 1000
38
+ end
6
39
  end
7
40
  end
8
41
  end
@@ -41,7 +41,10 @@ module Smartdown
41
41
  def interpolate(text, state)
42
42
  text.to_s.gsub(/%{([^}]+)}/) do |_|
43
43
  term = resolve_term($1, state)
44
- term.respond_to?(:humanize) ? term.humanize : term
44
+ if term.is_a?(Smartdown::Model::Answer::Base)
45
+ term = term.humanize
46
+ end
47
+ term
45
48
  end
46
49
  end
47
50
 
@@ -4,7 +4,7 @@ module Smartdown
4
4
  module Model
5
5
  module Element
6
6
  module Question
7
- class Date < Struct.new(:name, :alias)
7
+ class Date < Struct.new(:name, :from, :to, :alias)
8
8
  def answer_type
9
9
  Smartdown::Model::Answer::Date
10
10
  end
@@ -13,7 +13,11 @@ module Smartdown
13
13
  end
14
14
 
15
15
  def to_s(full = true)
16
- "Parse error in '#{filename}':\n\n" + @parse_error.cause.ascii_tree
16
+ position = parse_error.cause.pos
17
+ line, column = parse_error.cause.source.line_and_column(position)
18
+
19
+ "Parse error in file:'#{filename}' line:'#{line}' column:'#{column}'" +
20
+ "\n\n" + parse_error.cause.ascii_tree
17
21
  end
18
22
  end
19
23
 
@@ -7,7 +7,7 @@ require 'smartdown/parser/node_transform'
7
7
  module Smartdown
8
8
  module Parser
9
9
  class NodeInterpreter
10
- attr_reader :name, :source
10
+ attr_reader :name, :source, :reporter
11
11
 
12
12
  def initialize(name, source, options = {})
13
13
  @name = name
@@ -15,10 +15,11 @@ module Smartdown
15
15
  data_module = options.fetch(:data_module, {})
16
16
  @parser = options.fetch(:parser, Smartdown::Parser::NodeParser.new)
17
17
  @transform = options.fetch(:transform, Smartdown::Parser::NodeTransform.new(data_module))
18
+ @reporter = options.fetch(:reporter, Parslet::ErrorReporter::Deepest.new)
18
19
  end
19
20
 
20
21
  def interpret
21
- transform.apply(parser.parse(source),
22
+ transform.apply(parser.parse(source, reporter: reporter),
22
23
  node_name: name
23
24
  )
24
25
  end
@@ -102,9 +102,12 @@ module Smartdown
102
102
  }
103
103
 
104
104
  rule(:date => {identifier: simple(:identifier), :option_pairs => subtree(:option_pairs)}) {
105
+ transformed_option_pairs = Smartdown::Parser::OptionPairs.transform(option_pairs)
105
106
  Smartdown::Model::Element::Question::Date.new(
106
107
  identifier.to_s,
107
- Smartdown::Parser::OptionPairs.transform(option_pairs).fetch('alias', nil)
108
+ transformed_option_pairs.fetch('from', nil),
109
+ transformed_option_pairs.fetch('to', nil),
110
+ transformed_option_pairs.fetch('alias', nil),
108
111
  )
109
112
  }
110
113
 
@@ -45,6 +45,7 @@ module Smartdown
45
45
  equality_predicate.as(:equality_predicate) |
46
46
  set_membership_predicate.as(:set_membership_predicate) |
47
47
  comparison_predicate.as(:comparison_predicate) |
48
+ function_predicate.as(:function_predicate) |
48
49
  otherwise_predicate |
49
50
  named_predicate
50
51
  }
@@ -68,7 +69,6 @@ module Smartdown
68
69
 
69
70
  rule (:predicates) {
70
71
  combined_predicate.as(:combined_predicate) |
71
- function_predicate.as(:function_predicate) |
72
72
  predicate
73
73
  }
74
74
 
@@ -1,3 +1,3 @@
1
1
  module Smartdown
2
- VERSION = "0.9.0"
2
+ VERSION = "0.10.0"
3
3
  end
@@ -0,0 +1,71 @@
1
+ require 'smartdown/api/date_question'
2
+ require 'smartdown/model/element/question/date'
3
+
4
+ describe Smartdown::Api::DateQuestion do
5
+ before do
6
+ Timecop.freeze(Time.local(2014))
7
+ end
8
+
9
+ after do
10
+ Timecop.return
11
+ end
12
+
13
+ subject(:date_question) { Smartdown::Api::DateQuestion.new(elements) }
14
+ let(:elements) { [ date_question_element ] }
15
+ let(:date_question_element) {
16
+ Smartdown::Model::Element::Question::Date.new(name, *args)
17
+ }
18
+ let(:aliaz) { nil }
19
+ let(:args) {[start_year, end_year, aliaz]}
20
+
21
+
22
+ context "no to/from provided" do
23
+ let(:name) { 'year_of_emancipation' }
24
+ let(:start_year) { nil }
25
+ let(:end_year) { nil }
26
+
27
+ describe "#start_year" do
28
+ it 'returns good default' do
29
+ expect(date_question.start_year).to eq(Time.now.year - 1)
30
+ end
31
+ end
32
+
33
+ describe "#end_year" do
34
+ it 'returns good default' do
35
+ expect(date_question.end_year).to eq(Time.now.year + 3)
36
+ end
37
+ end
38
+ end
39
+
40
+ context "with to/from provided" do
41
+ context "Fixed years" do
42
+ let(:name) { 'year_of_incarceration_for_possession_of_alcohol' }
43
+ let(:start_year) { '1920' }
44
+ let(:end_year) { '1933' }
45
+
46
+ describe "start_year" do
47
+ specify { expect(date_question.start_year).to eq(start_year.to_i) }
48
+ end
49
+
50
+ describe "end_year" do
51
+ specify { expect(date_question.end_year).to eq(end_year.to_i) }
52
+ end
53
+ end
54
+
55
+ context "Relative years" do
56
+ let(:name) { 'year_of_incarceration_for_practicing_witchcraft' }
57
+ let(:start_year) { '-279' }
58
+ let(:end_year) { '-63' }
59
+
60
+ describe "start_year" do
61
+ specify { expect(date_question.start_year).to eq(1735) }
62
+ end
63
+
64
+ describe "end_year" do
65
+ specify { expect(date_question.end_year).to eq(1951) }
66
+ end
67
+
68
+ end
69
+ end
70
+
71
+ end
@@ -27,6 +27,38 @@ describe Smartdown::Parser::Element::DateQuestion do
27
27
  end
28
28
  end
29
29
 
30
+ context "with question tag and a date range with years" do
31
+ let(:source) { "[date: date_of_birth, from: 1980, to: -2]" }
32
+
33
+ it "parses" do
34
+ should parse(source).as(
35
+ date: {
36
+ identifier: "date_of_birth",
37
+ option_pairs: [
38
+ {
39
+ key: 'from',
40
+ value: '1980',
41
+ },
42
+ {
43
+ key: 'to',
44
+ value: '-2',
45
+ }
46
+ ]
47
+
48
+ }
49
+ )
50
+ end
51
+
52
+ describe "transformed" do
53
+ let(:node_name) { "my_node" }
54
+ subject(:transformed) {
55
+ Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
56
+ }
57
+
58
+ it { should eq(Smartdown::Model::Element::Question::Date.new("date_of_birth", '1980', '-2')) }
59
+ end
60
+ end
61
+
30
62
  context "with question tag and an alias" do
31
63
  let(:source) { "[date: date_of_birth, alias: date_for_adoption_or_birth]" }
32
64
 
@@ -50,7 +82,7 @@ describe Smartdown::Parser::Element::DateQuestion do
50
82
  Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
51
83
  }
52
84
 
53
- it { should eq(Smartdown::Model::Element::Question::Date.new("date_of_birth", "date_for_adoption_or_birth")) }
85
+ it { should eq(Smartdown::Model::Element::Question::Date.new("date_of_birth", nil, nil, "date_for_adoption_or_birth")) }
54
86
  end
55
87
  end
56
88
  end
@@ -82,9 +82,9 @@ describe Smartdown::Parser::Predicates do
82
82
  describe "predicate AND predicate" do
83
83
  subject(:parser) { described_class.new }
84
84
 
85
- it { should parse("my_pred? AND my_other_pred?").as(
85
+ it { should parse("my_pred() AND my_other_pred?").as(
86
86
  { combined_predicate: {
87
- first_predicate: { named_predicate: "my_pred?" },
87
+ first_predicate: { function_predicate: { name: "my_pred" } },
88
88
  and_predicates:
89
89
  [
90
90
  {named_predicate: "my_other_pred?"},
@@ -107,16 +107,16 @@ describe Smartdown::Parser::Predicates do
107
107
 
108
108
  describe "transformed" do
109
109
  let(:node_name) { "my_node" }
110
- let(:source) { "my_pred? AND my_other_pred?" }
110
+ let(:source) { "my_pred() AND my_other_pred?" }
111
111
  subject(:transformed) {
112
112
  Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
113
113
  }
114
114
 
115
115
  it { should eq(Smartdown::Model::Predicate::Combined.new(
116
116
  [
117
- Smartdown::Model::Predicate::Named.new("my_pred?"),
117
+ Smartdown::Model::Predicate::Function.new("my_pred", []),
118
118
  Smartdown::Model::Predicate::Named.new("my_other_pred?")
119
- ]
119
+ ]
120
120
  )) }
121
121
  end
122
122
  end
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,7 @@ $LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
3
3
  require 'pathname'
4
4
  require 'parslet/rig/rspec'
5
5
  require 'support/model_builder'
6
+ require 'timecop'
6
7
 
7
8
  RSpec.configure do |config|
8
9
  if config.files_to_run.one?
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smartdown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2014-07-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parslet
16
- requirement: &9477660 !ruby/object:Gem::Requirement
16
+ requirement: &18204100 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.6.1
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *9477660
24
+ version_requirements: *18204100
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &9476740 !ruby/object:Gem::Requirement
27
+ requirement: &18232520 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 3.0.0
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *9476740
35
+ version_requirements: *18232520
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rake
38
- requirement: &9475460 !ruby/object:Gem::Requirement
38
+ requirement: &18231800 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *9475460
46
+ version_requirements: *18231800
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: gem_publisher
49
- requirement: &9474100 !ruby/object:Gem::Requirement
49
+ requirement: &18230980 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,18 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *9474100
57
+ version_requirements: *18230980
58
+ - !ruby/object:Gem::Dependency
59
+ name: timecop
60
+ requirement: &18230000 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *18230000
58
69
  description:
59
70
  email: david.heath@digital.cabinet-office.gov.uk
60
71
  executables:
@@ -176,6 +187,7 @@ files:
176
187
  - spec/support/flow_input_interface.rb
177
188
  - spec/support/model_builder.rb
178
189
  - spec/api/state_spec.rb
190
+ - spec/api/date_question_spec.rb
179
191
  - spec/api/previous_question_spec.rb
180
192
  - spec/spec_helper.rb
181
193
  - spec/engine/state_spec.rb
@@ -252,7 +264,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
252
264
  version: '0'
253
265
  segments:
254
266
  - 0
255
- hash: -2036017641587620978
267
+ hash: 508907290184423365
256
268
  required_rubygems_version: !ruby/object:Gem::Requirement
257
269
  none: false
258
270
  requirements:
@@ -261,7 +273,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
261
273
  version: '0'
262
274
  segments:
263
275
  - 0
264
- hash: -2036017641587620978
276
+ hash: 508907290184423365
265
277
  requirements: []
266
278
  rubyforge_project:
267
279
  rubygems_version: 1.8.11
@@ -295,6 +307,7 @@ test_files:
295
307
  - spec/support/flow_input_interface.rb
296
308
  - spec/support/model_builder.rb
297
309
  - spec/api/state_spec.rb
310
+ - spec/api/date_question_spec.rb
298
311
  - spec/api/previous_question_spec.rb
299
312
  - spec/spec_helper.rb
300
313
  - spec/engine/state_spec.rb