fulcrum 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,16 @@
1
+ module Fulcrum
2
+ class Member < Api
3
+
4
+ class << self
5
+
6
+ def all(opts = {})
7
+ params = parse_opts([:page], opts)
8
+ call(:get, 'members.json', params)
9
+ end
10
+
11
+ def find(id)
12
+ call(:get, "members/#{id}.json")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ module Fulcrum
2
+ class Photo < Api
3
+
4
+ ALLOWED_FORMATS = %w(json image)
5
+
6
+ class << self
7
+
8
+ def find(id, opts = {})
9
+ format = image_opts(opts)
10
+ call(:get, "photos/#{id}.#{format}")
11
+ end
12
+
13
+ def thumbnail(id, opts = {})
14
+ format = image_opts(opts)
15
+ call(:get, "photos/#{id}/thumbnail.#{format}")
16
+ end
17
+
18
+ def create(file, content_type, id, label = '')
19
+ photo = Faraday::UploadIO.new(file, content_type)
20
+ call(:post, 'photos.json', { photo: { file: photo, access_key: id, label: label}})
21
+ end
22
+
23
+ def delete(id)
24
+ call(:delete, "photos/#{id}.json")
25
+ end
26
+
27
+ def image_opts(opts = {})
28
+ opts = opts.with_indifferent_access
29
+ format = opts.delete(:format) || 'json'
30
+ raise ArgumentError, "#{format} is not an allowed format, use either 'json' or 'image'" unless ALLOWED_FORMATS.include?(format)
31
+ format = "jpg" if format == 'image'
32
+ format
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ module Fulcrum
2
+ class Record < Api
3
+
4
+ class << self
5
+
6
+ def all(opts = {})
7
+ params = parse_opts([:page, :form_id, :bounding_box, :updated_since], opts)
8
+ call(:get, 'records.json', params)
9
+ end
10
+
11
+ def find(id)
12
+ call(:get, "records/#{id}.json")
13
+ end
14
+
15
+ def create(record)
16
+ validation = RecordValidator.new(record)
17
+ if validation.valid?
18
+ call(:post, 'records.json', record)
19
+ else
20
+ { error: { validation: validation.errors } }
21
+ end
22
+ end
23
+
24
+ def update(id, record)
25
+ validation = RecordValidator.new(record)
26
+ if validation.valid?
27
+ call(:put, "records/#{id}.json", record)
28
+ else
29
+ { error: { validation: validation.errors } }
30
+ end
31
+ end
32
+
33
+ def delete(id)
34
+ call(:delete, "records/#{id}.json")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ require 'active_support/core_ext/hash'
2
+
3
+ module Fulcrum
4
+ class BaseValidator
5
+ attr_accessor :data
6
+ attr_accessor :errors
7
+
8
+ def initialize(data)
9
+ @data = (data.is_a?(Hash) ? data : JSON.parse(data)).with_indifferent_access
10
+ @errors = {}
11
+ validate!
12
+ end
13
+
14
+ def valid?
15
+ errors.empty?
16
+ end
17
+
18
+ def add_error(key, data_name, error)
19
+ if errors.has_key?(key)
20
+ if errors[key].has_key?(data_name)
21
+ errors[key][data_name].push(error)
22
+ else
23
+ errors[key][data_name] = [error]
24
+ end
25
+ else
26
+ errors[key] = {}
27
+ errors[key][data_name] = [error]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ module Fulcrum
2
+ class ChoiceListValidator < BaseValidator
3
+
4
+ def validate!
5
+ if data['choice_list'].kind_of?(Hash) && !data['choice_list'].blank?
6
+ add_error('choice_list', 'name', 'must not be blank') if data['choice_list']['name'].blank?
7
+ choices(data['choice_list']['choices'])
8
+ else
9
+ @errors['choice_list'] = ['must be a non-empty hash']
10
+ end
11
+ return valid?
12
+ end
13
+
14
+ def choices(elements)
15
+ if elements.kind_of?(Array) && !elements.empty?
16
+ elements.each do |choice|
17
+ if choice.blank?
18
+ add_error('choices', 'choice', 'cannot be empty')
19
+ else
20
+ if choice['label'].blank?
21
+ add_error('choice', 'label', 'is required')
22
+ end
23
+ end
24
+ end
25
+ else
26
+ add_error('choice_list', 'choices', 'must be a non-empty array')
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ module Fulcrum
2
+ class ClassificationSetValidator < BaseValidator
3
+
4
+ def validate!
5
+ if data['classification_set']
6
+ add_error('classification_set', 'name', 'cannot be blank') if data['classification_set']['name'].blank?
7
+
8
+ if data['classification_set']['items'].blank? || !data['classification_set']['items'].kind_of?(Array)
9
+ add_error('classification_set', 'items', 'must be a non-empty array')
10
+ else
11
+ items(data['classification_set']['items'])
12
+ end
13
+ else
14
+ @errors['classification_set'] = ['must exist and not be blank']
15
+ end
16
+ return valid?
17
+ end
18
+
19
+ def items(elements, child = false)
20
+ parent, child = child ? ['child_classification', 'item'] : ['items', 'item']
21
+ if elements.kind_of?(Array) && !elements.empty?
22
+ elements.each do |element|
23
+ if element.blank?
24
+ add_error(parent, child, 'cannot be empty')
25
+ else
26
+ if element['label'].blank?
27
+ add_error(child, 'label', 'is required')
28
+ end
29
+ end
30
+ items(element['child_classifications'], true) if element.has_key?('child_classifications')
31
+ end
32
+ else
33
+ add_error(parent, child, 'must be a non-empty array')
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,150 @@
1
+ module Fulcrum
2
+ class FormValidator < BaseValidator
3
+
4
+ TYPES = %w(
5
+ TextField
6
+ ChoiceField
7
+ ClassificationField
8
+ PhotoField
9
+ DateTimeField
10
+ Section
11
+ )
12
+
13
+ def form_elements
14
+ data['form']['elements']
15
+ end
16
+
17
+ def form_name
18
+ data['form']['name']
19
+ end
20
+
21
+ def validate!
22
+ @items = {}
23
+ if data[:form]
24
+ if form_elements && !form_elements.empty?
25
+ add_error('form', 'name', 'cannot be blank') if data[:form][:name].blank?
26
+ fields(form_elements)
27
+ conditionals(form_elements)
28
+ else
29
+ add_error('form', 'elements', 'must be a non-empty array')
30
+ end
31
+ else
32
+ @errors['form'] = ['must exist and not be empty']
33
+ end
34
+ return valid?
35
+ end
36
+
37
+ def fields(elements)
38
+ if elements.kind_of?(Array) && !elements.empty?
39
+ elements.each { |element| field(element) }
40
+ else
41
+ add_error('form', 'elements', 'must be a non-empty array')
42
+ end
43
+ end
44
+
45
+ def field(element)
46
+ if element.blank?
47
+ add_error('elements', 'element', 'must not be empty')
48
+ else
49
+ if !element[:key]
50
+ add_error('element', 'key', 'must exist and not be nil')
51
+ return false
52
+ end
53
+
54
+ if @items.include?(element[:key])
55
+ add_error(element[:key], :key, 'must be unique')
56
+ return false
57
+ end
58
+
59
+ key = element[:key]
60
+ @items[key] = element[:type]
61
+
62
+ add_error(key, 'label', 'is required') if element[:label].blank?
63
+ add_error(key, 'data_name', 'is required') if element[:data_name].blank?
64
+ add_error(key, 'type', 'is not one of the valid types') unless TYPES.include?(element[:type])
65
+
66
+ %w(disabled hidden required).each do |attrib|
67
+ add_error(key, attrib, 'must be true or false') unless [true, false].include?(element[attrib])
68
+ end
69
+
70
+ case element[:type]
71
+
72
+ when 'ClassificationField'
73
+ if element[:classification_set_id]
74
+ add_error(key, 'classification_set_id', 'is required') if element[:classification_set_id].blank?
75
+ end
76
+
77
+ when 'Section'
78
+ if element['elements'].is_a?(Array)
79
+ if element['elements'].any?
80
+ fields(element['elements'])
81
+ else
82
+ add_error(key, 'elements', 'must contain additional elements')
83
+ end
84
+ else
85
+ add_error(key, 'elements', 'must be an array object')
86
+ end
87
+
88
+ when 'ChoiceField'
89
+ if element['choice_list_id']
90
+ add_error(key, 'choice_list_id', 'is required') if element[:choice_list_id].blank?
91
+ else
92
+ if element['choices'].is_a?(Array) && !element['choices'].blank?
93
+ element['choices'].each do |choice|
94
+ unless choice.has_key?('label') && choice['label'].present?
95
+ add_error('choices', 'label', 'contains an invalid label')
96
+ end
97
+ end
98
+ else
99
+ add_error(key, 'choices', 'must be a non-empty array')
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ def conditionals(elements)
107
+ elements.each { |element| conditional(element) }
108
+ end
109
+
110
+ def conditional(element)
111
+
112
+ operators = case element[:type]
113
+ when 'ChoiceField', 'ClassificationField'
114
+ %w(equal_to not_equal_to is_empty is_not_empty)
115
+ else
116
+ %w(equal_to not_equal_to contains starts_with greater_than less_than is_empty is_not_empty)
117
+ end
118
+
119
+ %w(required_conditions visible_conditions).each do |field|
120
+
121
+ if type = element["#{field}_type"]
122
+ add_error(key, "#{field}_type", 'is not valid') unless %(any all).include?(type)
123
+ end
124
+
125
+ if element[field]
126
+ if element[field].is_a?(Array)
127
+ element[field].each do |condition|
128
+
129
+ if key = condition[:field_key]
130
+ add_error(key, field, "key #{key} does not exist on the form") unless @items.keys.include?(key)
131
+ add_error(key, field, "operator for #{key} is invalid") unless operators.include?(condition[:operator])
132
+
133
+ if %w(is_empty is_not_empty).include?(condition[:operator]) && condition[:value].present?
134
+ add_error(key, field, 'value cannot be blank')
135
+ end
136
+ else
137
+ add_error(key, field, 'field key must exist for condition')
138
+ end
139
+
140
+ end
141
+ else
142
+ add_error(key, field, 'must be an array object')
143
+ end
144
+ end
145
+ end
146
+
147
+ conditionals(element[:elements]) if element[:type] == 'Section'
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,18 @@
1
+ module Fulcrum
2
+ class RecordValidator < BaseValidator
3
+ def validate!
4
+ if data['record'].kind_of?(Hash) && !data['record'].empty?
5
+ add_error('record', 'form_id', 'cannot be blank') if data['record']['form_id'].blank?
6
+ if data['record']['latitude'].to_f == 0.0 || data['record']['longitude'] == 0.0
7
+ add_error('record', 'coordinates', 'must be in WGS84 format')
8
+ end
9
+ if !data['record']['form_values'].kind_of?(Hash) || data['record']['form_values'].blank?
10
+ add_error('record', 'form_values', 'must be a non-empty hash')
11
+ end
12
+ else
13
+ @errors['record'] = ['must be a non-empty hash']
14
+ end
15
+ return valid?
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Fulcrum
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,175 @@
1
+ {
2
+ "form": {
3
+ "name": "Park Maintinence",
4
+ "description": "Form for inspection of park's state of maintenance.",
5
+ "created_at": "2012-04-27T10:48:31-04:00",
6
+ "updated_at": "2012-04-27T10:58:39-04:00",
7
+ "elements": [
8
+ {
9
+ "disabled":false,
10
+ "hidden":false,
11
+ "key":"b8fbca33-cad6-1eb5-b4ec-d78cf8aa9323",
12
+ "type":"Section",
13
+ "data_name":"park_report",
14
+ "required":false,
15
+ "label":"Park Report",
16
+ "elements":[
17
+ {
18
+ "disabled":false,
19
+ "hidden":false,
20
+ "multiple":false,
21
+ "allow_other":true,
22
+ "key":"42277b35-92a0-2fe5-15df-baf4a2c11d69",
23
+ "type":"ChoiceField",
24
+ "data_name":"amenity_type",
25
+ "required":false,
26
+ "label":"Amenity Type",
27
+ "choices":[
28
+ {
29
+ "value":"Boardwalk",
30
+ "label":"Boardwalk"
31
+ },
32
+ {
33
+ "value":"Pavilion",
34
+ "label":"Pavilion"
35
+ },
36
+ {
37
+ "value":"Bench",
38
+ "label":"Bench"
39
+ },
40
+ {
41
+ "value":"Path",
42
+ "label":"Path"
43
+ },
44
+ {
45
+ "value":"Playground Equipment",
46
+ "label":"Playground Equipment"
47
+ },
48
+ {
49
+ "value":"Land",
50
+ "label":"Land"
51
+ },
52
+ {
53
+ "value":"Restroom",
54
+ "label":"Restroom"
55
+ },
56
+ {
57
+ "value":"Athletic Field",
58
+ "label":"Athletic Field"
59
+ },
60
+ {
61
+ "value":"Boat Ramp",
62
+ "label":"Boat Ramp"
63
+ },
64
+ {
65
+ "value":"Picnic Table",
66
+ "label":"Picnic Table"
67
+ },
68
+ {
69
+ "value":"Dog Park",
70
+ "label":"Dog Park"
71
+ }
72
+ ]
73
+ },
74
+ {
75
+ "disabled":false,
76
+ "hidden":false,
77
+ "multiple":false,
78
+ "allow_other":true,
79
+ "key":"dec7d431-981e-55fe-ce27-fa0ce588b7b3",
80
+ "type":"ChoiceField",
81
+ "data_name":"type_of_problem",
82
+ "required":false,
83
+ "label":"Type of Problem",
84
+ "choices":[
85
+ {
86
+ "value":"Vandalism",
87
+ "label":"Vandalism"
88
+ },
89
+ {
90
+ "value":"Invasive Plant Species",
91
+ "label":"Invasive Plant Species"
92
+ },
93
+ {
94
+ "value":"Pests",
95
+ "label":"Pests"
96
+ },
97
+ {
98
+ "value":"Damage",
99
+ "label":"Damage"
100
+ },
101
+ {
102
+ "value":"None",
103
+ "label":"None"
104
+ }
105
+ ]
106
+ },
107
+ {
108
+ "disabled":false,
109
+ "hidden":false,
110
+ "key":"bbd86b27-4792-1ba3-6bf3-65b7bb5ee124",
111
+ "type":"TextField",
112
+ "data_name":"problem_notes",
113
+ "required":false,
114
+ "label":"Problem Notes"
115
+ },
116
+ {
117
+ "disabled":false,
118
+ "hidden":false,
119
+ "multiple":false,
120
+ "allow_other":true,
121
+ "key":"36d58746-2b5c-a545-3009-5744de326e69",
122
+ "type":"ChoiceField",
123
+ "data_name":"urgency_",
124
+ "required":false,
125
+ "label":"Urgency ",
126
+ "choices":[
127
+ {
128
+ "value":"High",
129
+ "label":"High"
130
+ },
131
+ {
132
+ "value":"Medium",
133
+ "label":"Medium"
134
+ },
135
+ {
136
+ "value":"Low",
137
+ "label":"Low"
138
+ },
139
+ {
140
+ "value":"None",
141
+ "label":"None"
142
+ }
143
+ ]
144
+ },
145
+ {
146
+ "disabled":false,
147
+ "hidden":false,
148
+ "key":"4194cc93-99ff-fb2c-ec1f-ad4e394e6731",
149
+ "type":"TextField",
150
+ "data_name":"name_of_park",
151
+ "required":false,
152
+ "label":"Name of Park"
153
+ },
154
+ {
155
+ "disabled":false,
156
+ "hidden":false,
157
+ "key":"21f0b6d5-4154-030e-d552-13819dfb3a0a",
158
+ "type":"PhotoField",
159
+ "required":false,
160
+ "label":"Photos"
161
+ }
162
+ ]
163
+ },
164
+ {
165
+ "disabled":false,
166
+ "hidden":false,
167
+ "key":"1797370a-06c3-a263-6c8f-17dcf7186b11",
168
+ "type":"DateTimeField",
169
+ "data_name":"date_of_report",
170
+ "required":false,
171
+ "label":"Date of Report"
172
+ }
173
+ ]
174
+ }
175
+ }