| 152 | } |
| 153 | |
| 154 | std::string RubyEngine::inferMeasureClassName(const openstudio::path& measureScriptPath) { |
| 155 | |
| 156 | auto inferClassNameCmd = fmt::format(R"ruby( |
| 157 | ObjectSpace.garbage_collect |
| 158 | ObjectSpace.garbage_collect |
| 159 | # Measure should be at root level (not inside a module) so we can just get constants |
| 160 | measurePath = '{}' |
| 161 | prev = Object.constants |
| 162 | # puts "prev = #{{prev}}" |
| 163 | load measurePath # need load in case have seen this script before |
| 164 | just_defined = Object.constants - prev |
| 165 | # puts "just_defined = #{{just_defined}}" |
| 166 | just_defined.select!{{|c| Object.const_get(c).ancestors.include?(OpenStudio::Measure::OSMeasure)}} |
| 167 | # puts "just_defined, filtered = #{{just_defined}}" |
| 168 | |
| 169 | if just_defined.empty? |
| 170 | raise "Unable to extract OpenStudio::Measure::OSMeasure object from " + |
| 171 | measurePath + ". The script should contain a class that derives " + |
| 172 | "from OpenStudio::Measure::OSMeasure and should close with a line stating " + |
| 173 | "the class name followed by .new.registerWithApplication." |
| 174 | end |
| 175 | if just_defined.size > 1 |
| 176 | raise "Found more than one OSMeasure at #{{measurePath}}: #{{just_defined}}" |
| 177 | end |
| 178 | c = just_defined[0] |
| 179 | class_info = Object.const_get(c) |
| 180 | $measure_name = class_info.to_s |
| 181 | |
| 182 | # Undef what we loaded |
| 183 | just_defined.each {{|x| Object.send(:remove_const, x) }} |
| 184 | ObjectSpace.garbage_collect |
| 185 | ObjectSpace.garbage_collect |
| 186 | )ruby", |
| 187 | measureScriptPath.generic_string()); |
| 188 | |
| 189 | std::string className; |
| 190 | |
| 191 | try { |
| 192 | exec(inferClassNameCmd); |
| 193 | ScriptObject measureClassNameObject = eval("$measure_name"); |
| 194 | // measureClassNameObject = rubyEngine->eval(fmt::format("{}.new()", className)); |
| 195 | // ScriptObject measureClassNameObject = rubyEngine->eval(inferClassName); |
| 196 | className = getAs<std::string>(measureClassNameObject); |
| 197 | } catch (const RubyException& e) { |
| 198 | auto msg = fmt::format("Failed to infer measure name from {}: {}\nlocation={}", measureScriptPath.generic_string(), e.what(), e.location()); |
| 199 | fmt::print(stderr, "{}\n", msg); |
| 200 | } |
| 201 | |
| 202 | return className; |
| 203 | } |
| 204 | |
| 205 | // Ideally this would return a openstudio::measure::OSMeasure* or a shared_ptr<openstudio::measure::OSMeasure> but this poses memory management |
| 206 | // issue for the underlying ScriptObject (and VALUE or PyObject), so just return the ScriptObject |
no test coverage detected